@harness-engineering/cli 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/skills/claude-code/harness-execution/SKILL.md +35 -0
- package/dist/agents/skills/claude-code/harness-roadmap-pilot/SKILL.md +33 -44
- package/dist/agents/skills/codex/harness-execution/SKILL.md +35 -0
- package/dist/agents/skills/codex/harness-roadmap-pilot/SKILL.md +33 -44
- package/dist/agents/skills/cursor/harness-execution/SKILL.md +35 -0
- package/dist/agents/skills/cursor/harness-roadmap-pilot/SKILL.md +33 -44
- package/dist/agents/skills/gemini-cli/harness-execution/SKILL.md +35 -0
- package/dist/agents/skills/gemini-cli/harness-roadmap-pilot/SKILL.md +33 -44
- package/dist/{agents-md-XOJE5ETA.js → agents-md-YYCLGMBF.js} +2 -2
- package/dist/{architecture-JEZPJ725.js → architecture-5KPP4ID4.js} +3 -3
- package/dist/{assess-project-RHXOQNMM.js → assess-project-B6ENHUAU.js} +1 -1
- package/dist/bin/harness-mcp.js +13 -13
- package/dist/bin/harness.js +22 -22
- package/dist/{check-phase-gate-GNDOMS35.js → check-phase-gate-REG3HWYC.js} +3 -3
- package/dist/{chunk-N24HCQA4.js → chunk-A65QO5KN.js} +132 -46
- package/dist/{chunk-UX3PQKVZ.js → chunk-BXKNWSG4.js} +1 -1
- package/dist/{chunk-XX4OLNWQ.js → chunk-C2ZGWMTN.js} +2 -2
- package/dist/{chunk-HEIMJJI4.js → chunk-DNX35Q5K.js} +2 -2
- package/dist/{chunk-BYVT5LVO.js → chunk-ERZCC5VH.js} +3 -3
- package/dist/{chunk-XFU6SB2Q.js → chunk-FL5SLUKZ.js} +62 -27
- package/dist/{chunk-NWLGL3IR.js → chunk-FUH4J43G.js} +1 -1
- package/dist/{chunk-MGUPSE6D.js → chunk-HIPBU2Q4.js} +5 -5
- package/dist/{chunk-3ISHDWO7.js → chunk-IF3JK3G5.js} +1 -1
- package/dist/{chunk-KAJOS3BA.js → chunk-LMZ2JTNP.js} +2 -2
- package/dist/{chunk-527G27VI.js → chunk-OQFEFVIN.js} +2 -2
- package/dist/{chunk-Z4AWEIBY.js → chunk-PZX2Y3RV.js} +3 -3
- package/dist/{chunk-ONCPJGHY.js → chunk-RGQXHCCH.js} +6 -6
- package/dist/{chunk-QBC7DI4Q.js → chunk-TCVQVHEF.js} +1 -1
- package/dist/{chunk-PC2R5RUJ.js → chunk-TE5YHHHZ.js} +1 -1
- package/dist/{chunk-HWTUUQOW.js → chunk-UBTMPO5H.js} +3 -3
- package/dist/{chunk-GSP2XIVS.js → chunk-XEOSSKIB.js} +1 -1
- package/dist/{chunk-DE5U6KOL.js → chunk-XZ2Q4UTW.js} +1 -1
- package/dist/{chunk-YQSYCBAH.js → chunk-YF3AY6KS.js} +6 -6
- package/dist/{chunk-GGKRA7A7.js → chunk-YQAFKOOL.js} +1 -1
- package/dist/{chunk-WWEVS7RO.js → chunk-ZOUJP3EY.js} +106 -80
- package/dist/{chunk-6WQCLCBO.js → chunk-ZRKFDE6M.js} +1 -1
- package/dist/{ci-workflow-ORZ4F45F.js → ci-workflow-JEYYCBOC.js} +2 -2
- package/dist/{dist-LHINSVK4.js → dist-3HW4LCHE.js} +15 -1
- package/dist/{docs-AU3DXFFO.js → docs-YRMW7UED.js} +3 -3
- package/dist/{engine-6GPWOYQH.js → engine-E7H5U3AC.js} +2 -2
- package/dist/{entropy-VLKDRMRT.js → entropy-3JBKCGJJ.js} +2 -2
- package/dist/{feedback-YXYY44ZC.js → feedback-U6VJ6AUJ.js} +1 -1
- package/dist/{generate-agent-definitions-XIHQPKG3.js → generate-agent-definitions-S2FRZF4Y.js} +3 -3
- package/dist/hooks/format-check.js +202 -0
- package/dist/hooks/quality-warner.js +32 -0
- package/dist/hooks/strict-quality-gate.js +48 -0
- package/dist/index.d.ts +8 -8
- package/dist/index.js +22 -22
- package/dist/{loader-YAN56MT3.js → loader-RGQJYERV.js} +2 -2
- package/dist/{mcp-RK3SFVYB.js → mcp-NDTFZXWF.js} +13 -13
- package/dist/{performance-64DPTLLQ.js → performance-FPWKC4CN.js} +3 -3
- package/dist/{review-pipeline-NENG6LW7.js → review-pipeline-WUHG7PHO.js} +2 -2
- package/dist/{runtime-FFIIRT2T.js → runtime-Q3ORD7LW.js} +2 -2
- package/dist/{security-3PIBN35B.js → security-JDHTRBGC.js} +1 -1
- package/dist/{validate-7IAF2ES3.js → validate-G3LKBOYA.js} +3 -3
- package/dist/{validate-cross-check-QPSAOZKD.js → validate-cross-check-4ITA72DF.js} +2 -2
- package/package.json +8 -8
- package/dist/hooks/quality-gate.js +0 -131
|
@@ -98,6 +98,39 @@ Do not improvise past unknowns. An assumption that turns out wrong is cheaper th
|
|
|
98
98
|
|
|
99
99
|
Report progress with: `**[Phase N/M]** Task N — <description>`
|
|
100
100
|
|
|
101
|
+
#### Step 0: Claim the roadmap item (execution-start claim)
|
|
102
|
+
|
|
103
|
+
Execution start is the one true "who is working on this now" moment — so this is
|
|
104
|
+
where the `assignee` is written, **not** at selection (roadmap-pilot no longer
|
|
105
|
+
assigns). The invariant is `assignee ≠ null ⟺ status == in-progress`, enforced by
|
|
106
|
+
RMH005 (`harness validate`).
|
|
107
|
+
|
|
108
|
+
1. **Resolve `currentUser`** the same way roadmap-pilot does: the `--user` argument
|
|
109
|
+
if provided, else `git config user.name` (fall back to `git config user.email`).
|
|
110
|
+
2. **If `docs/roadmap.md` exists and a row matches this plan** (match by the row's
|
|
111
|
+
`Spec`/`Plan` path or the feature name from the handoff/spec):
|
|
112
|
+
- **If `currentUser` cannot be resolved, STOP** with an actionable message —
|
|
113
|
+
"Set your identity via `git config user.name`/`user.email` (or pass `--user`)
|
|
114
|
+
before starting execution" — rather than claiming with a null owner (S4-002).
|
|
115
|
+
Leave the row `planned` and unassigned.
|
|
116
|
+
- Otherwise claim it:
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
manage_roadmap({
|
|
120
|
+
path: "<project-root>",
|
|
121
|
+
action: "update",
|
|
122
|
+
feature: "<feature-name>",
|
|
123
|
+
status: "in-progress",
|
|
124
|
+
assignee: "<currentUser>"
|
|
125
|
+
})
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
This routes through the assignee-lifecycle `claim()` chokepoint
|
|
129
|
+
(first-claim-wins: if the row is already `in-progress` under a different
|
|
130
|
+
owner, the claim is a no-op and you should treat the item as taken).
|
|
131
|
+
|
|
132
|
+
- If no roadmap row matches this plan (e.g. ad-hoc execution), skip the claim.
|
|
133
|
+
|
|
101
134
|
For each task, starting from current position:
|
|
102
135
|
|
|
103
136
|
1. **Read task instructions completely** before writing any code.
|
|
@@ -344,11 +377,13 @@ Claims about task completion, test results, or code behavior MUST cite evidence:
|
|
|
344
377
|
- **`harness state show`** — View current position and progress.
|
|
345
378
|
- **`harness state learn "<message>"`** — Append a learning from CLI.
|
|
346
379
|
- **State/Learnings files** — Session-scoped when session known, otherwise `.harness/`. State updated after every task; learnings append-only.
|
|
380
|
+
- **Roadmap claim** — Phase 2 Step 0: `manage_roadmap update` with `status: in-progress` + `assignee: <currentUser>` claims the item at execution start (the assignee = "who is executing" invariant). Stop if `currentUser` is unresolvable. Marking the row `done` later auto-clears the assignee via the lifecycle `setStatus` chokepoint — never leave a non-in-progress row assigned (RMH005).
|
|
347
381
|
- **Roadmap sync** — After plan completion, `manage_roadmap sync` with `apply: true`. Mandatory when roadmap exists. No `force_sync: true`.
|
|
348
382
|
- **`emit_interaction`** — Auto-transition to harness-verification at plan completion.
|
|
349
383
|
|
|
350
384
|
## Success Criteria
|
|
351
385
|
|
|
386
|
+
- The roadmap item is claimed at execution start (`status=in-progress` + `assignee=<currentUser>`) when a matching row exists; execution stops with an actionable message if `currentUser` is unresolvable
|
|
352
387
|
- Every task executed in order, atomically, one commit per task
|
|
353
388
|
- `.harness/state.json` accurately reflects position and progress
|
|
354
389
|
- `.harness/learnings.md` has entries for sessions with non-trivial discoveries
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Harness Roadmap Pilot
|
|
2
2
|
|
|
3
|
-
> AI-assisted selection of the next highest-impact unblocked roadmap item. Scores candidates, recommends one,
|
|
3
|
+
> AI-assisted selection of the next highest-impact unblocked roadmap item. Scores candidates, recommends one, and transitions to the appropriate next skill. Selection does NOT assign — the `assignee` field names who is _executing_, and is written at execution start (harness-execution), not at selection.
|
|
4
4
|
|
|
5
5
|
## When to Use
|
|
6
6
|
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
|
|
15
15
|
### Iron Law
|
|
16
16
|
|
|
17
|
-
**Never
|
|
17
|
+
**Never transition without the human confirming the recommendation first. Selection never writes `assignee`.**
|
|
18
18
|
|
|
19
|
-
Present the ranked candidates, the AI reasoning, and the recommended pick. Wait for explicit confirmation before
|
|
19
|
+
Present the ranked candidates, the AI reasoning, and the recommended pick. Wait for explicit confirmation before transitioning. The `assignee` field means _who is executing_ and is claimed at execution start by harness-execution — picking an item must NOT mark it assigned (an early assignee makes the item look human-claimed and the orchestrator silently skips it).
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
@@ -133,28 +133,19 @@ Ask the human in plain text (matching the `y/n/pick another` example above). Do
|
|
|
133
133
|
- If **pick another**: ask which candidate number, then proceed with that pick.
|
|
134
134
|
- If **no**: stop. No changes made.
|
|
135
135
|
|
|
136
|
-
### Phase 4:
|
|
136
|
+
### Phase 4: TRANSITION -- Hand Off to the Next Skill
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
**Do NOT write the `assignee` field here.** Selection picks the item; it does not
|
|
139
|
+
claim it. harness-execution claims the item (`status=in-progress` + `assignee`) at
|
|
140
|
+
execution start. Writing an assignee at selection makes the orchestrator treat the
|
|
141
|
+
item as already-claimed and silently skip it (the bug this skill must not reintroduce).
|
|
142
|
+
The item stays `planned`/`backlog` with `Assignee: —` and remains orchestrator-eligible.
|
|
139
143
|
|
|
140
|
-
|
|
141
|
-
manage_roadmap({
|
|
142
|
-
path: "<project-root>",
|
|
143
|
-
action: "update",
|
|
144
|
-
feature: "<feature-name>",
|
|
145
|
-
assignee: "<currentUser>"
|
|
146
|
-
})
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
- This updates the feature's `Assignee` field with assignment history tracking
|
|
150
|
-
- Automatically triggers external sync (GitHub Issues) if tracker config exists in `harness.config.json`
|
|
151
|
-
- External sync is fire-and-forget — errors are logged but do not block the assignment
|
|
152
|
-
|
|
153
|
-
2. Determine the transition target:
|
|
144
|
+
1. Determine the transition target:
|
|
154
145
|
- If the feature has a `spec` field (non-null): transition to `harness:autopilot`
|
|
155
146
|
- If the feature has no `spec`: transition to `harness:brainstorming`
|
|
156
147
|
|
|
157
|
-
|
|
148
|
+
2. Present the transition to the human via `emit_interaction`:
|
|
158
149
|
|
|
159
150
|
```json
|
|
160
151
|
emit_interaction({
|
|
@@ -163,16 +154,16 @@ Ask the human in plain text (matching the `y/n/pick another` example above). Do
|
|
|
163
154
|
transition: {
|
|
164
155
|
completedPhase: "roadmap-pilot",
|
|
165
156
|
suggestedNext: "<brainstorming|autopilot>",
|
|
166
|
-
reason: "Feature '<name>'
|
|
157
|
+
reason: "Feature '<name>' selected and ready for <brainstorming|execution>",
|
|
167
158
|
artifacts: ["docs/roadmap.md"],
|
|
168
159
|
requiresConfirmation: true,
|
|
169
|
-
summary: "
|
|
160
|
+
summary: "Selected '<name>'. <Spec exists -- ready for autopilot|No spec -- needs brainstorming first>. (Not assigned — harness-execution claims it at execution start.)",
|
|
170
161
|
qualityGate: {
|
|
171
162
|
checks: [
|
|
172
163
|
{ "name": "roadmap-parsed", "passed": true },
|
|
173
164
|
{ "name": "candidate-scored", "passed": true },
|
|
174
165
|
{ "name": "human-confirmed", "passed": true },
|
|
175
|
-
{ "name": "assignment-
|
|
166
|
+
{ "name": "no-assignment-at-selection", "passed": true }
|
|
176
167
|
],
|
|
177
168
|
allPassed: true
|
|
178
169
|
}
|
|
@@ -180,7 +171,7 @@ Ask the human in plain text (matching the `y/n/pick another` example above). Do
|
|
|
180
171
|
})
|
|
181
172
|
```
|
|
182
173
|
|
|
183
|
-
|
|
174
|
+
3. Run `harness validate`.
|
|
184
175
|
|
|
185
176
|
---
|
|
186
177
|
|
|
@@ -190,19 +181,18 @@ Ask the human in plain text (matching the `y/n/pick another` example above). Do
|
|
|
190
181
|
- **`loadProjectRoadmapMode` / `loadTrackerClientConfigFromProject` / `createTrackerClient`** -- Resolve `roadmap.mode` and obtain a `RoadmapTrackerClient` for file-less mode. Import from `@harness-engineering/core`.
|
|
191
182
|
- **`scoreRoadmapCandidatesForMode`** -- Mode-aware scoring entry point. Import from `@harness-engineering/core`. In file-backed mode delegates to `scoreRoadmapCandidates`; in file-less mode routes through `scoreRoadmapCandidatesFileLess` (priority + createdAt sort, FR-S3).
|
|
192
183
|
- **`scoreRoadmapCandidates`** -- Underlying file-backed scoring algorithm. Prefer `scoreRoadmapCandidatesForMode` from the skill; direct callers in file-backed-only code paths can still use this.
|
|
193
|
-
- **`
|
|
194
|
-
- **`emit_interaction`** -- Used for the skill transition at the end. Transitions to `harness:brainstorming` (no spec) or `harness:autopilot` (spec exists).
|
|
184
|
+
- **`emit_interaction`** -- Used for the skill transition at the end. Transitions to `harness:brainstorming` (no spec) or `harness:autopilot` (spec exists). This skill does NOT call `manage_roadmap update` to assign — the `assignee` field is owned by harness-execution (claim at execution start), enforced by RMH005 (`assignee ≠ null ⟺ in-progress`).
|
|
195
185
|
- **`STRATEGY.md` alignment (Phase 2 step 1a)** -- When present at repo root and valid, loaded via `validateStrategy` + `parseStrategyDoc` + `asStrategyDoc` from `@harness-engineering/core`. Applied as a bounded tiebreaker bonus (max `+0.75`) only when candidates score within `0.05` on the base formula. Boundary: roadmap-pilot READS; `harness-strategy` WRITES. Never modify `STRATEGY.md` from this skill.
|
|
196
|
-
- **`harness validate`** -- Run after
|
|
186
|
+
- **`harness validate`** -- Run after the transition is presented.
|
|
197
187
|
|
|
198
188
|
## Success Criteria
|
|
199
189
|
|
|
200
190
|
1. Roadmap is parsed and unblocked planned/backlog items are scored
|
|
201
191
|
2. Scoring uses two-tier sort: explicit priority first, then weighted score
|
|
202
192
|
3. AI reads top candidates' specs and provides recommendation with reasoning
|
|
203
|
-
4. Human confirms before
|
|
204
|
-
5.
|
|
205
|
-
6.
|
|
193
|
+
4. Human confirms before the transition is made
|
|
194
|
+
5. Selection does NOT write the `assignee` field (no assignment, no history record, no external assignee sync) — the pick stays `planned`/`backlog` and orchestrator-eligible
|
|
195
|
+
6. The `assignee` field is owned by harness-execution, which claims at execution start; RMH005 enforces `assignee ≠ null ⟺ in-progress`
|
|
206
196
|
7. Transition routes to brainstorming (no spec) or autopilot (spec exists)
|
|
207
197
|
8. When a pulse report exists, the recommendation rationale cites pulse signal for any top-3 candidate whose area is referenced in the pulse Headlines or Followups.
|
|
208
198
|
9. When `STRATEGY.md` is present and valid AND two candidates score within `0.05` on the base formula, the recommendation rationale cites strategy-alignment as the tiebreaker. The bonus never overrides a meaningful base-score difference.
|
|
@@ -211,12 +201,13 @@ Ask the human in plain text (matching the `y/n/pick another` example above). Do
|
|
|
211
201
|
|
|
212
202
|
## Rationalizations to Reject
|
|
213
203
|
|
|
214
|
-
| Rationalization | Reality
|
|
215
|
-
| ----------------------------------------------------------------------------------------------------------------------- |
|
|
216
|
-
| "The top-scored candidate is obviously correct, so I can
|
|
217
|
-
| "
|
|
218
|
-
| "
|
|
219
|
-
| "
|
|
204
|
+
| Rationalization | Reality |
|
|
205
|
+
| ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
206
|
+
| "The top-scored candidate is obviously correct, so I can transition without asking the human" | The Iron Law: never transition without the human confirming the recommendation first. |
|
|
207
|
+
| "I'll write the assignee now so the pick is recorded / the user is credited" | Selection never writes `assignee`. An assignee means _in-progress_ (set by harness-execution at execution start). Writing it at selection makes the orchestrator silently skip the item — the exact bug this skill must not reintroduce. |
|
|
208
|
+
| "Affinity data is not available so the scoring is degraded -- I should just pick the first planned item" | Proceed without affinity scoring by zeroing out the affinity weight. Position and dependents signals still produce meaningful rankings. |
|
|
209
|
+
| "The feature has no spec, but I can skip brainstorming and jump straight to planning since the summary is clear enough" | No spec routes to brainstorming, spec exists routes to autopilot. A one-line roadmap summary is not a spec. |
|
|
210
|
+
| "STRATEGY.md exists, so I should let it override the top-scored candidate when alignment is clear" | The alignment bonus is bounded (max `+0.75`) and only fires when base scores are within `0.05`. It is a tiebreaker, not a hard filter — a clearly higher-scored item still wins. |
|
|
220
211
|
|
|
221
212
|
## Examples
|
|
222
213
|
|
|
@@ -252,25 +243,23 @@ Proceed? (y/n/pick another)
|
|
|
252
243
|
|
|
253
244
|
Human confirms **y**.
|
|
254
245
|
|
|
255
|
-
**Phase 4:
|
|
246
|
+
**Phase 4: TRANSITION**
|
|
256
247
|
|
|
257
248
|
```
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
Roadmap updated: docs/roadmap.md
|
|
261
|
-
External sync: github:harness-eng/harness#43 assigned (automatic)
|
|
249
|
+
Graph Connector stays planned, Assignee: — (not assigned at selection).
|
|
250
|
+
harness-execution will claim it (status=in-progress + assignee) at execution start.
|
|
262
251
|
|
|
263
252
|
Transitioning to harness:autopilot (spec exists)...
|
|
264
253
|
```
|
|
265
254
|
|
|
266
255
|
## Gates
|
|
267
256
|
|
|
268
|
-
- **No
|
|
269
|
-
- **No
|
|
257
|
+
- **No transition without human confirmation.** The CONFIRM phase must complete with explicit approval before transitioning.
|
|
258
|
+
- **No assignment at selection.** This skill never writes the `assignee` field — that field means _who is executing_ and is claimed at execution start by harness-execution. Writing it here would make the orchestrator silently skip the item.
|
|
270
259
|
- **No scoring without a parsed roadmap.** If `docs/roadmap.md` does not exist or fails to parse, stop with an error.
|
|
271
260
|
|
|
272
261
|
## Escalation
|
|
273
262
|
|
|
274
263
|
- **When no unblocked candidates exist:** Inform the human. Suggest reviewing blocked items to see if blockers can be resolved, or adding new features via `harness-roadmap --add`.
|
|
275
264
|
- **When affinity data is unavailable:** Proceed without affinity scoring (weight falls to 0 for all candidates). Note this in the output.
|
|
276
|
-
- **When
|
|
265
|
+
- **When the human asks you to assign the item to them at selection:** Explain that the `assignee` field means _who is executing_ and is claimed at execution start by harness-execution; assigning at selection makes the orchestrator silently skip the item (enforced by RMH005). Proceed with the transition instead.
|
|
@@ -98,6 +98,39 @@ Do not improvise past unknowns. An assumption that turns out wrong is cheaper th
|
|
|
98
98
|
|
|
99
99
|
Report progress with: `**[Phase N/M]** Task N — <description>`
|
|
100
100
|
|
|
101
|
+
#### Step 0: Claim the roadmap item (execution-start claim)
|
|
102
|
+
|
|
103
|
+
Execution start is the one true "who is working on this now" moment — so this is
|
|
104
|
+
where the `assignee` is written, **not** at selection (roadmap-pilot no longer
|
|
105
|
+
assigns). The invariant is `assignee ≠ null ⟺ status == in-progress`, enforced by
|
|
106
|
+
RMH005 (`harness validate`).
|
|
107
|
+
|
|
108
|
+
1. **Resolve `currentUser`** the same way roadmap-pilot does: the `--user` argument
|
|
109
|
+
if provided, else `git config user.name` (fall back to `git config user.email`).
|
|
110
|
+
2. **If `docs/roadmap.md` exists and a row matches this plan** (match by the row's
|
|
111
|
+
`Spec`/`Plan` path or the feature name from the handoff/spec):
|
|
112
|
+
- **If `currentUser` cannot be resolved, STOP** with an actionable message —
|
|
113
|
+
"Set your identity via `git config user.name`/`user.email` (or pass `--user`)
|
|
114
|
+
before starting execution" — rather than claiming with a null owner (S4-002).
|
|
115
|
+
Leave the row `planned` and unassigned.
|
|
116
|
+
- Otherwise claim it:
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
manage_roadmap({
|
|
120
|
+
path: "<project-root>",
|
|
121
|
+
action: "update",
|
|
122
|
+
feature: "<feature-name>",
|
|
123
|
+
status: "in-progress",
|
|
124
|
+
assignee: "<currentUser>"
|
|
125
|
+
})
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
This routes through the assignee-lifecycle `claim()` chokepoint
|
|
129
|
+
(first-claim-wins: if the row is already `in-progress` under a different
|
|
130
|
+
owner, the claim is a no-op and you should treat the item as taken).
|
|
131
|
+
|
|
132
|
+
- If no roadmap row matches this plan (e.g. ad-hoc execution), skip the claim.
|
|
133
|
+
|
|
101
134
|
For each task, starting from current position:
|
|
102
135
|
|
|
103
136
|
1. **Read task instructions completely** before writing any code.
|
|
@@ -344,11 +377,13 @@ Claims about task completion, test results, or code behavior MUST cite evidence:
|
|
|
344
377
|
- **`harness state show`** — View current position and progress.
|
|
345
378
|
- **`harness state learn "<message>"`** — Append a learning from CLI.
|
|
346
379
|
- **State/Learnings files** — Session-scoped when session known, otherwise `.harness/`. State updated after every task; learnings append-only.
|
|
380
|
+
- **Roadmap claim** — Phase 2 Step 0: `manage_roadmap update` with `status: in-progress` + `assignee: <currentUser>` claims the item at execution start (the assignee = "who is executing" invariant). Stop if `currentUser` is unresolvable. Marking the row `done` later auto-clears the assignee via the lifecycle `setStatus` chokepoint — never leave a non-in-progress row assigned (RMH005).
|
|
347
381
|
- **Roadmap sync** — After plan completion, `manage_roadmap sync` with `apply: true`. Mandatory when roadmap exists. No `force_sync: true`.
|
|
348
382
|
- **`emit_interaction`** — Auto-transition to harness-verification at plan completion.
|
|
349
383
|
|
|
350
384
|
## Success Criteria
|
|
351
385
|
|
|
386
|
+
- The roadmap item is claimed at execution start (`status=in-progress` + `assignee=<currentUser>`) when a matching row exists; execution stops with an actionable message if `currentUser` is unresolvable
|
|
352
387
|
- Every task executed in order, atomically, one commit per task
|
|
353
388
|
- `.harness/state.json` accurately reflects position and progress
|
|
354
389
|
- `.harness/learnings.md` has entries for sessions with non-trivial discoveries
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Harness Roadmap Pilot
|
|
2
2
|
|
|
3
|
-
> AI-assisted selection of the next highest-impact unblocked roadmap item. Scores candidates, recommends one,
|
|
3
|
+
> AI-assisted selection of the next highest-impact unblocked roadmap item. Scores candidates, recommends one, and transitions to the appropriate next skill. Selection does NOT assign — the `assignee` field names who is _executing_, and is written at execution start (harness-execution), not at selection.
|
|
4
4
|
|
|
5
5
|
## When to Use
|
|
6
6
|
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
|
|
15
15
|
### Iron Law
|
|
16
16
|
|
|
17
|
-
**Never
|
|
17
|
+
**Never transition without the human confirming the recommendation first. Selection never writes `assignee`.**
|
|
18
18
|
|
|
19
|
-
Present the ranked candidates, the AI reasoning, and the recommended pick. Wait for explicit confirmation before
|
|
19
|
+
Present the ranked candidates, the AI reasoning, and the recommended pick. Wait for explicit confirmation before transitioning. The `assignee` field means _who is executing_ and is claimed at execution start by harness-execution — picking an item must NOT mark it assigned (an early assignee makes the item look human-claimed and the orchestrator silently skips it).
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
@@ -133,28 +133,19 @@ Ask the human in plain text (matching the `y/n/pick another` example above). Do
|
|
|
133
133
|
- If **pick another**: ask which candidate number, then proceed with that pick.
|
|
134
134
|
- If **no**: stop. No changes made.
|
|
135
135
|
|
|
136
|
-
### Phase 4:
|
|
136
|
+
### Phase 4: TRANSITION -- Hand Off to the Next Skill
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
**Do NOT write the `assignee` field here.** Selection picks the item; it does not
|
|
139
|
+
claim it. harness-execution claims the item (`status=in-progress` + `assignee`) at
|
|
140
|
+
execution start. Writing an assignee at selection makes the orchestrator treat the
|
|
141
|
+
item as already-claimed and silently skip it (the bug this skill must not reintroduce).
|
|
142
|
+
The item stays `planned`/`backlog` with `Assignee: —` and remains orchestrator-eligible.
|
|
139
143
|
|
|
140
|
-
|
|
141
|
-
manage_roadmap({
|
|
142
|
-
path: "<project-root>",
|
|
143
|
-
action: "update",
|
|
144
|
-
feature: "<feature-name>",
|
|
145
|
-
assignee: "<currentUser>"
|
|
146
|
-
})
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
- This updates the feature's `Assignee` field with assignment history tracking
|
|
150
|
-
- Automatically triggers external sync (GitHub Issues) if tracker config exists in `harness.config.json`
|
|
151
|
-
- External sync is fire-and-forget — errors are logged but do not block the assignment
|
|
152
|
-
|
|
153
|
-
2. Determine the transition target:
|
|
144
|
+
1. Determine the transition target:
|
|
154
145
|
- If the feature has a `spec` field (non-null): transition to `harness:autopilot`
|
|
155
146
|
- If the feature has no `spec`: transition to `harness:brainstorming`
|
|
156
147
|
|
|
157
|
-
|
|
148
|
+
2. Present the transition to the human via `emit_interaction`:
|
|
158
149
|
|
|
159
150
|
```json
|
|
160
151
|
emit_interaction({
|
|
@@ -163,16 +154,16 @@ Ask the human in plain text (matching the `y/n/pick another` example above). Do
|
|
|
163
154
|
transition: {
|
|
164
155
|
completedPhase: "roadmap-pilot",
|
|
165
156
|
suggestedNext: "<brainstorming|autopilot>",
|
|
166
|
-
reason: "Feature '<name>'
|
|
157
|
+
reason: "Feature '<name>' selected and ready for <brainstorming|execution>",
|
|
167
158
|
artifacts: ["docs/roadmap.md"],
|
|
168
159
|
requiresConfirmation: true,
|
|
169
|
-
summary: "
|
|
160
|
+
summary: "Selected '<name>'. <Spec exists -- ready for autopilot|No spec -- needs brainstorming first>. (Not assigned — harness-execution claims it at execution start.)",
|
|
170
161
|
qualityGate: {
|
|
171
162
|
checks: [
|
|
172
163
|
{ "name": "roadmap-parsed", "passed": true },
|
|
173
164
|
{ "name": "candidate-scored", "passed": true },
|
|
174
165
|
{ "name": "human-confirmed", "passed": true },
|
|
175
|
-
{ "name": "assignment-
|
|
166
|
+
{ "name": "no-assignment-at-selection", "passed": true }
|
|
176
167
|
],
|
|
177
168
|
allPassed: true
|
|
178
169
|
}
|
|
@@ -180,7 +171,7 @@ Ask the human in plain text (matching the `y/n/pick another` example above). Do
|
|
|
180
171
|
})
|
|
181
172
|
```
|
|
182
173
|
|
|
183
|
-
|
|
174
|
+
3. Run `harness validate`.
|
|
184
175
|
|
|
185
176
|
---
|
|
186
177
|
|
|
@@ -190,19 +181,18 @@ Ask the human in plain text (matching the `y/n/pick another` example above). Do
|
|
|
190
181
|
- **`loadProjectRoadmapMode` / `loadTrackerClientConfigFromProject` / `createTrackerClient`** -- Resolve `roadmap.mode` and obtain a `RoadmapTrackerClient` for file-less mode. Import from `@harness-engineering/core`.
|
|
191
182
|
- **`scoreRoadmapCandidatesForMode`** -- Mode-aware scoring entry point. Import from `@harness-engineering/core`. In file-backed mode delegates to `scoreRoadmapCandidates`; in file-less mode routes through `scoreRoadmapCandidatesFileLess` (priority + createdAt sort, FR-S3).
|
|
192
183
|
- **`scoreRoadmapCandidates`** -- Underlying file-backed scoring algorithm. Prefer `scoreRoadmapCandidatesForMode` from the skill; direct callers in file-backed-only code paths can still use this.
|
|
193
|
-
- **`
|
|
194
|
-
- **`emit_interaction`** -- Used for the skill transition at the end. Transitions to `harness:brainstorming` (no spec) or `harness:autopilot` (spec exists).
|
|
184
|
+
- **`emit_interaction`** -- Used for the skill transition at the end. Transitions to `harness:brainstorming` (no spec) or `harness:autopilot` (spec exists). This skill does NOT call `manage_roadmap update` to assign — the `assignee` field is owned by harness-execution (claim at execution start), enforced by RMH005 (`assignee ≠ null ⟺ in-progress`).
|
|
195
185
|
- **`STRATEGY.md` alignment (Phase 2 step 1a)** -- When present at repo root and valid, loaded via `validateStrategy` + `parseStrategyDoc` + `asStrategyDoc` from `@harness-engineering/core`. Applied as a bounded tiebreaker bonus (max `+0.75`) only when candidates score within `0.05` on the base formula. Boundary: roadmap-pilot READS; `harness-strategy` WRITES. Never modify `STRATEGY.md` from this skill.
|
|
196
|
-
- **`harness validate`** -- Run after
|
|
186
|
+
- **`harness validate`** -- Run after the transition is presented.
|
|
197
187
|
|
|
198
188
|
## Success Criteria
|
|
199
189
|
|
|
200
190
|
1. Roadmap is parsed and unblocked planned/backlog items are scored
|
|
201
191
|
2. Scoring uses two-tier sort: explicit priority first, then weighted score
|
|
202
192
|
3. AI reads top candidates' specs and provides recommendation with reasoning
|
|
203
|
-
4. Human confirms before
|
|
204
|
-
5.
|
|
205
|
-
6.
|
|
193
|
+
4. Human confirms before the transition is made
|
|
194
|
+
5. Selection does NOT write the `assignee` field (no assignment, no history record, no external assignee sync) — the pick stays `planned`/`backlog` and orchestrator-eligible
|
|
195
|
+
6. The `assignee` field is owned by harness-execution, which claims at execution start; RMH005 enforces `assignee ≠ null ⟺ in-progress`
|
|
206
196
|
7. Transition routes to brainstorming (no spec) or autopilot (spec exists)
|
|
207
197
|
8. When a pulse report exists, the recommendation rationale cites pulse signal for any top-3 candidate whose area is referenced in the pulse Headlines or Followups.
|
|
208
198
|
9. When `STRATEGY.md` is present and valid AND two candidates score within `0.05` on the base formula, the recommendation rationale cites strategy-alignment as the tiebreaker. The bonus never overrides a meaningful base-score difference.
|
|
@@ -211,12 +201,13 @@ Ask the human in plain text (matching the `y/n/pick another` example above). Do
|
|
|
211
201
|
|
|
212
202
|
## Rationalizations to Reject
|
|
213
203
|
|
|
214
|
-
| Rationalization | Reality
|
|
215
|
-
| ----------------------------------------------------------------------------------------------------------------------- |
|
|
216
|
-
| "The top-scored candidate is obviously correct, so I can
|
|
217
|
-
| "
|
|
218
|
-
| "
|
|
219
|
-
| "
|
|
204
|
+
| Rationalization | Reality |
|
|
205
|
+
| ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
206
|
+
| "The top-scored candidate is obviously correct, so I can transition without asking the human" | The Iron Law: never transition without the human confirming the recommendation first. |
|
|
207
|
+
| "I'll write the assignee now so the pick is recorded / the user is credited" | Selection never writes `assignee`. An assignee means _in-progress_ (set by harness-execution at execution start). Writing it at selection makes the orchestrator silently skip the item — the exact bug this skill must not reintroduce. |
|
|
208
|
+
| "Affinity data is not available so the scoring is degraded -- I should just pick the first planned item" | Proceed without affinity scoring by zeroing out the affinity weight. Position and dependents signals still produce meaningful rankings. |
|
|
209
|
+
| "The feature has no spec, but I can skip brainstorming and jump straight to planning since the summary is clear enough" | No spec routes to brainstorming, spec exists routes to autopilot. A one-line roadmap summary is not a spec. |
|
|
210
|
+
| "STRATEGY.md exists, so I should let it override the top-scored candidate when alignment is clear" | The alignment bonus is bounded (max `+0.75`) and only fires when base scores are within `0.05`. It is a tiebreaker, not a hard filter — a clearly higher-scored item still wins. |
|
|
220
211
|
|
|
221
212
|
## Examples
|
|
222
213
|
|
|
@@ -252,25 +243,23 @@ Proceed? (y/n/pick another)
|
|
|
252
243
|
|
|
253
244
|
Human confirms **y**.
|
|
254
245
|
|
|
255
|
-
**Phase 4:
|
|
246
|
+
**Phase 4: TRANSITION**
|
|
256
247
|
|
|
257
248
|
```
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
Roadmap updated: docs/roadmap.md
|
|
261
|
-
External sync: github:harness-eng/harness#43 assigned (automatic)
|
|
249
|
+
Graph Connector stays planned, Assignee: — (not assigned at selection).
|
|
250
|
+
harness-execution will claim it (status=in-progress + assignee) at execution start.
|
|
262
251
|
|
|
263
252
|
Transitioning to harness:autopilot (spec exists)...
|
|
264
253
|
```
|
|
265
254
|
|
|
266
255
|
## Gates
|
|
267
256
|
|
|
268
|
-
- **No
|
|
269
|
-
- **No
|
|
257
|
+
- **No transition without human confirmation.** The CONFIRM phase must complete with explicit approval before transitioning.
|
|
258
|
+
- **No assignment at selection.** This skill never writes the `assignee` field — that field means _who is executing_ and is claimed at execution start by harness-execution. Writing it here would make the orchestrator silently skip the item.
|
|
270
259
|
- **No scoring without a parsed roadmap.** If `docs/roadmap.md` does not exist or fails to parse, stop with an error.
|
|
271
260
|
|
|
272
261
|
## Escalation
|
|
273
262
|
|
|
274
263
|
- **When no unblocked candidates exist:** Inform the human. Suggest reviewing blocked items to see if blockers can be resolved, or adding new features via `harness-roadmap --add`.
|
|
275
264
|
- **When affinity data is unavailable:** Proceed without affinity scoring (weight falls to 0 for all candidates). Note this in the output.
|
|
276
|
-
- **When
|
|
265
|
+
- **When the human asks you to assign the item to them at selection:** Explain that the `assignee` field means _who is executing_ and is claimed at execution start by harness-execution; assigning at selection makes the orchestrator silently skip the item (enforced by RMH005). Proceed with the transition instead.
|
|
@@ -98,6 +98,39 @@ Do not improvise past unknowns. An assumption that turns out wrong is cheaper th
|
|
|
98
98
|
|
|
99
99
|
Report progress with: `**[Phase N/M]** Task N — <description>`
|
|
100
100
|
|
|
101
|
+
#### Step 0: Claim the roadmap item (execution-start claim)
|
|
102
|
+
|
|
103
|
+
Execution start is the one true "who is working on this now" moment — so this is
|
|
104
|
+
where the `assignee` is written, **not** at selection (roadmap-pilot no longer
|
|
105
|
+
assigns). The invariant is `assignee ≠ null ⟺ status == in-progress`, enforced by
|
|
106
|
+
RMH005 (`harness validate`).
|
|
107
|
+
|
|
108
|
+
1. **Resolve `currentUser`** the same way roadmap-pilot does: the `--user` argument
|
|
109
|
+
if provided, else `git config user.name` (fall back to `git config user.email`).
|
|
110
|
+
2. **If `docs/roadmap.md` exists and a row matches this plan** (match by the row's
|
|
111
|
+
`Spec`/`Plan` path or the feature name from the handoff/spec):
|
|
112
|
+
- **If `currentUser` cannot be resolved, STOP** with an actionable message —
|
|
113
|
+
"Set your identity via `git config user.name`/`user.email` (or pass `--user`)
|
|
114
|
+
before starting execution" — rather than claiming with a null owner (S4-002).
|
|
115
|
+
Leave the row `planned` and unassigned.
|
|
116
|
+
- Otherwise claim it:
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
manage_roadmap({
|
|
120
|
+
path: "<project-root>",
|
|
121
|
+
action: "update",
|
|
122
|
+
feature: "<feature-name>",
|
|
123
|
+
status: "in-progress",
|
|
124
|
+
assignee: "<currentUser>"
|
|
125
|
+
})
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
This routes through the assignee-lifecycle `claim()` chokepoint
|
|
129
|
+
(first-claim-wins: if the row is already `in-progress` under a different
|
|
130
|
+
owner, the claim is a no-op and you should treat the item as taken).
|
|
131
|
+
|
|
132
|
+
- If no roadmap row matches this plan (e.g. ad-hoc execution), skip the claim.
|
|
133
|
+
|
|
101
134
|
For each task, starting from current position:
|
|
102
135
|
|
|
103
136
|
1. **Read task instructions completely** before writing any code.
|
|
@@ -344,11 +377,13 @@ Claims about task completion, test results, or code behavior MUST cite evidence:
|
|
|
344
377
|
- **`harness state show`** — View current position and progress.
|
|
345
378
|
- **`harness state learn "<message>"`** — Append a learning from CLI.
|
|
346
379
|
- **State/Learnings files** — Session-scoped when session known, otherwise `.harness/`. State updated after every task; learnings append-only.
|
|
380
|
+
- **Roadmap claim** — Phase 2 Step 0: `manage_roadmap update` with `status: in-progress` + `assignee: <currentUser>` claims the item at execution start (the assignee = "who is executing" invariant). Stop if `currentUser` is unresolvable. Marking the row `done` later auto-clears the assignee via the lifecycle `setStatus` chokepoint — never leave a non-in-progress row assigned (RMH005).
|
|
347
381
|
- **Roadmap sync** — After plan completion, `manage_roadmap sync` with `apply: true`. Mandatory when roadmap exists. No `force_sync: true`.
|
|
348
382
|
- **`emit_interaction`** — Auto-transition to harness-verification at plan completion.
|
|
349
383
|
|
|
350
384
|
## Success Criteria
|
|
351
385
|
|
|
386
|
+
- The roadmap item is claimed at execution start (`status=in-progress` + `assignee=<currentUser>`) when a matching row exists; execution stops with an actionable message if `currentUser` is unresolvable
|
|
352
387
|
- Every task executed in order, atomically, one commit per task
|
|
353
388
|
- `.harness/state.json` accurately reflects position and progress
|
|
354
389
|
- `.harness/learnings.md` has entries for sessions with non-trivial discoveries
|