Print the banner below verbatim before any other action — skip if dispatched as a subagent. See references/banner-preamble.md.
════════════════════════════════════════════════════════════════════════
🔨 hv-work · orchestrator-driven parallel implementation
triggers: "implement", "build these" · pairs: hv-ship, hv-review
════════════════════════════════════════════════════════════════════════
hv-work
Orchestrator-driven parallel implementation with per-task verification and commits.
Configuration
Read .hv/config.json:
models.orchestrator— model for planning and verification (defaultopus)models.worker— model for implementation subagents (defaultsonnet)work.isolation—"branch"(default) or"worktree". Ignored whenwork.dispatch == "tmux"(every slot owns a worktree by construction).work.mergeStrategy—"direct"(default) or"pr"work.dispatch—"subagent"(default) or"tmux". Selects the worker backend."subagent"dispatches in-processAgentworkers that write files while the orchestrator commits."tmux"runs each worker as its own Claude Code session in its own worktree, committing and opening a PR against the cycle branch. Seereferences/tmux-dispatch.md.work.workerSlots— integer, default3. Size of the tmux worker pool; ignored under"subagent".work.workerCommand— string, default"". Launch command for a tmux worker session; empty buildsclaude --model <models.worker> --dangerously-skip-permissions(workers commit, open PRs and run tests unattended).work.accounts— array of{name, configDir}, default[]. Maps tmux slots to independentCLAUDE_CONFIG_DIRs so each authenticates as its own account. Empty means every slot inherits the ambient config dir.work.operatorCommand— string, default"". Used to relaunch the orchestrator inside tmux when the cycle starts outside one; empty buildsclaude --continue --model <models.orchestrator> --permission-mode auto.autonomy.level—"off"(default),"auto", or"loop". Controls whether Step 13 (Learn), Step 14 (Refactor), and Step 15 (Loop continuation) nudge or invoke the next skill directly.
When to Use
- User describes a task, feature, or list of improvements
- Conversation has enough spec to act on
- Work is decomposable into 2+ independent pieces
Flow
Guard → Clarify (if needed) → Status → Plan → Isolate → Dispatch → Verify → Commit → TODO → Merge/PR → Status
Preview Mode (--preview)
When invoked as /hv-work --preview <target> (or with --preview anywhere in the args), the skill enters read-only preview mode — it produces the same approach peek the formerly-separate /hv-assume skill emitted, then stops. No writes, no commits, no helper calls beyond reads. Steps 1–15 are bypassed.
The target may be a backlog item (B07, F03, T11), a plan key (M01-S01, M01-B07), or a milestone (M01). Ambiguous → ask once; do not auto-pick.
Procedure:
Preflight only —
.hv/bin/hv-preflight. No guard, no status registration.Load context silently per
references/context-load-protocol.md. Issue all reads in parallel. For backlog-item targets under umbrella mode: parse the entry'sRepos:field (hv-todo-field <ID> repos); when umbrella mode is on (.hv/bin/hv-umbrella-onreturnsyes) and the item carries aRepos:value, resolve to absolute sub-repo path(s) via.hv/bin/hv-resolve-repos. Multi-repo items resolve to a list — keep all entries for the render. Skip repo resolution for slice / milestone targets (umbrella-flat per M02 acceptance). Ifhv-decisions-queryreturns matches, surface them in the peek's "Hard boundaries to respect" section (between "Files I'd create" and "Tests I'd add") — one line each:- <decision title> — <one-line summary>. The user's job during review is to spot conflicts before code lands.Produce the peek. Print this structure to chat. Nothing else — no preamble, no recap of what context you read:
Peek for <target>: Approach <one paragraph — the shape of what I'd do and why this over alternatives> Repo <name> (<absolute-sub-repo-path>) # omit when single-repo or no Repos: tag <name2> (<absolute-sub-repo-path-2>) # one line per repo for multi-repo items Files I'd touch - <path> — <reason> - <path> — <reason> Files I'd create - <path> — <reason> Hard boundaries to respect - <decision title> — <one-line summary> - <decision title> — <one-line summary> Tests I'd add - <test name or location> — <what it verifies> Assumptions I'm making - <named assumption that, if wrong, changes the approach> - <named assumption that, if wrong, changes the approach> Known unknowns - <thing I'd resolve mid-flight> (will pause if unresolvable) - <thing I'd resolve mid-flight> (will pause if unresolvable) If any of this is wrong, push back before /hv-work runs.Omit
Hard boundaries to respectif no DECISIONS topics matched. OmitRepoentirely when umbrella mode is off, the target is a slice / milestone, or the item has noRepos:tag. When present, render as<name> (<absolute-path>)resolved via.hv/repos.json. Multi-repo items render one indented line per resolved sub-repo — so the user can verify every dispatch target before/hv-workruns.Be specific. "I'd touch the auth code" is useless — cite paths. If you don't know the path well enough to cite it, say so under Known unknowns.
Stop. Do not auto-invoke
/hv-work(without--preview), write a plan, or take any action. The user reviews and either:- Says "go" — they invoke
/hv-work <target>themselves (without--preview). - Pushes back — they redirect, you restate the peek with corrections.
- Asks for a written plan — offer
/hv-plan <target>.
- Says "go" — they invoke
Key principles for preview mode:
- Pure read. No writes, no commits, no helper calls beyond reads.
- Be specific. Generic peeks are useless. Cite paths, test names, function names.
- Name assumptions. The whole mode's value is making implicit choices visible.
- Stop after the peek. No auto-continuation; the user's pushback is the point.
- Plan beats peek for high-stakes work. Offer
/hv-planif the user wants something durable rather than ephemeral.
Orchestrator-model contract (F35, loop mode). When /hv-work Step 4's F34 uncertainty pre-flight needs a peek, it runs this Preview Mode procedure inline (not via recursive Skill dispatch) — the peek inherits the orchestrator model since the cycle is already running under it. The Step 4 chain reads the peek output from chat context and proceeds to /hv-plan --auto-loop. Manual invocations from /hv-next or the user's prompt (/hv-work --preview <ID>) are unconstrained — the user is in the loop and can correct any peek that under-performs.
Step 1 — Preflight & Guard
.hv/bin/hv-preflight
See docs/reference/preflight.md for exit-code handling.
.hv/bin/hv-guard-clean "/hv-work"
Exit 0 = clean, continue. Exit 2 = not a repo, surface and stop.
Exit 1 (dirty tree) — auto-sweep known tool siblings first. Some toolchains generate sibling files after a previous /hv-work wave finished (Godot .gd.uid, Xcode .xcworkspace/contents.xcworkspacedata, SwiftPM Package.resolved, Tuist-regenerated .xcodeproj, .DS_Store). If these are all that's dirty, they belong in a chore: commit, not a refusal.
git status --porcelain
Classify every line:
- Sibling artifact — path matches a sibling of a tracked file (e.g.
Foo.gd.uidnext to trackedFoo.gd), or matches one of these patterns:*.gd.uid,*.xcworkspace/contents.xcworkspacedata,Package.resolved,*.xcodeproj/project.pbxprojregenerated without meaningful diff,.DS_Store. - User change — anything else.
If every dirty path is a sibling artifact, sweep them into a single commit and continue:
git add -A -- <matching paths>
git commit -m "chore: sweep tool-generated siblings before hv-work"
If any path is a user change, stop with the original guard message — the user decides whether to stash, commit, or discard.
Greenfield variant. On a fresh git init'd repo (no commits yet), hv-guard-clean emits a tailored message pointing at the chore: import initial files baseline commit. Run it and re-invoke — the guard then sees a clean tree.
Don't narrate the sweep unless it happened; silent pass-through is the common case.
On any Step 1 guard failure that stops /hv-work (exit 2 not-a-repo, or exit 1 user-change dirty tree) — this is a terminal path; the user is about to step away from the loop to resolve. Per the F19 terminal-path-only convention (mirrored in /hv-next empty-backlog and /hv-pause), surface any [Auto:Loop] decisions logged during this loop session before printing the guard message:
Surface any [Auto:Loop] decisions per references/terminal-loop-surface.md (silent when empty). Print the surface verbatim above the guard message.
After surfacing, clear the loop timestamp so the next loop session starts fresh:
.hv/bin/hv-loop-stamp clear # no-op when loopStartedAt is already unset
Initialize task list. Follow the canonical pattern in references/task-list-init.md — load TaskCreate(…) via ToolSearch select:TaskCreate,TaskUpdate if needed, then create one task per phase below.
Phases:
- Preflight & guard — clean tree + repo confirmed (Step 1)
- Register status — branch named,
hv-status-addrecorded (Steps 3, 5) - Plan tasks — wave layout + briefs ready (Step 4)
- Branch / worktree — isolation set up per
work.isolation(Step 5) - Dispatch & verify per wave — workers run, orchestrator verifies each completion (Steps 6–8)
- Commit + TODO + sweep — per-task commits, TODO entries marked complete, item plans tombstoned, tool siblings swept (Steps 7.5, 8.5, 9, 9.5)
- Merge/PR & report — integration + status removal + summary + post-cycle nudges (Steps 10–15)
- Knowledge lifecycle — hit-tracking and contradiction logging (Steps 2.5, 4)
Step 2 — Clarify Ambiguous Briefs (only when needed)
If — and only if — the current brief is too thin to plan concrete tasks (missing scope, conflicting requirements, or two equally plausible interpretations), use the AskUserQuestion tool to resolve the ambiguity before touching any code. Otherwise skip this step entirely — the default is to proceed.
Good reasons to ask:
- The scope hits 2+ incompatible files or areas, and picking one vs. both changes the plan materially.
- A requirement is vague in a way that yields opposite reasonable implementations (e.g., "add sorting" — ascending or descending, stable or not, which columns).
- Multiple captured items imply different orderings, and the user didn't say which to tackle first.
Bad reasons to ask (don't):
- To confirm you understand — just act.
- For preferences you can infer from
KNOWLEDGE.mdor the existing codebase. - Style choices inside an agreed scope — that's implementation.
When asking, use a single AskUserQuestion call with 1-3 questions. Each question:
- Short
header(e.g.,"Scope","Target","Order"). - Options map to concrete plans. Mark the most likely intent
(Recommended). - For conflicting items, use
multiSelect: trueand ask which subset to include in this run.
Plain-text fallback: ask once; on ambiguity, default to Recommended and state it explicitly in the dispatch brief. See references/ask-user-question-fallback.md.
Loop mode exception: if autonomy.level == "loop" and the brief is genuinely ambiguous (you'd otherwise ask Step 2), the routing depends on the item's shape:
- Major + Milestone-tagged item — defer to Step 4's auto-dispatch chain. The chain auto-resolves design via
/hv-brainstorm --auto-loop(writes a design artifact with[Auto:Loop]decisions for fresh picks), then runs the uncertainty pre-flight + plan dispatch (/hv-plan --auto-loop). Step 2 does not stop in this case — the chain owns design resolution under loop. - Non-Major or untagged item — stop the loop and surface the question for the user to resolve. Do not silently pick a default — invisible decisions across N looped items defeat the point of the loop. The user resolves and re-invokes
/hv-next(or this/hv-work) to continue the queue.
Step 2.5 — Detect Knowledge-vs-Correction Contradictions (F03 lifecycle)
Detect knowledge-vs-correction contradictions (F03 lifecycle). When Step 2's AskUserQuestion produces a user response that contradicts the cycle's framing (the user picks "Pick different items", supplies an "Other" free-text correction that pushes back on a stated assumption, or otherwise redlines the planned direction), check whether the user's correction text overlaps with any bullet returned by Step 4's eventual K+D query.
V1 overlap heuristic — simple case-insensitive substring match: for each candidate bullet from the K+D query, extract its body text; if a meaningful fragment (≥4 contiguous lowercase words) from the bullet appears in the user's correction text, the bullet is a contradiction candidate. Log via:
.hv/bin/hv-knowledge-contradiction --add --topic <T> --title <S> --text "<first 200 chars of correction>"
Process candidates in parallel — the helper serializes its sidecar writes behind a per-file lock, so concurrent calls don't lose updates. /hv-learn Step 9 surfaces these candidates at session end and asks per-bullet whether to demote.
Edge case: this step only fires when Step 2 actually surfaced an AskUserQuestion AND the user provided a non-Recommended answer. The Step 4 query happens later — so this detection ALSO fires later, comparing the now-loaded bullets against the earlier correction. Practical sequencing: cache the correction text in the cycle's working memory at Step 2, then run the overlap check after Step 4's K+D returns matches.
Skip silently when Step 2 was skipped (no clarification needed).
Step 3 — Register in Status
After picking the branch name:
Single-repo:
.hv/bin/hv-status-add <branch> <ID1>,<ID2>[,...] [worktree-path]
Umbrella mode (when umbrella.enabled is true and items carry Repos:): parse the Repos: field from each item's TODO entry. The value is a comma-separated CSV — single-repo items have one name (Repos: web), multi-repo items have two or more (Repos: web, api). All items in a wave must share the same set of repos.
Single-repo wave: pass the one name via --repo:
.hv/bin/hv-status-add --repo <repo-name> <branch> <ID1>,<ID2>[,...] [worktree-path]
Multi-repo wave: register one entry per (branch, repo) pair via hv-status-add-multi:
.hv/bin/hv-status-add-multi --branch <branch> --items <ID1>,<ID2>[,...] --repos "<repos-csv>"
Parse the Repos: field for each item using hv-todo-field:
.hv/bin/hv-todo-field <ID> repos
This uses the canonical parse_todo_fields from hvlib, so multi-repo CSVs (web, api) are captured intact and the parser correctly handles Detail:/Related:/Milestone: boundaries that the old grep chain could over-eat. Hand the resulting CSV to hv-resolve-repos for validation; if it exits non-zero, surface the missing names and stop.
Idempotent on (branch, repo) — call again with the worktree path(s) once Step 5 creates them.
Step 4 — Plan Tasks
Plan-as-artifact check (first). If the work has a milestone-and-unit key — an item tagged to a milestone (Milestone: M01 on B07 → key M01-B07) or a slice (M01-S01) — check for an existing plan:
.hv/bin/hv-plan-show <milestone>-<unit> 2>/dev/null
If a plan exists, use it as the orchestrator's plan — its task decomposition, files, verify steps, and assumptions become the dispatch briefs in Step 6 instead of decomposing ad-hoc. Restate any user redlines from the conversation, but don't silently re-derive what the user already signed off on. If the conversation contradicts the plan, ask the user whether to update the plan first (/hv-plan again) or proceed and ignore it.
Loop-mode auto-dispatch chain (B28 / F32 / F34 / F35). In loop mode with a Major + Milestone-tagged item but no plan, /hv-work runs the full research → plan chain in three steps, all via the Skill tool so the dispatched skills inherit the orchestrator model:
- Design pre-flight (B28). If
.hv/designs/<itemId>.mdis absent, dispatch/hv-brainstorm --auto-loop <itemId>. The dispatched skill auto-resolves design questions (Local-first → Bounded web → Placeholder), logs[Auto:Loop]decisions for fresh picks, and writes.hv/designs/<itemId>.mdwithauto: truefrontmatter. When a design already exists, this step is a no-op. - Uncertainty pre-flight (F34). Run
.hv/bin/hv-uncertain <itemId>. Exit 0 (uncertain, reasons on stdout) → run the Preview Mode procedure (above) inline with<itemId>as the target; the peek prints to chat and lands in the orchestrator's session context. Exit 1 (certain) → skip the peek. - Plan dispatch (F32 / F35). Dispatch
/hv-plan --auto-loop <milestone>-<itemId>./hv-planStep 3 reads the design artifact as soft input (already wired), and the auto-resolution pipeline writes the plan with all picks honored.
After the chain returns, re-run the plan-as-artifact check at the top of this step — the plan now exists; use it as the orchestrator's plan. Off and auto modes skip this chain entirely and fall through to manual decomposition. See references/loop-mode-plan-dispatch.md for the full choreography.
If no plan exists and the loop-mode dispatch above did not fire (off/auto, or Minor/untagged item), proceed with the steps below.
From the conversation context:
Consult knowledge + decisions. Apply the canonical K+D query pattern (
references/knowledge-consult.md) with topics inferred from the planned work areas. Also run.hv/bin/hv-glossary-read "<terms appearing in the TODO entry or task plan>"for any domain term used in the TODO entry (terms live in.hv/KNOWLEDGE.md's## Glossarytopic), and surface inline conflict-call-outs (synonym or drift) when the user's wording deviates from the canonical term during the cycle. Carry matches into Step 6 briefs as**Known gotchas:**(relevant knowledge bullets only) and**Hard boundaries:**(full decision entries — rule + Why + Forbids + Permits). Workers must treat boundaries as constraints, not hints. If a planned task would violate a decision, stop and surface to the user before dispatching.- Soft-cap check. Run
.hv/bin/hv-map-cap-check— emits a one-line nudge to stderr if the subsystem count is at or above the configured soft cap. Never blocks.
REQUIRED — Register hits on consumed bullets (F03 lifecycle). After writing the Step 6 briefs, apply the hit-register pattern from
references/knowledge-consult.mdHit-register after consumption: for each bullet that landed in a brief's**Known gotchas:**section, call.hv/bin/hv-knowledge-hit --topic "<T>" --title "<first-line-of-bullet>"once, issuing all calls as a single parallel batch — the helper serializes its sidecar writes behind a per-file lock, so concurrent calls don't lose hits. Bullets returned but pruned before the briefs don't earn credit. Silent on success. Provisional bullets auto-promote to confirmed oncehits >= learn.promoteThreshold(default 3).- Soft-cap check. Run
Identify discrete tasks — files to create/modify, what changes, acceptance criteria.
Absorb wave-internal file collisions. Before grouping into waves, scan task pairs for any two tasks whose modified-file sets intersect — not just rename / link-sweep. Under
work.isolation: "branch"two write-only workers editing the same file race on disk (the second worker'sEditreads sibling-mutated content), so a same-file pair would otherwise force serialization across waves. The orchestrator's standing recourse is absorption: fold one task's same-file portion into the other task's Step 6 brief at dispatch time, leaving the absorbed task touching only files no other task writes. Both then run as parallel write-only workers. This is a reproducible orchestrator-side technique, not per-orchestrator improvisation — resolve every intersecting pair by absorption (preferred), clean split-ownership, or serialize-across-waves before grouping. Rename + link-sweep is the canonical instance; use.hv/bin/hv-plan-rename-check <old-name> [<scope>...]as ground truth for it (re-run at Step 7 to catch enumeration gaps). Seereferences/loop-mode-plan-dispatch.mdAbsorb wave-internal file collisions for the absorption choreography, the M02-S02 worked example, and the rename + link-sweep resolution table.Disjoint file sets are not proof of independence — scan for shared-symbol collisions too. Two tasks can hold non-intersecting file sets and still break the merged tree, because the break exists in neither worker's diff. Two shapes: a task that widens, narrows, or re-types a shared symbol's signature while a sibling adds a fresh call to it, and a task that stops emitting a constant, key, or output field while a sibling starts depending on it. Each worker's own output is internally consistent and Step 7's per-task diff review passes both — the defect is only visible in the union, and only to something that resolves symbols across the whole tree. Rename is the one instance of this class the rules above already catch (
hv-plan-rename-check); signature changes and dropped emissions are not renames and no grep for the old name finds them. When a task's brief changes a symbol's shape rather than its name, either serialize it ahead of every task that references the symbol, or absorb the call-site updates into it. When the project has a whole-tree resolution check available (typecheck, compile,hv-qaexecutable check), run it once after Step 7.5 commits the wave rather than per task — per-task verification structurally cannot catch this class.Group into dependency waves:
- Wave 1: independent files → parallel
- Wave 2+: depend on wave 1 outputs → sequential or next parallel batch
Step 4.5 — Umbrella Pre-Flight (when umbrella mode is on)
Umbrella mode is in effect when .hv/repos.json registers ≥1 sub-repo (the data is truth; umbrella.enabled is informational). Detect with .hv/bin/hv-umbrella-on — see references/umbrella-mode.md for the registry shape, resolution helpers, and Repos: field semantics.
If hv-umbrella-on returns no, skip this step entirely (single-repo path).
If yes:
Every item must carry
Repos:. Parse via.hv/bin/hv-todo-field <ID> repos. If any item lacks a tag, stop with: "Error:[<ID>]lacks aRepos:tag. Re-run/hv-captureto add it. Cannot route to a sub-repo."All items in a wave must resolve to the same repo set (as a set, order-independent). Single-repo and multi-repo items can't mix in one wave; two multi-repo items must list the same names. On divergence, stop with: "Error: items in this wave target different sub-repo sets:
<set-a>vs<set-b>. Split into separate/hv-workruns."Validate every name in the resolved set via
.hv/bin/hv-resolve-repos "<csv>"(exits 1 with stderr on any missing name). On failure, stop with: "Error:Repos: <name>not registered in.hv/repos.json. Run/hv-initfrom the umbrella root to register sub-repos."Walk-up convenience (single-repo only). If
/hv-workwas invoked from a cwd that.hv/bin/hv-resolve-reporesolves to a registered sub-repo, default that sub-repo as the wave's scope when items lack an explicitRepos:tag. Multi-repo items always come from the capturedRepos:field — no cwd default.
When the gate passes, carry the resolved sub-repo set forward to Step 5 (branch / worktree creation) and Step 10 (merge / PR).
Step 5 — Create Branch or Worktree
Choose a descriptive name (e.g., hv/quick-switch, hv/fix-timer-badge).
Backend branch — work.dispatch == "tmux"
When work.dispatch is "tmux", skip the isolation guard and the branch/worktree patterns below entirely — they describe the subagent backend.
First, confirm this session is inside tmux. It is a precondition, not a nicety.
.hv/bin/hv-worker-session check # exit 0 = inside, exit 1 = outside
The backend is worth its cost for exactly one reason: a worker that needs a decision can idle and a human can answer in that worker's pane. Launched from a terminal that isn't already inside tmux, the worker windows land in a detached session nobody is looking at — every escalation goes unanswered and the backend silently degrades into a worse subagent mode. Don't proceed on the assumption someone will attach later.
Exit 1 (outside tmux) — hand the cycle over and stop. Write a short instruction file telling the operator what it is resuming (the cycle target, the wave layout so far, and that it should continue from Step 5), then:
.hv/bin/hv-worker-session ensure --instruction-file <path>
That creates the session, spawns an operator window running claude --continue (which resumes this conversation, so the plan and briefs survive), pastes the instruction, and prints the attach command.
Then stop this cycle immediately. Print the helper's attach block verbatim and end the run. Do not continue to the pool, do not dispatch, do not "keep going in case the handoff failed" — two orchestrators driving one pool dispatch the same task twice and race on the same slots. The handoff either worked (the operator is running it) or the helper exited non-zero (report that and let the user attach by hand). This is a terminal path, so surface any [Auto:Loop] decisions per references/terminal-loop-surface.md before printing the block.
Exit 0 (inside tmux) — continue. After creating the cycle branch, stand up the worker pool:
git checkout -b <cycle-branch>
.hv/bin/hv-status-add <cycle-branch> <ID>[,<ID>...]
.hv/bin/hv-worker-pool init --slots <work.workerSlots> --base <cycle-branch>
hv-worker-pool init is idempotent — it creates only the slots that are missing and rebuilds any whose worktree went away. Slots persist across cycles by design; the tmux windows are what get recreated per dispatch.
work.isolation does not apply on this path: each slot has its own worktree and therefore its own .git/index, which is the precondition the isolation guard exists to enforce. Don't also evaluate the guard — it would be checking a condition that cannot occur.
Preconditions worth failing fast on: tmux on PATH, and a claude binary (or a work.workerCommand that resolves). If either is missing, stop and tell the user to either install it or set work.dispatch=subagent — do not silently fall back to the subagent backend, because the user chose this path deliberately and a silent downgrade hides that it didn't happen.
Everything below this point in Step 5 applies to work.dispatch == "subagent" only.
Isolation guard (fires before any worker dispatch)
Before any worker is dispatched, abort fatally if the planned wave has ≥2 commit-producing parallel workers AND work.isolation == "branch" — branch isolation forces concurrent commit-producing workers to share .git/index, which races even on disjoint files. Read-only workers (research, lint-only verifications, smoke validators that don't commit) are exempt; under the default write-only pattern the guard rarely fires because the orchestrator owns commits in Step 7.5. See references/isolation-guard.md for the abort message, the M02-S01 incident that motivated the rule, and the full Forbids / Permits block.
Branch / worktree creation
Pick the pattern from references/isolation-patterns.md based on work.isolation ("branch" or "worktree" from .hv/config.json) and whether umbrella mode is on (Step 4.5 resolved the sub-repo set; carry it forward). The reference's decision table covers all five combinations: single-repo branch, single-repo worktree, umbrella sub-repo branch, umbrella sub-repo worktree (Layout B), umbrella multi-repo branch (via .hv/bin/hv-multi-branch-create, which runs an atomic precheck across every named repo before creating any branches).
The most common case — single-repo, branch isolation — is just:
git checkout -b <branch>
.hv/bin/hv-status-add <branch> <ID>[,<ID>...]
For umbrella-mode branch creation (single sub-repo, multi-repo, Layout B worktree), see references/umbrella-mode.md Branch creation — that reference owns the canonical umbrella ceremony.
Orchestrator stays at the repo root (or umbrella root in umbrella mode); workers cd into their assigned directory before any file operation, and use absolute paths in their briefs.
Step 6 — Dispatch Worker Agents
Dispatch vs orchestrator-direct. When the wave is N near-identical mechanical inserts on disjoint files (e.g. an 18-site SKILL.md sweep), prefer orchestrator-direct parallel Edit calls over dispatching N write-only subagents — dispatch overhead exceeds the benefit when the orchestrator already has per-site content. Litmus: "is this task one Edit against a uniquely-anchored old_string?" — yes → inline; no → dispatch.
For each independent task, dispatch a subagent with the worker model:
You are implementing Task N of [total].
[UMBRELLA: "Sub-repo: <name>. Run all git operations from <absolute-sub-repo-path>; the umbrella's `.git/` is shared coordinator state, NOT your target."]
[WORKTREE: "Working directory: <absolute-worktree-path>. cd there before any file operations."]
**Goal:** [one sentence]
**Files:**
- Create: [paths]
- Modify: [paths with line references]
**What to do:**
[Precise instructions — what to read, what to change, exact code where possible]
**Known gotchas:**
[Relevant bullets from hv-knowledge-query output]
**Hard boundaries:**
[Relevant entries from hv-decisions-query — full rule + forbids/permits, not just the rule. Workers MUST respect these; the orchestrator's verification step (Step 7) checks the diff for violations.]
**Canonical terms:**
[Relevant terms from hv-glossary-read — definition + aliases. Workers MUST use these canonical names in code/comments/commit messages where they apply; aliases are listed so divergent user phrasing in the TODO entry maps back to the right term.]
**Critical constraints:**
[Behavior preservation, patterns to follow, things NOT to touch]
**Claims to verify before building on them:**
[Every factual claim this brief rests on — a line number, a call-site count, "function X already returns Y", "this is the only adopter". Check each one first. If a claim is false, STOP and report which claim and what's actually there — do not implement around it. A wrong claim usually means the task is wrong, and the orchestrator needs to know that more than it needs the code.]
**Do NOT run `git add` or `git commit`.** Write changes to files only. The orchestrator owns the commit phase (Step 7.5) — your job is to leave a clean working-tree diff matching the brief.
**Suggested commit message:** [exact commit message — orchestrator uses this in Step 7.5]
**On completion:** report the list of files you modified, plus any tool-generated siblings the toolchain produced, and confirm you did not stage or commit. Name any brief claim that turned out false, even if you worked around it.
Umbrella mode notes: when umbrella mode is on, the [UMBRELLA: ...] line replaces the WORKTREE line if the wave uses branch isolation; both lines appear together if the wave uses Layout B worktrees. The sub-repo path is the absolute path resolved via .hv/repos.json (single-repo callers ignore both lines). Workers MUST cd to the named directory before any git command — the orchestrator stays at the umbrella for .hv/ access, so worker commands run in the umbrella's cwd by default and would target the wrong .git/.
Multi-repo dispatch: for a wave with <N> sub-repos in its resolved set, dispatch one worker per sub-repo, each with the sub-repo's name and absolute path in its [UMBRELLA: ...] line. Workers run in parallel — each repo has its own .git/index, so cross-repo parallelism doesn't trip the parallel-waves-require-worktree-isolation guard (which fires only when ≥2 workers share one .git/). Each worker's brief lists only the files in its own sub-repo; the orchestrator verifies each repo's commit independently in Step 7.
Rules for briefs: exact paths + line numbers; show the pattern to follow; name the suggested commit message; read-first, minimal-diff, no unrelated changes; workers do NOT stage or commit.
Enumerate the brief's falsifiable claims — precision is not correctness. A brief specific enough to execute (exact line numbers, exact call-site counts, "the only adopter is X") is also specific enough to be wrong, and a worker handed a precise wrong instruction implements it faithfully. The claims worth listing are the ones the task's shape depends on: if the claim is false, the task is the wrong task, not just a task with a bad line number. Stale plans are the usual source — see KNOWLEDGE.md's Re-grep the actual surface before executing a stored plan, which is the orchestrator-side half of the same failure. Leave the section out when the brief carries no such claim (a from-scratch file creation, a mechanical sweep with a grep-derived list); an empty ritual section trains workers to skip it.
Cross-referencing parallel artifacts — pre-bake citations. When two parallel workers author artifacts that cite each other, pre-specify the citation language in each brief; neither worker should need to read the other's output. Works for structural cross-references (cite by path + named role); serialize when citation must quote or restate.
Doc-writer + helper-writers in the same wave drifts. Workers authoring docs that show helper output or signatures while sibling workers are writing those helpers will paraphrase the brief and drift from the real code. Pin exact signatures verbatim into the doc-brief, or serialize the doc writer after the helper commit so it reads the actual implementation.
Rename-task addendum. Briefs for git mv old new (or equivalent rename) tasks MUST carry the Step 4 grep step — instruct the worker to run git grep -l "<old-name>" -- <scope> before reporting and extend coverage to every match. Step 7 re-runs the same grep; both layers catch enumerate gaps.
Launch all independent agents in one message (parallel tool calls) — write-only workers don't race on .git/index, so this is safe under any isolation mode. Don't announce — just do it.
Backend branch — work.dispatch == "tmux"
Same brief, different transport. Write each task's brief to a file and dispatch it to a slot:
.hv/bin/hv-worker-dispatch --slot <wN> --brief-file <path>
Two differences from the subagent path, and only two:
- Prepend the standing worker contract from
references/tmux-dispatch.mdThe worker contract. A tmux worker boots with none of this session's context — no conversation, no loaded KNOWLEDGE, no plan — so the rules the subagent path gets implicitly (stay in your tree, stage explicit paths, escalate rather than guess, cite the channel an approval came through) must be in the brief text. The contract also defines the two sentinels the worker prints,HV-BLOCKEDandHV-DONE, which Step 7 routes on. - The brief tells the worker to commit and open a PR against the cycle branch, replacing the "Do NOT run
git addorgit commit" line. Keep the**Suggested commit message:**line — the worker uses it directly rather than the orchestrator.
The brief body — Goal, Files, What to do, Known gotchas, Hard boundaries, Canonical terms, Critical constraints, Claims to verify — is byte-identical to the subagent path. Don't fork the template; a second copy drifts.
Dispatch all slots for a wave in sequence (each call returns once pickup is confirmed), then move to Step 7's poll loop. hv-worker-dispatch exits 4 if a brief never submitted — treat that as a failed dispatch and retry that slot once before reassigning the task.
Edit-tool race in parallel same-file workers. When parallel workers edit the same file at different ranges, an Edit call may report "File has been modified since read" after a sibling worker's edit invalidates the cached state. Mitigation: re-Read the file, re-run the same Edit with byte-identical old_string — do NOT regenerate old_string from scratch (risks sibling-edited content).
Alternative: legacy worker-commits (opt-in)
When a wave touches files that overlap and write-only would racing on disk (rare — usually a planning failure that should be re-decomposed), or when an out-of-band tool requires a commit between worker steps, fall back to the legacy pattern: workers stage and commit themselves. Under this pattern:
- Single worker per wave under
work.isolation: "branch". Multiple workers committing to the same.git/indexis forbidden by the [decisions / Skill Authoring / Parallel waves require worktree isolation] rule. Either re-plan as N sequential single-worker waves on a shared branch (the M02 multi-feature pattern), or flipwork.isolationto"worktree"so each worker has its own index. - Brief includes
**Commit with message:** [exact text]— workers stage their own files and commit, one commit per task. Skip Step 7.5 (orchestrator-side commit) on this path.
This path is documented for completeness; the default write-only pattern above is preferred for new work.
Step 7 — Verify Each Completion
Orchestrator verifies internally (don't narrate):
Trust the diff, not the worker's narrative — when a worker re-enters files in a later wave, it can mis-attribute its own writes to an earlier wave even when the on-disk output is correct. Verification reads git diff and the resulting files; the worker's completion report is supplementary.
- Inspect pending changes:
git status --porcelainthengit difffor the files the worker reported. (Legacy path:git log --oneline -1if the worker committed.) - Read modified files — changes match the brief.
- Structural checks: grep for expected patterns, no regressions.
- Rename validation (Step 4 rename + link-sweep rule). Re-run
git grep -l "<old-name>" -- <scope>; files outside the worker's modified-file set → dispatch a fix-up to extend coverage before staging. - Claim-weight check on gap-fills. When a worker fills a gap left by extraction (sparse carrier prose, missing rationale), expect plausible-sounding editorial that wasn't in the source. Verify the claim weight of any sentence the worker authored, not just structural shape — plausible ≠ sourced.
- A worker that disputes its brief is a PASS on the worker and a FAIL on the plan. When a completion report names a false claim from the
**Claims to verify**section, do not re-dispatch the same brief with a patched line number — confirm the worker's finding against the code yourself, then re-derive the task from what's actually there. If the correction changes what the task should accomplish (not just where), take it back to Step 4 and re-plan; if it invalidates a stored plan's premise, say so in the commit message rather than editing the plan back. A worker that returns exactly what the brief asked for on a task with falsifiable claims and reports no fricti
…(truncated)