harness-claude-code · workflow guide

How the workflow works

A walkthrough of the top-level commands, the role-based agents, and the GitHub-issue lifecycle they drive together — including who talks to whom, where the user sits in the loop, and how every state transition is recoverable because the source of truth lives on issues, labels, and a per-unit task checklist.

New in v0.41 — the /ship command unifies all three kinds of work into one loop. It is a superset of /implement-feature: the same feature flow, plus an enhancement lane (one feature-shaped kind:enhancement issue, no interview) and a full bug lane (read-only analyze-bug → human approval gate → the lighter fix-bug Workflow). Kickoff routes by kind: — feature/enhancement launch implement-slice.mjs, bugs launch fix-bug.mjs. Milestone is optional: omit it for the repo-wide maintenance lane. /implement-feature remains as a feature-only fallback. See what changed.

The big picture

The harness is built from top-level commands and a set of role-based agents. Commands are orchestrators: they don't write code themselves; they bring agents onto a team, brief them, route messages, and — in execution — launch a background unit-cycle Workflow per unit of work against GitHub-issue state, backed by a pure-shell discovery script that decides what's eligible each pass. The unified execution command is /ship, driven by ship-finder.sh (five stages: reconcile · analyze-bug · kickoff · fix-pr · close-pr). It spans all three kinds of work and routes kickoff by kind:kind:feature / kind:enhancement launch implement-slice.mjs, kind:bug launches fix-bug.mjs. (/implement-feature + task-finder.sh remain as a feature-only fallback with four stages.) Durable state lives in three places: the GitHub issue and its labels, the issue body's task checklist (or, for a bug, its approved analysis comment), and the unit branch's commits — which means the loop can be stopped, resumed, or re-run without losing track of where work is.

                ┌──────────────────────────────────────────────────────────┐
                │                       USER (you)                        │
                └──┬────────────────────┬──────────────────────────────┬──┘
                   │ /deep-dive-feature │ /scaffold-project            │ /ship [milestone]
                   ▼                    ▼                              ▼
            ┌────────────┐         ┌────────────┐                ┌──────────────┐
            │ product-   │         │ scaffold-  │                │ orchestrator │
            │  owner →   │         │  project   │                │ (ship-finder │
            │ design-    │         │ (templates │                │  .sh · 5     │
            │  lead →    │         │ + seeds    │                │  stages)     │
            │ architect  │         │  locked    │                └──────┬───────┘
            │ + writers  │         │  tokens)   │            kickoff routes by kind:
            └─────┬──────┘         └─────┬──────┘                       ▼
                  │                      │            ┌───────────────────────────────────┐
                  ▼                      ▼            │ feature / enhancement → implement-  │
       PRD + design system + ADRs   bootable stack   │   slice.mjs (author E2E → cov gate  │
       + surfaces + contracts       + tokens         │   → implement → pass E2E → review)  │
       (committed on feature        (compose up;     │ bug → fix-bug.mjs (regression test  │
        branch; PR opened)           PR opened)      │   first → fix → review)             │
                                                     └─────────────────┬───────────────────┘
                                                                       ▼
                                                       draft PR → fix-pr / close-pr
                                                       (the outer loop's other stages)

       (bug lane adds an up-front  analyze-bug  stage: read-only reproduce + root-cause
        → # Bug Analysis comment → human approves → kickoff launches fix-bug.mjs)

What changed: 0.39 → 0.40

Everything in discovery (/deep-dive-feature, /scaffold-project) is unchanged. The whole shift is in execution. Through 0.39, /implement-feature was a nine-stage state machine: each step of a slice's life — implement a task, review it, fix it, validate E2E, review the slice, fix the slice — was its own label-driven stage that dispatched a one-shot agent and then ended the pass, handing control back to GitHub labels until the next pass re-discovered the work. In 0.40 that entire inner cycle collapses into one background implement-slice Workflow per slice, and the outer command keeps only the four stages that genuinely need to live outside a single run.

0.39 — nine label-driven stages before

  • One issue per slice + one sub-issue per task, each carrying level:* / type:* / review:* / e2e:* labels
  • task-finder.sh ran nine per-stage scripts; the orchestrator round-tripped each task and slice through GitHub one stage per pass
  • Each lifecycle step (implement-task, review-task, fix-task, prepare-slice, review-slice, fix-slice…) was a separate orchestrator-driven dispatch
  • Crash recovery leaned on engineer handoff docs (a budget-gate hook + a precompact hook wrote /tmp handoff files)
  • Task state was the set of open/closed task sub-issues and their labels
  • review-slice (added in 0.39.26) flipped the review:* label and opened the PR itself

0.40 — one Workflow per slice after

  • One issue per slice only; tasks live as a static-ID checklist in the slice body. The retired label families are deleted
  • task-finder.sh runs four stages: reconcile (0), kickoff-launch (1), fix-pr (8), close-pr (9)
  • Stage 1 launches implement-slice.mjs; that one run owns author E2E → coverage gate → plan → implement → pass E2E → gate review → fix → quality review → fix → open draft PR
  • Crash recovery is the slice branch's WIP commits + the durable checklist + the Workflow resume journal. Fix loops are uncapped (loop to confidence-to-pass); the budget gate and handoff docs are gone
  • Task state is the [ ]/[x] checklist; commit trailers carry Task: <static-id> (not Refs #<task#>)
  • the inlined runReviewSlice() fan-out posts its comment and returns the verdict — it flips no label and opens no PR; the surrounding implement-slice phases own those

The differences that matter

Dimension 0.39 0.40
outer loop 9 stages, all owned by /implement-feature 4 stages — reconcile / kickoff-launch / fix-pr / close-pr. The inner cycle is no longer the command's concern.
inner slice cycle spread across stages 2–7, one dispatch + label flip per pass per step one background implement-slice Workflow that runs the whole cycle serially in a single run
issues created slice issue plus a typed task sub-issue per task (e2e/backend/frontend) slice issue only; the task breakdown is an inline static-ID checklist in the slice body
task identity GitHub issue number; commits trail Refs #<task#> permanent static ID (s42.be.1); commits trail Task: <id> — never translated to an issue number
labels level:*, type:*, review:*, e2e:* + status/merge those four families deleted; GitHub keeps only the slice issue, the status:in-progress lock, the status:need-attention halt, and the PR / merge:*
E2E coverage specs authored, then validated against a stack (no pre-implementation gate) new coverage gaterunReviewSlice('test-coverage') reviews the authored specs before any production code is written
slice review contract flips review:runningreview:passed/need-fix and opens the PR posts the comment and returns the verdict; the post-implementation review is split into a gate review (reviewMode:'gate', spec/contract/security, blocks) and a quality review (reviewMode:'quality', code-quality axes, advisory); plus the pre-implementation scope:'test-coverage' gate; inlined as runReviewSlice(); the surrounding phases own the lock + PR
crash recovery engineer handoff docs (budget-gate + precompact hooks) WIP commits + durable checklist + Workflow resume journal; fix loops are uncapped (loop to confidence-to-pass), kept convergent by anchored re-review rounds (closure-check the prior findings, hunt new ones only in changed code) + aggressive-recall first rounds + opt-in adversarial verify. Handoff hooks/skill deleted.
reviewer agent the primary task reviewer, dispatched per task a single-agent fallback only, used when Workflow is unavailable

What did not change: labels are still the cross-pass protocol for the durable state that remains; every state transition is still recoverable from GitHub + git; the orchestrator still never writes code or diagnoses failures (detect-and-dispatch); and /loop /implement-feature <feature-name> is still the way to ship a feature unattended.

Discovery vs. execution

There are two distinct phases, and they don't share state in memory — they share state through git and GitHub. Discovery produces documents; execution consumes those documents and produces code.

Discovery documents

Run once per feature, before any code lands. The user converses with product-owner until the requirement is locked, then with design-lead until the visual language + surface/navigation inventory are locked, then with architect until the technical decisions are locked. Each interviewer is read-only; writer teammates (doc-writer instances) commit the artifacts. The phase ends with a single PR titled feature-lockin.

  • /deep-dive-feature — PRD + critical path + glossary + design system + surface/nav inventory + per-surface UI interaction contracts + ADRs + implement-detail + per-entity api-contract / data-model files + runbooks
  • /scaffold-project — greenfield only; reads the ADRs and brings up a bootable stack, seeding the locked design tokens

Execution code

Run repeatedly per feature, idempotently, against GitHub-issue state. The orchestrator runs task-finder.sh once per pass — pure shell, no LLM — to find candidates across four outer stages, then processes the report. Stage 1 launches one background implement-slice Workflow per eligible slice; that run owns the whole inner cycle. The other three stages reconcile dead runs and handle the PR. Wrap with /loop for unattended end-to-end shipping.

  • /ship [milestone] drives reconcile → analyze-bug → kickoff → fix-pr → close-pr (/implement-feature is the feature-only four-stage subset)
  • Kickoff launches implement-slice.mjs (feature/enhancement) or fix-bug.mjs (bug), which dispatch e2e-author / engineer and run the inlined review fan-out (axis-reviewer agents)
  • Across units work runs in parallel; within a unit everything is serial inside its one workflow

The agents

Role-based subagents under agents/. Each one is briefed by a command, a workflow, or a workflow skill — never by the user directly. Roles are sharp on purpose: product-owner never writes architecture; design-lead never writes code; engineer never writes E2E specs; reviewer is read-only on production code. In execution the agents are no longer dispatched by the /ship orchestrator directly — they are dispatched by the unit-cycle Workflow (implement-slice.mjs / fix-bug.mjs). The two exceptions the outer loop still owns are the read-only analyze-bug engineer (pre-approval) and the fix-pr engineer (an external-wait stage).

Agent Model Mode Owns
product-owner opus read Interviews the user about the six product axes (user, problem, success criteria, scope-in, non-goals, edge cases). Composes a dispatch prompt for the requirement-writer at the end; never writes files.
design-lead opus read Interviews the user about the visual language and information architecture. Locks the design system + the surface / navigation inventory (every routed surface and how it's reached). Composes a dispatch prompt for the design-writer; ui-ux-pro-max is its toolbox. Answers the designer teammates' requests for the locked interview results and folds the human-voted winner into its payload. Reads PRD; never writes files.
designer sonnet write Sample-page generator, 1–2 named instances after the design interview: designer-pro-max (toolbox ui-ux-pro-max) and designer-taste (toolbox taste-skill) — one per installed toolbox plugin. Each pulls the interview results from design-lead and writes self-contained plain-HTML candidates under docs/design-system/sample-candidates/<name>/; the human votes the winner (solo mode skips the vote and adds a token proposal; with neither plugin the phase is skipped — design-lead's own taste, the pre-existing fallback). Writes uncommitted candidates only; design-writer moves the winner into docs/design-system/samples/ and commits.
architect opus read Interviews the user about technical decisions. Reads the locked surface inventory and models the app shell / nav container as a real C4 component. Composes five scoped dispatch prompts (implement-detail, ADR, api-contract, data-model, runbooks). Reads PRD; never writes files.
doc-writer sonnet write Seven named instances: requirement-writer (Phase 1), design-writer (Phase 1.5), then implement-detail-writer, adr-writer, api-contract-writer, data-model-writer, runbook-writer (Phase 2). Each writer pulls its scoped payload from the matching interviewer via SendMessage, then commits its artifacts.
engineer sonnet write Fullstack implementer. Dispatched by the implement-slice Workflow with a (slice #, task IDs) pair, routed by verb: implement (named backend/frontend checklist tasks), pass-E2E (drive production code to green against a booted stack), fix-slice (review findings), fix-pr (CI / merge conflict). Reads the slice body's ## Tasks checklist as the ledger, resumes from ticked boxes + Task: <id> WIP commits. Strict outside-in TDD. Never writes E2E specs.
e2e-author sonnet write Authors and fixes Playwright E2E specs for a slice's e2e checklist tasks, dispatched by the implement-slice Workflow with a (slice #, task IDs) pair. Sets up its own slice worktree, smoke-runs each touched spec, commits with Task: <id> trailers and pushes, ticks the authored boxes, posts a summary comment.
axis-reviewer sonnet read Single-axis reviewer — applies exactly ONE pattern-reviewer-* catalogue to the slice diff and returns structured findings (no verdict, no comment, no label). The implement-slice fan-out (runReviewSlice()) spawns one per applicable pattern; the workflow owns dedup, adversarial verify, scoring, the verdict, and posting. Runs in full or coverage scope.
reviewer sonnet read Single-context fallback reviewer for one slice — used only when the implement-slice fan-out review is unavailable in the running harness. Collapses the fan-out into one context: applies the same per-axis rules (axis-reviewer) to every applicable pattern-reviewer-* at once on top of the always-on pattern-test-coverage gate, posts one # Slice Review comment, and returns the verdict (it flips no label and opens no PR — the calling workflow owns those). The primary path is the runReviewSlice() fan-out (below).
sre sonnet write CI/CD owner (GitHub Actions, OIDC, environments, image promotion). Defined but not yet auto-dispatched by any workflow skill — invoke manually for now.

Discovery — /deep-dive-feature

Three sequential phases (product → design → architecture). The orchestrator grows the team one teammate at a time: it does not pre-create writers until the interviewer they answer is ready to hand off a dispatch payload.

STEP 1–4

Phase 1: product

product-owner + user
  • Orchestrator spins up a team with one teammate
  • User talks to product-owner directly
  • User "locks requirements" explicitly
  • Product owner composes a scoped dispatch prompt, names the kebab-case feature
STEP 5–6

Phase 1 publish

orchestrator + requirement-writer
  • Orchestrator creates a milestone and a worktree-backed branch docs/<feature-name>
  • Invites requirement-writer (subagent_type = doc-writer)
  • Writer pulls payload from product-owner via SendMessage
  • Writer commits PRD, critical-path, glossary updates
STEP 6A–6D

Phase 1.5: design

design-lead + user + 1–2 designers + design-writer
  • Orchestrator invites design-lead after the requirement is published
  • User talks to design-lead directly
  • Locks the visual language + the surface / navigation inventory + a per-surface UI interaction contract skeleton (the semantic interface E2E specs drive against)
  • Sample-page duel: 1–2 designer teammates (toolboxes ui-ux-pro-max / taste-skill, one per installed plugin) generate plain-HTML candidates; the human votes the winner (no plugins → skip, design-lead's own taste)
  • design-writer commits docs/design-system/{overview,tokens,components,accessibility}.md + surfaces.md + docs/ui-contract/<screen>.yaml + the winning samples into docs/design-system/samples/
STEP 7–9

Phase 2: architecture

architect + user
  • Orchestrator invites architect
  • User talks to architect directly
  • User "locks decisions" explicitly
  • Architect composes five scoped dispatch prompts
STEP 10

Phase 2 publish

orchestrator + 5 writers
  • Orchestrator invites five doc-writer instances
  • Each writer pulls its scope-appropriate payload from architect
  • Implement-detail, ADRs, api-contracts, data-models, runbooks are committed
STEP 11

Lock in

orchestrator
  • Push the docs/<feature-name> branch
  • Open a single PR labeled feature-lockin
  • User reviews and merges manually — discovery is done
GUARDRAILS

What the orchestrator does not do

policy
  • Never answers product questions itself
  • Never answers for the user — interviewer questions reach the human verbatim
  • Never skips a lock gate
  • Never invites writers ahead of their interviewer

Scaffold — /scaffold-project

Optional, runs once on a greenfield project (the skill aborts if any of backend/, frontend/, a compose file, or e2e/ already exists). Reads stack and topology decisions from docs/architecture-decision-record/ and materializes a bootable skeleton from templates. Ends in its own PR.

  1. Confirm greenfield — all four surfaces (backend, frontend, compose, e2e) must be missing.
  2. Read the ADRs to pick backend / frontend stack variants and the compose service list; assert the ADR-published docs/stack.yaml manifest exists (the adr writer publishes it at lock-in — scaffold distills it only as a fallback for older lock-ins). A declared stack without a template takes the skeleton path (layout + manifests + ci-checks.sh pinned; framework entry owned by the first engineer slice).
  3. Create chore/scaffold-project.
  4. Materialize backend → commit. Materialize frontend → commit. Materialize docker-compose.yaml → commit.
  5. Boot check: docker compose up -d --build, probe each framework-metadata endpoint, then docker compose down. Stops on failure — does not mutate templates to mask a broken boot.
  6. Materialize Playwright e2e → npm install → commit.
  7. Materialize the CI pipeline + CI-parity pre-push hook + gitleaks pre-commit hook → commit.
  8. Assert the design system exists: docs/design-system/tokens.md and surfaces.md must be present (locked upstream by design-lead during /deep-dive-feature). Fails loudly if absent — scaffold consumes the design system, it does not generate it.
  9. Translate the locked tokens.md into frontend/src/styles/tokens.css and import it from the frontend entry → commit. (The ## Design taste section of CLAUDE.md is already authored by design-writer — scaffold doesn't re-write it.)
  10. Push and open the PR.

Execution — /ship [milestone]

One sweep through the outer stages, in order. The orchestrator runs skills/operation-git/scripts/ship-finder.sh [milestone] once — pure shell, no LLM — to discover every eligible candidate against ONE GitHub-state snapshot, then walks the report top-to-bottom. /ship has five stages — reconcileanalyze-bugkickofffix-prclose-pr — spanning all three kinds. Milestone is optional: pass one to scope to a feature, omit it for the repo-wide maintenance lane (all open bugs + one-off enhancements). Each stage is self-skipping (its report section reads - (none) when there's nothing to do). One invocation = one pass — wrap with /loop /ship [milestone] to keep advancing until the work ships.

/implement-feature <feature-name> is the feature-only fallback: the same flow minus the analyze-bug stage (four stages, always milestone-scoped), driven by task-finder.sh.

The command's whole job is: reconcile dead-run locks, dispatch the read-only analyze-bug engineer for freshly-filed bugs (→ # Bug Analysis comment → human approval gate), launch a unit's Workflow at kickoff (routed by kind:implement-slice.mjs for feature/enhancement, fix-bug.mjs for bug), and shepherd the resulting PR. The inner unit cycle — author E2E, coverage gate, implement, pass E2E, review, fix (or, for a bug, regression-test-first → fix → review) — is never the command's concern; it runs entirely inside the Workflow that kickoff launches (next section). The orchestrator never waits for backgrounded work within a pass: once it launches a workflow or dispatches an engineer, it ends the pass; a finished run re-invokes it (event-driven) and the next pass picks up what moved.

The four outer stages

0

reconcile

lock release
  • Runs first; launches nothing
  • Finds orphaned locks: a slice status:in-progress whose implement-slice Workflow died, or a draft PR status:fix-in-progress whose fix-pr engineer died
  • Gated by a runtime-telemetry liveness heartbeat (a fresh last_seen vetoes the reap) with GitHub-activity staleness as fallback
  • Residual gap: the default-typed workflow agents (review-prep, plan, the 3-lens verify batches, publish) emit no heartbeat, so a long verify phase is heartbeat-quiet — but it's bracketed by axis-reviewer activity and stays inside the ~30-min threshold, so it won't false-reap in practice
  • Releases the lock so a fresh run relaunches next pass from durable state (WIP commits + checklist + resume journal)
1

kickoff-slice

lock + launch
  • Picks kind:feature + status:ready-to-implement slices with zero open Blocked by and not already locked
  • Flips status:ready-to-implementstatus:in-progress (the lock that hides the slice from the next pass)
  • Launches implement-slice.mjs in the background, one per slice, via the Workflow tool
  • That run owns the entire inner cycle; the orchestrator moves on
8

fix-pr

dispatch
  • External-wait stage: picks draft PRs with failing CI and/or merge conflicts (no in-flight lock)
  • Locks with status:fix-in-progress
  • Dispatches the engineer (fix-pr verb) — the only agent the outer loop dispatches directly
  • Agent resolves conflict / fixes CI, pushes, strips the lock
9

close-pr

merge
  • Processed sequentially (concurrent merges race the base branch)
  • Re-checks live mergeable + check rollup as a go/no-go gate
  • Promotes every mergeable draft → ready; squash-merges + deletes branch only when merge:auto
  • GitHub auto-closes the slice via the PR body's Closes #<slice-#> line; merge:manual PRs are left for the user

Stage numbers are non-contiguous (0, 1, 8, 9) on purpose — they preserve the identity of the surviving stages from the old nine-stage scheme. Stages 2–7 (implement-task, review-task, fix-task, prepare-slice, review-slice, fix-slice) were retired: their work now lives inside the slice workflow.

Keep advancing

LOOP

Event-driven, with a backstop

recommended
  • /loop /implement-feature <feature-name>
  • A finished implement-slice / fix-pr run re-invokes the orchestrator (fast path); its tracking task is closed by owner at pass entry
  • A long ScheduleWakeup (≈1800s) is the backstop for dead runs, merge-driven Blocked by cascades, and lost sessions — not a poll
  • Stops only on quiescence: a pass that dispatches nothing AND has zero open tracking tasks
HALT

status:need-attention

user-owned
  • The slice workflow flips this when it can't proceed without a human (e.g. an E2E spec needs rewriting, or the inlined review can't set up its worktree / can't post its verdict)
  • The orchestrator never recovers it — never asks, never blocks, never flips it back
  • task-finder.sh drops it from every stage
  • The user comments, then flips it back to status:ready-to-implement so kickoff relaunches a fresh run

The slice runs as a workflow

The heart of v0.40. The slice lifecycle is exactly two workflow layers (Workflow nesting is one level deep — a child calling workflow() throws). A workflow script spawns every agent() as a peer in one flat pool, expressing fan-out the two-level Agent tree can't. Both scripts ship with the plugin under workflows/ and are invoked by scriptPath against ${CLAUDE_PLUGIN_ROOT}.

/loop /implement-feature <feature>        (outer driver: reconcile / kickoff / fix-pr / close-pr)
  └─ Stage 1 LAUNCHES (background, one per slice):
       implement-slice.mjs                ← TOP: owns author → implement → review → fix → PR
          ├─ agent()  e2e-author / engineer          (generative, serial — shared worktree)
          └─ runReviewSlice(…)  ← inlined fan-out: coverage gate + gate review + quality review (one axis-reviewer agent per pattern)
                └─ agent() ×N             (one per pattern-reviewer dimension + adversarial verify)

The split rule: assessment work that is parallel-decomposable → a fan-out child workflow; generative work that is sequential → a single agent. Only the reviewer stages qualify as workflows — the author / implement / pass-E2E / fix stages are TDD chains on one shared slice worktree, so they can't parallelize and stay single agent() dispatches.

implement-slice.mjs — the per-slice cycle (TOP)

One background run per slice. It owns the entire inner cycle that the old nine-stage /implement-feature used to round-trip through GitHub labels. GitHub keeps only durable, human-relevant state: the slice issue, the status:in-progress lock (held for the whole run), the status:need-attention halt, and the final draft PR. Task tracking lives in the slice body's ## Tasks static-ID checklist — Prep parses it, and each dispatched agent ticks its boxes as it finishes.

Phase Realization What it does
Prep agent() Read the slice body, parse the checklist (the resume source), resolve branch + PR metadata.
Author E2E agent · e2e-author One dispatch for every not-yet-[x] e2e task. Skipped if the slice has no e2e tasks — and a slice gets one only when it closes a cross-surface journey segment, so backend-only / pure-layout slices skip this phase entirely.
Coverage gate runReviewSlice('test-coverage') New in 0.40. Static review of the authored specs vs the slice AC + non-happy-paths, before any production code. Loops to an e2e-author fix until covered (no round cap). On resume it is skipped only when the newest # E2E Coverage Gate comment says APPROVE — ticked e2e boxes prove authoring, not passage, so a run killed on a BLOCK re-gates.
Plan agent() Group impl tasks into ordered engineer dispatches (DAG-respecting, ≤3 tasks/group — the size cap that replaces the deleted budget gate's "bound the context" role).
Implement agent · engineer Groups run serially (shared worktree). Done groups skipped (resume).
Pass E2E agent · engineer (diagnose → fix loop) Each round: one diagnose dispatch boots the stack, runs the specs, and categorizes failures into correlated groups; then one fix dispatch per group runs serially (shared worktree). Loops (uncapped) until a round diagnoses GREEN. A test-case constraint → halt().
Gate review runReviewSlice('production-code', …, 'gate') The fan-out over the gating dimensions only (spec-compliance / contract / security), looping to an engineer fix-slice until APPROVE (no round cap). Judges each task at its owning layer (a backend invariant is proven at the backend layer, never demanded through E2E) via a per-task discharge ledger; on APPROVE the workflow ticks every AC checkbox — the reviewer-gated verified gate (the engineer's task-box tick is only a progress claim).
Quality review runReviewSlice('production-code', …, 'quality') The fan-out over the code-quality axes only (never blocking). Bounded, not a loop: exactly one review/fix cycle (review → one engineer polish pass over the Defer/Nit findings) plus one final re-review, whose residual debt is triaged into kind:refactor / kind:enhancement tracking issues at status:ready-to-review.
PR agent() Open the idempotent merge:auto draft PR (Closes #<slice>) and release the slice lock.

Gating fix loops are uncapped — coverage gate, implement re-dispatch, and the gate review each loop until they reach confidence to pass (review APPROVE, or every task ticked [x]). The quality review is the exception — bounded to one fix cycle + one re-review, never a loop (code quality never blocks, so its residual is triaged into tracking issues rather than chased to zero). Re-review rounds are anchored: each gets the prior round's findings + the exact sha that round judged, closure-checks them (same title/file, no upward re-grade of unchanged code), and hunts new findings only in the code changed since — convergence instead of an independent re-sample of the whole diff per round. A gating I:M is always classed Fix (never Defer) and the fix dispatch inlines the Fix-class findings, so a skipped gating MEDIUM can't flap into a later-round blocker. The fan-out's full adversarial verify is opt-in (verifyLenses arg, default OFF) — when on, only a finding that survives refutation holds a gate open; when off the dimension reviewer's own severity stands for non-blocking findings, but each newly-reported verdict-driving blocker still faces an always-on 2-lens (correctness + context) floor — neutralised only when both lenses refute it on concrete evidence (coverage gap dropped; gate blocker downgraded to MEDIUM, still fixed via the gating-I:MFix class). Either way the gate review blocks only on a gating-dimension I:H — spec-compliance, contract, or security — while code-quality findings are deferred debt handled by the separate quality review and never block the slice. halt() flips status:in-progressstatus:need-attention and posts a comment — the only path to a human; the outer loop never recovers it. Two progress guards bound the uncapped loops: the oscillation stall (the same blocker surviving its own targeted fix for 3 consecutive rounds) and the churn guard (3 consecutive rounds each surfacing a new blocker on code unchanged since the prior round — reviewer noise, not defects). Resume is the slice branch's WIP commits (each carrying Task: <id> + Refs #<slice>) + the durable checklist + the Workflow resume journal — plus the verdict comments for review passage: every gate/review comment is stamped with the reviewed branch-tip SHA (**Reviewed tip:**) and an invisible <!-- resume-state --> marker carrying the loop-guard streaks. Each looping review (coverage gate, gate review) runs a one-shot read-only reviewEntryAction probe before its first review (the gate review's probe is hoisted to the Pass E2E phase): a durable coverage-gate APPROVE skips the gate; a gate APPROVE still at the remote tip skips Pass E2E + Gate review (straight to the idempotent AC-tick + PR, and the quality pass too when its comment already exists at that tip); a standing BLOCK verdict that nothing landed against — no commit and no fix-summary comment after it — returns fix-first, dispatching the fix straight away instead of re-running the fan-out only to reproduce the same BLOCK (a fix that did land, even partially, falls through to review, which catches the rest); and any BLOCK re-seeds the oscillation + churn streaks from its resume-state marker instead of resetting them. There are no handoff docs.

runReviewSlice() — inlined fan-out review

A function inside implement-slice.mjs (was a separate review-slice.mjs child workflow before this change), called for the pre-implementation coverage gate (scope:'test-coverage') and the two post-implementation review stages (scope:'production-code', switched by reviewMode: 'gate' runs the gating dimensions, 'quality' runs the code-quality axes). It isolates each review dimension onto its own axis-reviewer agent and — only when the verifyLenses arg is on (default OFF) — adversarially verifies every finding before it can block: verification batches same-dimension findings (≤10 per agent) through three independent skeptic lenses, so dispatch scales with dimensions × chunks rather than one agent per finding while the cross-lens majority vote stays intact. With verify off (the default) findings are trusted as the reviewer graded them and this pass is skipped — it is itself a self-review, so it is opt-in.

The new boundary (changed in 0.40): it posts the verdict comment and RETURNS the verdict object — it flips no label and opens no PR. The parent implement-slice owns the lock and the terminal draft PR. Previously, when the outer /loop called it directly at Stage 6, it flipped review:runningreview:passed/need-fix and opened the PR itself; that boundary moved up into the parent.

 gate mode    : Prep ─► Gating dims (fan-out) ──── dedup ─ verify ─► compose ─► Publish   (BLOCK on gating I:H)
 quality mode : Prep ─► Quality dims (fan-out) ─── dedup ─ verify ─► compose ─► Publish   (ADVISORY, never blocks)
(worktree/diff)  gate: ≤3 gating pattern-reviewer-* dims · quality: the debt dims the touched surfaces select (incl. per-language lenses)
                 each fan-out + 3-lens refutation (opt-in)                                 (code)    (1 agent)

Each pass fans out, dedups, then verifies before composing — so the verdict decides on a confirmed finding, never a raw one. Dedup and scoring (severity → Impact, (Impact, Effort) → Fix/Defer/Nit/Drop, gate verdict = BLOCK iff a gating-dimension (spec/contract/security) I:H survives; quality verdict = always ADVISORY — every code-quality finding is deferred debt) are plain deterministic JS, not LLM steps. Each dimension agent reads exactly one pattern-reviewer-* skill and applies that skill's per-project .claude/memory/patterns/<skill>.md overlay — so dreamed rules reach the fan-out the same way they reach the single-agent fallback.

Scope coverage runs only the Spec-phase test-coverage dimension over the authored E2E specs, pre-implementation: the usual "test files are out of scope" rule inverts (the specs are the deliverable), the verdict is BLOCK on any confirmed coverage gap (not just I:H), and Quality is skipped entirely.

Quality review — one fix cycle, one re-review, then debt triage. Because code-quality findings never block, the slice doesn't churn on them: the quality review is a separate, bounded pass that runs only after the gate review APPROVES. It does exactly one review/fix cycle — review the code-quality axes, then a single engineer polish pass over the non-gating (Defer / Nit) findings — plus one final re-review (it is not a loop). Whatever debt remains after that pass is then triaged into tracking issues — one per review dimension, routed by kind: non-functional findings → kind:enhancement (they add observable behavior, so they earn a feature-shaped body with ACs + an e2e task and the full E2E/integration treatment); every other dimension → kind:refactor (behavior-preserving — a no-e2e ## Tasks checklist, so implement-slice skips all E2E machinery and the only new tests are unit tests for extracted seams). Both are created at status:ready-to-review (the human gate) and deduped against open issues of that kind, so the quality debt is recorded for the /ship maintenance lane instead of holding the slice. fix-bug.mjs mirrors the same two-stage split via its own runReview('gate') / runReview('quality') — gate fix loop, then one bounded quality cycle + triage — minus the AC-tick (a bug has no AC ledger; the regression test is its spec-compliance gate).

Two model tiers

The phases split across two models by the kind of work each does — judgment stays on Sonnet, mechanical work drops to Haiku. Tunable in one place via AGENT_MODEL / WRITER_MODEL at the top of the script.

Phase Model Why this tier
Prep haiku Read-only worktree, diff vs origin/main. Pure tool-orchestration — carries no review judgment.
Prep · surfaces sonnet Turns the raw touched paths into the surface flags that decide which dimensions run (full scope only). The one judgment call in prep: a misclassified path silently drops a whole review dimension, so it keeps the stronger model (and is biased toward true).
Spec / Quality / Verify sonnet The pattern-reviewer-* dimension fan-out plus the 3-lens adversarial refutation — the judgment-bearing review work. Pinned to match the single reviewer fallback agent (model: sonnet).
Publish haiku Writes the comment, runs post-comment.sh, returns the verdict. A pure executor — the only write in the whole workflow.

Labels are the protocol

Agents don't share memory. They share GitHub state. The cross-pass protocol is a small set of labels; the within-slice task ledger is the slice body's checklist. That's why the lifecycle survives crashes, context limits, and user interruptions — the next pass just reads the state and re-derives where work is.

v0.40 deleted four whole label families. The inner slice cycle no longer round-trips through labels — it runs inside one workflow — so the labels that drove each inner step are gone: level:* type:* review:* e2e:*. level:* is moot (one issue per slice now), type:* lives in the checklist's per-task type, and review:* / e2e:* became in-memory state inside the workflow.

kind:*

  • kind:feature — per-slice-Workflow lifecycle (implement-slice.mjs)
  • kind:enhancement — one feature-shaped issue, same implement-slice.mjs cycle, no interview
  • kind:refactor — behavior-preserving debt auto-filed by review triage; implement-slice.mjs minus E2E (no-e2e task body)
  • kind:bug/ship analyze → human gate → fix-bug.mjs

status:*

  • status:ready-to-review — human approval gate (a new slice/enhancement, or a bug's posted # Bug Analysis)
  • status:ready-to-implement — kickoff picks this up
  • status:in-progress — the unit lock (slice, enhancement, analyze, or bug-fix); held for the whole run
  • status:fix-in-progress — PR-level lock (fix-pr)
  • status:need-attention — workflow halted; the human owns the next move

merge:* / feature-lockin

  • merge:auto / merge:manual — close-pr behavior (workflow opens drafts merge:auto)
  • feature-lockin — the discovery PR from /deep-dive-feature

The task checklist (not a label)

  • The slice body's ## Tasks section is the durable task ledger
  • Static IDs (e2e.1, be.1, fe.2) are permanent — never translated to issue numbers
  • [ ][x] as each agent finishes; this is the resume source
  • Commit trailers carry Task: s<slice#>.<id> + Refs #<slice#>

How labels flip

Two of the three lanes below are state machines over GitHub labels (the slice issue and the draft PR). The middle lane is different: the inner cycle that used to be a label machine is now a phase progression inside one workflow run — no labels flip between its phases. Read top to bottom for the happy path; the orange call-outs mark the failure loops. The pill on each arrow is the actor that performs the step.

skill orchestrator step — a /implement-feature stage or a workflow skill (e.g. create-feature-issues) agent the implement-slice Workflow or a dispatched sub-agent human the user github GitHub automation (PR merge auto-closes linked issues)

Slice issue (labels)

(issue does not exist)
create-feature-issues opens ONE issue (inline task checklist in body) + attaches slice branch
kind:feature · status:ready-to-review
human approves the slice scope
status:ready-to-review → status:ready-to-implement
/implement-feature (stage 1) skips if any open Blocked by; otherwise locks + LAUNCHES the workflow
status:ready-to-implement → status:in-progress
implement-slice workflow runs the whole inner cycle (middle lane) — no labels flip during it
cycle SUCCEEDS
status:in-progress removed + draft PR opened
cycle HALTS
status:in-progress → status:need-attention
github on PR merge: Closes #<slice-#> body closes this issue
issue closed (slice shipped)
halt recovery: status:need-attention is a user-owned halt. The orchestrator never touches it; the human comments, fixes the blocker (e.g. edits an E2E spec), then flips status:need-attentionstatus:ready-to-implement so kickoff relaunches a fresh run from the checklist + WIP commits.

Inside the workflow (phases)

launched by stage 1 (slice already locked)
Prep parse the slice body's ## Tasks checklist (the resume source)
Author E2E — e2e-author per e2e task
Coverage gate runReviewSlice('test-coverage') over the authored specs; anchored fix loop to APPROVE (uncapped)
Plan — group impl tasks (≤3 / group, DAG)
Implement engineer per group, serial (shared worktree); done groups skipped
Pass E2E — engineer diagnoses failures, then per-group engineer fixes drive specs to GREEN (loop)
Gate review runReviewSlice('production-code', …, 'gate') over spec/contract/security; anchored fix loop to APPROVE (uncapped); on APPROVE tick the ACs
Quality review — runReviewSlice(…, 'quality') over the code-quality axes; one fix cycle + one re-review (bounded); residual triaged into refactor/enhancement issues
APPROVE
PR phase → open draft PR
can't proceed
halt() → status:need-attention
PR idempotent merge:auto draft (Closes #<slice>); release the lock
draft PR open · lock released · run ends
resume: a SIGKILL leaves the lock set but the run gone. Stage 0 reconcile releases it; the next pass relaunches a fresh workflow that re-reads the checklist (ticked boxes skipped), probes the newest SHA-stamped verdict comments (a passed gate is skipped; an APPROVE at the current tip jumps to AC-tick + PR; a BLOCK re-seeds the stall streaks), and replays its agent() prefix from the resume journal — no handoff docs.

Draft slice PR (labels)

(PR does not exist)
implement-slice (PR phase) opens draft PR on cycle success; body contains Closes #<slice-#>
draft · merge:auto
human optional: opt out of auto-merge
merge:auto → merge:manual
github CI runs on the slice branch
GREEN + MERGEABLE
ready to close
RED or CONFLICT
CI fail / merge conflict
/implement-feature (stage 8) locks the red PR and dispatches engineer (fix-pr)
+ status:fix-in-progress
engineer (fix-pr) resolves conflict / fixes CI; strips lock on success
status:fix-in-progress → (removed)
/implement-feature (stage 9) scope: green + MERGEABLE → ready; squash-merge only if merge:auto
draft → ready → squash-merged + branch deleted
bail path: when the CI failure points at an E2E-spec rewrite, the engineer flips the PR to status:need-attention instead of guessing. The PR stays parked until the human edits the spec and removes the label.

Two cross-cutting rules emerge from the diagrams. (1) The status:in-progress label is the slice lock — held for the entire workflow run, it hides the slice from the next kickoff pass and is what the reconcile reaper releases when a run dies. (2) The inner cycle no longer flips labels — review verdicts, E2E validation, and fix loops are in-memory phase transitions inside one run; only the four durable transitions (lock, halt, draft PR, merge) touch GitHub.

Who talks to whom

Two distinct channels: a Team-shared channel for the interactive discovery agents (product-owner, design-lead, designer and doc-writer instances, architect) and a workflow dispatch channel for the lifecycle agents (engineer, e2e-author, axis-reviewer agents) — almost all of which are now dispatched by the implement-slice Workflow, not the command directly.

Team mode

discovery (deep-dive-feature)

TeamCreate spins up named teammates that can SendMessage each other. The orchestrator grows the team one teammate at a time. Writers pull dispatch payloads from interviewers via SendMessage — the orchestrator never forwards artifacts inline.

  • product-owner ↔ user (direct)
  • product-ownerrequirement-writer (pull payload)
  • design-lead ↔ user (direct)
  • design-leaddesigner-pro-max / designer-taste (pull interview results; user votes the winner)
  • design-leaddesign-writer (pull payload)
  • architect ↔ user (direct)
  • architect ↔ 5 writers (pull scoped payloads)

Dispatch mode

execution (4-stage outer loop + the slice workflow)

/implement-feature runs task-finder.sh once per pass, then at Stage 1 launches one implement-slice Workflow per slice. That workflow dispatches every inner-cycle agent as a background peer; the outer loop dispatches only the fix-pr engineer. Agents run to completion, commit, push, tick their checklist boxes. No shared memory.

  • Skill → implement-slice Workflow (one per slice)
  • Workflow → e2e-author / engineer (per task group)
  • Workflow → runReviewSlice() → N axis-reviewer agents
  • Skill → engineer fix-pr (the lone outer-loop dispatch)

Hook mode

engineer worktree pushes

engineer-pre-push.sh fires as a PreToolUse hook on every Bash call. It no-ops unless the command contains git push in an engineer worktree. On a real push it runs the lint / type / security / test gate for the relevant stack and denies the push on failure.

  • Hook → engineer (deny / allow)
  • Engineer reads the denial summary, fixes, retries the push

SendMessage handshake — Phase 2 example

            ┌──────────────┐               ┌────────────────────┐
            │    USER      │               │   orchestrator     │
            └──────┬───────┘               │ (/deep-dive)       │
                   │                       └─────────┬──────────┘
                   │   talks directly to             │ invites + briefs
                   │                                 │
                   ▼                                 ▼
            ┌──────────────┐    SendMessage    ┌────────────────┐
            │  architect   │ ◄──── pull ───────│ adr-writer     │
            │  (read-only) │ ─── payload ────► │ (subagent_type │
            │              │                   │  = doc-writer) │
            └──────────────┘                   └────────────────┘
                   ▲                                  │
                   │  also answers:                   │ commits ADRs
                   │  • implement-detail-writer       │ on the feature
                   │  • api-contract-writer           │ branch
                   │  • data-model-writer             │
                   │  • runbook-writer                │

Where the user sits

The user is in the loop in three specific places — and intentionally out of the loop everywhere else.

MomentWhyWhat the user does
discovery interview Only the user knows what they want to build. product-owner, design-lead, and architect talk to the user directly — the orchestrator does not interpose, paraphrase, or guess. Answers questions. Says "lock requirements" / "lock the design" / "lock decisions" when ready.
PR review Every workflow ends with a PR. Discovery PRs (feature-lockin) and scaffold PRs require a human merge. Slice PRs default to merge:auto (the implement-slice workflow's default), so close-pr lands them once green + MERGEABLE. Reviews and requests changes via comments before CI goes green — or relabels to merge:manual to hold the PR open and merge it by hand.
status:need-attention The implement-slice workflow calls halt() rather than guess when an E2E spec rewrite is needed, scope is ambiguous, or an infra step fails (the inlined review can't set up its worktree, or a verdict can't be posted). Fix loops themselves are uncapped — they loop to confidence-to-pass rather than halting. The lifecycle stops on that slice / PR until the human clears the label. Edits the spec / clarifies scope / removes the label so the next pass picks the work up.

Everywhere else — task dispatch, code review, security review, fix loops, draft PR creation, merge of merge:auto PRs — the user can step away. The loop reads labels, dispatches one-shot agents, and advances. /loop /implement-feature <feature-name> keeps it going until the feature is shipped or until something hits status:need-attention.