Harness Roadmap Pilot
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
assigneefield names who is executing, and is written at execution start (harness-execution), not at selection.
When to Use
- When the team or individual needs to pick the next item to work on from the roadmap
- When there are multiple unblocked items and prioritization guidance is needed
- After completing a feature and looking for the next highest-impact work
- NOT when the roadmap does not exist (direct user to harness-roadmap --create)
- NOT when the user already knows what to work on (use harness-brainstorming or harness-autopilot directly)
Process
Iron Law
Never transition without the human confirming the recommendation first. Selection never writes assignee.
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).
Phase 1: SCAN -- Score Candidates
- Resolve the roadmap mode with
loadProjectRoadmapMode(projectRoot)from@harness-engineering/core.- In
file-backedmode (default): check thatdocs/roadmap.mdexists. If missing, error. "No roadmap found at docs/roadmap.md. Run harness-roadmap --create first." - In
file-lessmode:docs/roadmap.mdis intentionally absent. Synthesize the roadmap from the tracker: a.loadTrackerClientConfigFromProject(projectRoot)->createTrackerClient(config)to obtain aRoadmapTrackerClient. b.client.fetchAll()-> map eachTrackedFeatureinto aRoadmapFeatureand group by milestone (or use a single synthetic milestone if the tracker has no milestone field). c. If the tracker call fails, surface the error verbatim; do not fall back to a file-backed branch.
- In
- Parse the roadmap (file-backed only) using
parseRoadmapfrom@harness-engineering/core. (File-less mode produces the in-memory roadmap directly from step 1.) - Determine the current user:
- Use the
--userargument if provided - Otherwise, attempt to detect from git config:
git config user.nameorgit config user.email - If neither available, proceed without affinity scoring
- Use the
- Call
scoreRoadmapCandidatesForMode(roadmap, { currentUser }, config)from@harness-engineering/core(FR-S3). This is the mode-aware wrapper: infile-backedmode it delegates toscoreRoadmapCandidatesunchanged; infile-lessmode it routes throughscoreRoadmapCandidatesFileLessfor the D4 priority+createdAt ordering. Always passing through this wrapper keeps the skill mode-agnostic. - If no candidates: inform the human. "No unblocked planned or backlog items found. All items are either in-progress, done, blocked, or the roadmap is empty."
Present the top 5 candidates:
ROADMAP PILOT -- Candidate Scoring
Top candidates (scored by position 50%, dependents 30%, affinity 20%):
# Feature Milestone Priority Score Breakdown
1. Feature A MVP Release P0 0.85 pos:0.9 dep:0.8 aff:1.0
2. Feature B MVP Release P1 0.72 pos:0.8 dep:0.6 aff:0.5
3. Feature C Q2 Release -- 0.65 pos:0.7 dep:0.5 aff:0.0
4. Feature D Backlog -- 0.40 pos:0.3 dep:0.4 aff:0.0
5. Feature E Backlog -- 0.35 pos:0.2 dep:0.3 aff:0.0
Phase 2: RECOMMEND -- AI-Assisted Analysis
- For the top 3 candidates, read their spec files (if they exist):
- Read the spec's Overview and Goals section
- Read the spec's Success Criteria section
- Assess effort and impact from the spec content
1a. Read STRATEGY.md if present at repo root (strategy-alignment input). Use a Node one-liner that calls validateStrategy from @harness-engineering/core, then (when valid) parseStrategyDoc + asStrategyDoc:
node -e "import('@harness-engineering/core').then(async m => {
const v = await m.validateStrategy(process.cwd());
if (!v.present || !v.valid) { console.log(JSON.stringify({ grounded: false })); return; }
const raw = require('fs').readFileSync('STRATEGY.md', 'utf-8');
console.log(JSON.stringify({ grounded: true, doc: m.asStrategyDoc(m.parseStrategyDoc(raw)) }));
})"
When grounded: true, capture the Target problem, Our approach, and Tracks section bodies. For each top-3 candidate, compute a strategy-alignment score:
+0.5if the candidate's feature name or spec keywords plausibly advance one of theTracks(case-insensitive substring or paraphrase match)+0.25if the candidate's spec Overview cites theTarget problemorOur approachverbatim or near-verbatim0otherwise
The alignment score is a tiebreaker bonus, NEVER a hard filter:
- Apply only when the absolute difference between two candidates' base scores is
≤ 0.05(items score similarly onposition × 0.5 + dependents × 0.3 + affinity × 0.2). - Never let the bonus override a meaningful base-score difference.
- When alignment is applied, cite it in the recommendation rationale ("Tiebreaker: aligned with track 'pulse-reports' from STRATEGY.md").
- When
STRATEGY.mdis absent or invalid, skip this step silently; the recommendation proceeds without a strategy tiebreaker.
1b. Read the most recent pulse report (if any):
- List entries in
docs/pulse-reports/and filter to those matching the regex/^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}\.md$/(the canonicalYYYY-MM-DD_HH-MM.mdpulse-report filename shape). Filtering before sorting prevents non-conforming files (e.g.README.md,NOTES.md, partial-timestamp drafts) from corrupting the signal. - Lexical-sort the matched filenames (ISO timestamps sort chronologically) and take the LAST entry as the most recent.
- If zero entries match the regex (directory empty, absent, or only contains non-conforming files), soft-fail: skip this step and proceed without pulse signal. Do not block recommendation.
- For each top-3 candidate, scan the most recent pulse report's Headlines and Followups sections for keywords matching the candidate's name, milestone, or spec keywords. Note any signal that elevates priority (top followup item related to a candidate; an error spike in a candidate's area) or suppresses it (recent stable signal in candidate's area).
- When pulse signal is found, cite it verbatim in the recommendation rationale (e.g., "Pulse 2026-05-05_08-00 headline: 'auth errors up 30%' — elevates Auth Hardening").
- Use ONLY the most recent file. If older reports conflict with the most recent, ignore the older signal.
- Provide a recommendation with reasoning:
RECOMMENDATION
I recommend Feature A (MVP Release, P0, score: 0.85).
Reasoning:
- Highest priority (P0) with strong positional signal (first in MVP milestone)
- Unblocks 2 downstream features (Feature X, Feature Y)
- You completed its blocker "Foundation" -- high context affinity
- Spec exists with clear success criteria (12 acceptance tests)
- Estimated effort: medium (8 tasks in the plan)
Alternative: Feature B (P1, score: 0.72) -- consider if Feature A's scope is too large for the current time window.
Proceed with Feature A? (y/n/pick another)
Phase 3: CONFIRM -- Human Decision
Ask the human in plain text (matching the y/n/pick another example above). Do not elevate this confirmation to an AskUserQuestion tool call — candidate labels and natural header choices ("Pick candidate", etc.) exceed its 12-char header cap and the prompt is rejected as ERR.
- Wait for human confirmation.
- If yes: proceed to Phase 4.
- If pick another: ask which candidate number, then proceed with that pick.
- If no: stop. No changes made.
Phase 4: TRANSITION -- Hand Off to the Next Skill
Do NOT write the assignee field here. Selection picks the item; it does not
claim it. harness-execution claims the item (status=in-progress + assignee) at
execution start. Writing an assignee at selection makes the orchestrator treat the
item as already-claimed and silently skip it (the bug this skill must not reintroduce).
The item stays planned/backlog with Assignee: — and remains orchestrator-eligible.
Sharded mode + auto-done. When the claim is later written (at execution start),
in sharded mode (docs/roadmap.d/ present) it patches the single row's shard
and appends an assignment record to the shared _meta.md, then regenerates the
docs/roadmap.md aggregate — stage both. And there is no manual done-marking
step: a row reaches done automatically when the implementing PR merges
(merge-triggered auto-done via External-ID; the reconciler clears the assignee per
RMH005). See knowledge merge-triggered-auto-done.md.
Determine the transition target:
- If the feature has a
specfield (non-null): transition toharness:autopilot - If the feature has no
spec: transition toharness:brainstorming - Optional (feature-shaped items): before brainstorming, a no-spec item that carries
user-facing behavior may first run
product-requirementsto author a PRD (user stories + acceptance criteria + prioritization) atdocs/product-requirements/<item>/prd.md, which brainstorming then consumes. Suggest this for feature work; skip it for bugs, chores, and refactors (a PRD there is speculative ceremony).
- If the feature has a
Present the transition to the human via
emit_interaction:emit_interaction({ path: "<project-root>", type: "transition", transition: { completedPhase: "roadmap-pilot", suggestedNext: "<brainstorming|autopilot>", reason: "Feature '<name>' selected and ready for <brainstorming|execution>", artifacts: ["docs/roadmap.md"], requiresConfirmation: true, summary: "Selected '<name>'. <Spec exists -- ready for autopilot|No spec -- needs brainstorming first>. (Not assigned — harness-execution claims it at execution start.)", qualityGate: { checks: [ { "name": "roadmap-parsed", "passed": true }, { "name": "candidate-scored", "passed": true }, { "name": "human-confirmed", "passed": true }, { "name": "no-assignment-at-selection", "passed": true } ], allPassed: true } } })Run
harness validate.
Harness Integration
parseRoadmap/serializeRoadmap-- Parse and writedocs/roadmap.md(file-backed mode only). Import from@harness-engineering/core.loadProjectRoadmapMode/loadTrackerClientConfigFromProject/createTrackerClient-- Resolveroadmap.modeand obtain aRoadmapTrackerClientfor file-less mode. Import from@harness-engineering/core.scoreRoadmapCandidatesForMode-- Mode-aware scoring entry point. Import from@harness-engineering/core. In file-backed mode delegates toscoreRoadmapCandidates; in file-less mode routes throughscoreRoadmapCandidatesFileLess(priority + createdAt sort, FR-S3).scoreRoadmapCandidates-- Underlying file-backed scoring algorithm. PreferscoreRoadmapCandidatesForModefrom the skill; direct callers in file-backed-only code paths can still use this.emit_interaction-- Used for the skill transition at the end. Transitions toharness:brainstorming(no spec) orharness:autopilot(spec exists). This skill does NOT callmanage_roadmap updateto assign — theassigneefield is owned by harness-execution (claim at execution start), enforced by RMH005 (assignee ≠ null ⟺ in-progress).STRATEGY.mdalignment (Phase 2 step 1a) -- When present at repo root and valid, loaded viavalidateStrategy+parseStrategyDoc+asStrategyDocfrom@harness-engineering/core. Applied as a bounded tiebreaker bonus (max+0.75) only when candidates score within0.05on the base formula. Boundary: roadmap-pilot READS;harness-strategyWRITES. Never modifySTRATEGY.mdfrom this skill.harness validate-- Run after the transition is presented.
Success Criteria
- Roadmap is parsed and unblocked planned/backlog items are scored
- Scoring uses two-tier sort: explicit priority first, then weighted score
- AI reads top candidates' specs and provides recommendation with reasoning
- Human confirms before the transition is made
- Selection does NOT write the
assigneefield (no assignment, no history record, no external assignee sync) — the pick staysplanned/backlogand orchestrator-eligible - The
assigneefield is owned by harness-execution, which claims at execution start; RMH005 enforcesassignee ≠ null ⟺ in-progress - Transition routes to brainstorming (no spec) or autopilot (spec exists)
- 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.
- When
STRATEGY.mdis present and valid AND two candidates score within0.05on the base formula, the recommendation rationale cites strategy-alignment as the tiebreaker. The bonus never overrides a meaningful base-score difference. - When
STRATEGY.mdis absent or invalid, the skill completes without error and no strategy-alignment rationale appears in the output. harness validatepasses after all changes
Rationalizations to Reject
| Rationalization | Reality |
|---|---|
| "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. |
| "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. |
| "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. |
| "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. |
| "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. |
Examples
Example: Pick Next Item from a Multi-Milestone Roadmap
Context: A roadmap with 3 milestones, 8 features. 2 are in-progress, 1 is done, 2 are blocked, 3 are planned/backlog and unblocked. User is @cwarner who completed "Core Library Design".
Phase 1: SCAN
ROADMAP PILOT -- Candidate Scoring
Top candidates:
# Feature Milestone Priority Score Breakdown
1. Graph Connector MVP Release P2 0.78 pos:0.8 dep:0.6 aff:1.0
2. Performance Baselines Q3 Hardening -- 0.45 pos:0.5 dep:0.3 aff:0.0
3. Push Notifications Backlog -- 0.30 pos:0.2 dep:0.2 aff:0.5
Phase 2: RECOMMEND
I recommend Graph Connector (MVP Release, P2, score: 0.78).
Reasoning:
- Only prioritized item among candidates (P2)
- You completed its blocker "Core Library Design" -- maximum affinity bonus
- Unblocks "API Integration" downstream
- Spec exists at docs/changes/graph-connector/proposal.md
Proceed? (y/n/pick another)
Human confirms y.
Phase 4: TRANSITION
Graph Connector stays planned, Assignee: — (not assigned at selection).
harness-execution will claim it (status=in-progress + assignee) at execution start.
Transitioning to harness:autopilot (spec exists)...
Gates
- No transition without human confirmation. The CONFIRM phase must complete with explicit approval before transitioning.
- No assignment at selection. This skill never writes the
assigneefield — 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. - No scoring without a parsed roadmap. If
docs/roadmap.mddoes not exist or fails to parse, stop with an error.
Escalation
- 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. - When affinity data is unavailable: Proceed without affinity scoring (weight falls to 0 for all candidates). Note this in the output.
- When the human asks you to assign the item to them at selection: Explain that the
assigneefield 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.