witness-plan — spec delta → step plan → gate
Ground rules (every witness skill)
Resolve the CLI once per session:
WITNESS="${WITNESS_BIN:-npx -y @popovych.co/witness@0.14.4}"
- Render the CLI's decision output verbatim and in full — every line, unmodified. Never print a command set you remember; never recompose, reformat, summarise or reorder what the CLI emitted. Which decisions are live, how they rank, and what each costs are the CLI's answers, and they change with the round, the bound, the repair grant and the content sha — a remembered set is wrong in more states than it is right.
- The human decides; you may type it. Run a
witness decide verb when the human names an option — its number or its verb — or gives a bare affirmation ("y", "ok", "go") while a CLI-rendered decision block carrying a recommended option is on screen: the affirmation selects the recommended option, and you append --via affirmation to the printed command. Otherwise the string is run byte-for-byte: never recomposed, never reformatted, never with a placeholder you resolved yourself. The moment you compose a --note or resolve an id, you are authoring their decision. A nod never takes --approve --override, --stop, a trust grant, or witness abandon — those require naming, and the CLI refuses them with nod-cannot. A block with no recommendation also requires naming. Questions you authored yourself (the brainstorm interview, the design converge step) are conversation, not a block: an affirmation there accepts your stated recommendation, with no selection entry, no journal claim and no flag. A selection does not survive session death: killed and re-run, render the block again and ask again.
- Never edit
specs/**, plans/**, or designs/** (the canon dirs — paths: in witness.config.yaml may relocate them) — not with an edit tool, not with a write tool, not with Bash redirection. The CLI is the sole writer of state; you author in scratch files under $(mktemp -d) and hand them to the CLI. (The canon guard blocks you; the trailer audit catches what it can't.)
- Read canon with
witness read <id>, never by path. Canon lives at the primary root; inside a worktree the files are absent by design, so a path read finds nothing and a stale copy cannot be mistaken for the contract. Fat artifacts: witness read <spec-id> --design --outline, then --lines <a>-<b>.
- Read a file before your first edit of it in this session. Relay boundaries,
verify-red's stash cycle, and worktree re-attach all change files under you — an edit against a remembered copy is how "modified since read" and partial applies happen. The CLI now prints stale-reads: when it churns the tree; treat that list as unread.
- Never invoke gate reviewers or relay verdicts.
witness gate runs reviewers itself and journals what they said; your summary of a verdict is not evidence.
- Refusal repair loop: a
witness verb exiting 2 prints structured violations (field · rule · got · want). Fix your input and retry — 3 total attempts per artifact, then stop, show the human the violation list verbatim, and end your turn.
- A refused or hook-blocked command is a stop, not a step to drop. Re-issue it on its own; if it still refuses, tell the human what was blocked and why. Never proceed by deleting the refused half of a compound command — a dropped step is silent, and silence is how a skipped check becomes a shipped defect.
- Re-entrancy: derive position from CLI output (
$WITNESS next, the dashboard, log, index) — never from conversation memory. Killed and re-run, you must converge.
Inputs (rebuild them, never remember them)
Everything below is rebuilt from witness diff and the CLI's read verbs — never from conversation memory, never by path.
$WITNESS diff <spec-id> # the delta this plan must realize (base: previous plan's pin → last live → empty)
$WITNESS read <spec-id> # the parent spec, current content (reading is fine — writing is not)
$WITNESS index # the plans table names this spec's prior plans, if any
$WITNESS read <plan-id> # …then read the one you care about
$WITNESS decide plan <plan-id> --show # ONLY when re-entered after a revise
Effort slug (write needs --effort): take it from the $WITNESS next line that routed you here — next resolves it to a live effort that wrote this plan or its parent, so the slug in that command is the answer. Deriving your own instead risks booking the write onto an abandoned stream. If you arrived without that line: one active effort → use it; several → $WITNESS log <slug> per candidate, and the effort whose write entries name the parent spec owns this plan; still ambiguous → ask the human. If next asks for a recap instead of a write, no live effort can carry this plan — that recap is the owed work, not the plan.
Plan id: <spec-id>-plan-<n> — n = 1 + the highest existing n in plans/ for this spec (a spec accumulates plans over its life; expand-contract amends it twice in one effort).
Author the plan
Every criterion in the delta must be realized by ≥ 1 step; every step maps to ≥ 1 criterion or is honestly scaffolding: true (rigging only — fixtures, wiring, config; never behavior a criterion owns). derives-from is stamped by the CLI from the parent's current content — never put it in the manifest; a supplied stale pin refuses.
If the parent spec is ui-flagged, its design must already be approved (the design stage runs between decompose and plan). Read it with $WITNESS read <spec-id> --design (--outline, then --lines <a>-<b>, when it is fat) — your steps derive from that approved look, not a fresh invention — and put its approved artifact sha in the manifest as "design-from" (the CLI refuses a plan whose pin is missing, stale, or present on a non-ui parent; get it from the spec's design.sha stamp via $WITNESS log <spec-id>). A UI step names the design section (design#<id>) it realizes alongside its @spec: browser test.
DIR=$(mktemp -d)
cat > "$DIR/meta.json" <<'EOF'
{
"type": "plan",
"parent": "auth-refresh",
"depends": [],
"needs": [],
"steps": [
{ "id": "s1", "title": "rotate tokens on refresh", "criteria": ["ac-rotate"] },
{ "id": "s2", "title": "smoke wiring", "scaffolding": true }
]
}
EOF
cat > "$DIR/body.md" <<'EOF'
## Step: s1
Test-first, bite-sized, checkable: name the exact files to create/modify, the
failing test to write first (its name carries `@spec:auth-refresh`), the
minimal implementation, and the observable end state that proves ac-rotate.
## Step: s2
What rigging this sets up and why no criterion owns it.
EOF
$WITNESS write auth-refresh-plan-1 --effort <effort> --meta "$DIR/meta.json" --body "$DIR/body.md"
Body discipline (write-validated: exactly one ## Step: <id> section per manifest step, none missing, none orphaned):
- Each step section is executable by a fresh session with zero context: exact paths, the test to write first, expected red, minimal code, expected green.
- A step realizing browser-visible behavior (markup, styles, routes, client-side interaction) names an end-to-end Puppeteer test as its test-to-write-first — the browser drives the slice's real backend and store, faking only third-party boundaries the repo doesn't own. Browser-level e2e TDD is the implement contract; the implement gate's pr-test lens treats a unit test standing in for the browser — or a browser test stubbing the slice's own backend — as a coverage gap.
- Steps ordered so nothing presumes an artifact a later step creates.
- Chore-class plans choose their own parent here — a chore never reaches the decompose stage, so no earlier stage picked one for you. Take the spec whose implementation area the chore touches; take
parent: principles when the chore is repo-wide. Either way the parent must be approved/live or the write refuses. Report the choice so the gate stop shows what you routed to.
Gate
$WITNESS gate plan <plan-id> # append --manual when the run asked for it
- Auto-pass → done; hand back to /witness.
- Stop → render the gate output verbatim and in full, including its ranked options and
run: line, and END YOUR TURN.
- Re-entered after
--revise → decide --show gives the verdict + note (findings anchor to <plan-id> > ## Step: <id>); rewrite via witness write with the same plan id; re-gate. A parent amended mid-flight fails pin-fresh — rewriting through witness write re-stamps the pin to current content; your body must then realize the new delta ($WITNESS diff again). --show also emits state: and exits: — a reopened or settled state means the verdict above it is history, so act on the exits: line, not on remembered findings.
- Findings implicate the spec (plan faithful, spec wrong)? Tell the human
--revise --upstream <spec-id> reopens decompose for it.
1---2name: witness-plan3description: Derive an implementation plan from a witness spec's delta — step manifest mapping criteria, write-validated4---56# witness-plan — spec delta → step plan → gate78<!-- Derived (MIT): plan discipline from obra/superpowers writing-plans9(exact files, bite-sized verifiable steps). Rewritten for witness: the plan10derives from a CLI-computed delta and its steps are schema'd against the11parent's criteria. See NOTICE.md. -->1213## Ground rules (every witness skill)1415Resolve the CLI once per session:1617```bash18WITNESS="${WITNESS_BIN:-npx -y @popovych.co/witness@0.14.4}"19```2021- **Render the CLI's decision output verbatim and in full — every line, unmodified.** Never print a command set you remember; never recompose, reformat, summarise or reorder what the CLI emitted. Which decisions are live, how they rank, and what each costs are the CLI's answers, and they change with the round, the bound, the repair grant and the content sha — a remembered set is wrong in more states than it is right.22- **The human decides; you may type it.** Run a `witness decide` verb when the human **names an option** — its number or its verb — or gives a bare affirmation ("y", "ok", "go") while a **CLI-rendered decision block carrying a recommended option** is on screen: the affirmation **selects the recommended option**, and you append `--via affirmation` to the printed command. Otherwise the string is run **byte-for-byte**: never recomposed, never reformatted, never with a placeholder you resolved yourself. The moment you compose a `--note` or resolve an id, you are authoring their decision. **A nod never takes** `--approve --override`, `--stop`, a trust grant, or `witness abandon` — those require naming, and the CLI refuses them with `nod-cannot`. A block with **no** recommendation also requires naming. **Questions you authored yourself** (the brainstorm interview, the design converge step) are conversation, not a block: an affirmation there accepts your stated recommendation, with no selection entry, no journal claim and no flag. A selection does not survive session death: killed and re-run, render the block again and ask again.23- **Never edit `specs/**`, `plans/**`, or `designs/**`** (the canon dirs — `paths:` in witness.config.yaml may relocate them) — not with an edit tool, not with a write tool, not with Bash redirection. The CLI is the sole writer of state; you author in scratch files under `$(mktemp -d)` and hand them to the CLI. (The canon guard blocks you; the trailer audit catches what it can't.)24- **Read canon with `witness read <id>`, never by path.** Canon lives at the primary root; inside a worktree the files are **absent by design**, so a path read finds nothing and a stale copy cannot be mistaken for the contract. Fat artifacts: `witness read <spec-id> --design --outline`, then `--lines <a>-<b>`.25- **Read a file before your first edit of it in this session.** Relay boundaries, `verify-red`'s stash cycle, and worktree re-attach all change files under you — an edit against a remembered copy is how "modified since read" and partial applies happen. The CLI now prints `stale-reads:` when it churns the tree; treat that list as unread.26- **Never invoke gate reviewers or relay verdicts.** `witness gate` runs reviewers itself and journals what they said; your summary of a verdict is not evidence.27- **Refusal repair loop:** a `witness` verb exiting 2 prints structured violations (`field · rule · got · want`). Fix your input and retry — **3 total attempts** per artifact, then stop, show the human the violation list verbatim, and end your turn.28- **A refused or hook-blocked command is a stop, not a step to drop.** Re-issue it on its own; if it still refuses, tell the human what was blocked and why. Never proceed by deleting the refused half of a compound command — a dropped step is silent, and silence is how a skipped check becomes a shipped defect.29- **Re-entrancy:** derive position from CLI output (`$WITNESS next`, the dashboard, `log`, `index`) — never from conversation memory. Killed and re-run, you must converge.3031## Inputs (rebuild them, never remember them)3233Everything below is rebuilt from `witness diff` and the CLI's read verbs — never from conversation memory, never by path.3435```bash36$WITNESS diff <spec-id> # the delta this plan must realize (base: previous plan's pin → last live → empty)37$WITNESS read <spec-id> # the parent spec, current content (reading is fine — writing is not)38$WITNESS index # the plans table names this spec's prior plans, if any39$WITNESS read <plan-id> # …then read the one you care about40$WITNESS decide plan <plan-id> --show # ONLY when re-entered after a revise41```4243**Effort slug** (write needs `--effort`): **take it from the `$WITNESS next` line that routed you here** — next resolves it to a live effort that wrote this plan or its parent, so the slug in that command is the answer. Deriving your own instead risks booking the write onto an abandoned stream. If you arrived without that line: one active effort → use it; several → `$WITNESS log <slug>` per candidate, and the effort whose `write` entries name the parent spec owns this plan; still ambiguous → ask the human. If `next` asks for a `recap` instead of a write, no live effort can carry this plan — that recap is the owed work, not the plan.4445**Plan id**: `<spec-id>-plan-<n>` — n = 1 + the highest existing n in `plans/` for this spec (a spec accumulates plans over its life; expand-contract amends it twice in one effort).4647## Author the plan4849Every criterion in the delta must be realized by ≥ 1 step; every step maps to ≥ 1 criterion **or** is honestly `scaffolding: true` (rigging only — fixtures, wiring, config; never behavior a criterion owns). `derives-from` is **stamped by the CLI** from the parent's current content — never put it in the manifest; a supplied stale pin refuses.5051If the parent spec is `ui`-flagged, its **design must already be approved** (the design stage runs between decompose and plan). Read it with `$WITNESS read <spec-id> --design` (`--outline`, then `--lines <a>-<b>`, when it is fat) — your steps derive from that approved look, not a fresh invention — and put its approved artifact sha in the manifest as `"design-from"` (the CLI refuses a plan whose pin is missing, stale, or present on a non-ui parent; get it from the spec's `design.sha` stamp via `$WITNESS log <spec-id>`). A UI step names the design section (`design#<id>`) it realizes alongside its `@spec:` browser test.5253```bash54DIR=$(mktemp -d)55cat > "$DIR/meta.json" <<'EOF'56{57 "type": "plan",58 "parent": "auth-refresh",59 "depends": [],60 "needs": [],61 "steps": [62 { "id": "s1", "title": "rotate tokens on refresh", "criteria": ["ac-rotate"] },63 { "id": "s2", "title": "smoke wiring", "scaffolding": true }64 ]65}66EOF67cat > "$DIR/body.md" <<'EOF'68## Step: s16970Test-first, bite-sized, checkable: name the exact files to create/modify, the71failing test to write first (its name carries `@spec:auth-refresh`), the72minimal implementation, and the observable end state that proves ac-rotate.7374## Step: s27576What rigging this sets up and why no criterion owns it.77EOF78$WITNESS write auth-refresh-plan-1 --effort <effort> --meta "$DIR/meta.json" --body "$DIR/body.md"79```8081Body discipline (write-validated: exactly one `## Step: <id>` section per manifest step, none missing, none orphaned):8283- Each step section is executable by a fresh session with zero context: exact paths, the test to write first, expected red, minimal code, expected green.84- A step realizing browser-visible behavior (markup, styles, routes, client-side interaction) names an **end-to-end Puppeteer** test as its test-to-write-first — the browser drives the slice's real backend and store, faking only third-party boundaries the repo doesn't own. Browser-level e2e TDD is the implement contract; the implement gate's pr-test lens treats a unit test standing in for the browser — or a browser test stubbing the slice's own backend — as a coverage gap.85- Steps ordered so nothing presumes an artifact a later step creates.86- **Chore-class plans choose their own parent here** — a chore never reaches the decompose stage, so no earlier stage picked one for you. Take the spec whose implementation area the chore touches; take `parent: principles` when the chore is repo-wide. Either way the parent must be `approved`/`live` or the write refuses. Report the choice so the gate stop shows what you routed to.8788## Gate8990```bash91$WITNESS gate plan <plan-id> # append --manual when the run asked for it92```9394- **Auto-pass** → done; hand back to /witness.95- **Stop** → render the gate output verbatim and in full, including its ranked options and `run:` line, and END YOUR TURN.96- **Re-entered after `--revise`** → `decide --show` gives the verdict + note (findings anchor to `<plan-id> > ## Step: <id>`); rewrite via `witness write` with the same plan id; re-gate. A parent amended mid-flight fails `pin-fresh` — rewriting through `witness write` re-stamps the pin to current content; your body must then realize the *new* delta (`$WITNESS diff` again). `--show` also emits `state:` and `exits:` — a `reopened` or `settled` state means the verdict above it is history, so act on the `exits:` line, not on remembered findings.97- Findings implicate the **spec** (plan faithful, spec wrong)? Tell the human `--revise --upstream <spec-id>` reopens decompose for it.