From 9eb69346ceaa5de6ce0a754130c7f11b42e78940 Mon Sep 17 00:00:00 2001 From: Bastien Chanot Date: Wed, 16 Sep 2026 20:39:15 +0200 Subject: [PATCH 01/12] docs(spec): design for the ask-don't-guess clarification doctrine --- .../specs/2026-09-16-ask-dont-guess-design.md | 221 ++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 docs/superpowers/specs/2026-09-16-ask-dont-guess-design.md diff --git a/docs/superpowers/specs/2026-09-16-ask-dont-guess-design.md b/docs/superpowers/specs/2026-09-16-ask-dont-guess-design.md new file mode 100644 index 0000000..97143cd --- /dev/null +++ b/docs/superpowers/specs/2026-09-16-ask-dont-guess-design.md @@ -0,0 +1,221 @@ +# Ask, don't guess: clarification doctrine for the orchestrators + +Date: 2026-09-16. Branch: `feature/ask-dont-guess`. Status: draft for user review. + +## Problem + +The orchestrators (`/ship-feature`, `/feat`, `/bugfix`, `/hotfix`, +`/init-project`) settle choices the user never made. Reported symptom: "add a +share icon" ships with the icon wherever the executor put it; nobody asked +left or right. + +Two causes, both written in the doctrine: + +1. `lib/contract-interview.md` STEP 2 asks a question only when a testable + outcome, a scope, or a non-contradictory constraint is missing. A request + can pass all three and still leave every visible choice open. Taste is + invisible to the trigger, so raising the 3-question cap would change + nothing. +2. When an executor halts with `NEED-DECISION`, `skills/feat/SKILL.md:153` + and `skills/bugfix/SKILL.md:165` instruct the orchestrator to "make the + decision HERE", twice, before escalating. The question reaches the user + after two guesses. + +The parent rule both inherit is `CLAUDE.global.md:51`: "One question upfront +if needed — don't interrupt mid-task." + +## Decisions taken with the user (2026-09-15/16) + +- The global rule changes, for all work, skill or not. `/hotfix` included. +- Three classes of open choice trigger a question: VISIBLE (placement, label, + wording, color, order, click behavior), PUBLIC NAME (command, flag, + endpoint, env var, file the user reads), SCOPE (should X change too, X + unnamed in the request). Class 4, internal technical choices with no + observable effect, never triggers one. +- Questions are asked when they arise, mid-task included, batched when + possible. + +## Design + +### 1. Parent rule, `CLAUDE.global.md` + +Line 51 becomes: + + - Ask rather than guess. A choice visible in the result (placement, + wording, order, behavior), a name that becomes public (command, flag, + endpoint, file), or a scope the request does not settle → ask, even + mid-task. Batch what can be batched. Internal technical choices with + no observable effect stay yours. + *Exception: skill-mandated gates and checkpoints (orchestrator + validation gates, approval gates, darwin checkpoints) always fire.* + +The exception line is kept as is. + +The rule "Bug received → fix directly: check logs, find root cause, resolve +autonomously." gains "; a visible choice in the fix still gets asked" so the +two lines do not contradict each other. `link.sh:20` symlinks this file to +`~/.claude/CLAUDE.md`; no other copy exists. + +### 2. Shared trigger, `lib/contract-interview.md` STEP 2 + +STEP 2 is renamed CLARIFY and split into two passes. Pass A keeps the three +existing gap checks and runs at contract time, against the request. Pass B is +the open-choice sweep: defined here, run once per flow at the PLAN step +(section 3), against the plan just written, because that is where a visible +choice becomes concrete. Replacement text: + + ## STEP 2 — CLARIFY (ask, never guess) + + Two passes, both in the main loop, both may talk to the human. + + **Pass A — gaps.** Run here, against the request. Ask if one of these is + missing AND not derivable from the repo: + - a testable expected outcome + - an unambiguous scope (what is allowed to change) + - non-contradictory constraints + + **Pass B — open choices.** Defined here, run ONCE at the flow's PLAN step + (see "Where pass B fires"), against the plan just written — that is + where choices become concrete. Enumerate every choice the run will + settle that the request leaves open; keep those in these classes: + 1. VISIBLE — the user would see it in the result: placement, label, + wording, color, order, what a click does. + 2. PUBLIC NAME — a name that outlives the run: command, flag, endpoint, + env var, a file the human will read. + 3. SCOPE — "should X change too?", where the request does not name X. + + NEVER ask class 4 — internal technical choices with no observable + effect (function decomposition, data shape, local naming, layout inside + an already-scoped zone). Those are delegated; asking them is the noise + that makes classes 1-3 ignorable. Never ask what the repo or the + request already answers — verify paths/APIs/behavior yourself first. + + No question cap. Each pass asks what it finds, in ONE batch. A request + that leaves nothing open goes through silently. More than 5 open + choices in pass B = the request is under-specified: list them, say so, + stop — do not fire a questionnaire. "You decide" / "peu importe" is an + answer: record it as `A: delegated — ` and never re-ask. + + Pass B answers land in the contract's CLARIFICATIONS marked + `[gated ]` — the contract is already on disk by then. + +Two new sections follow STEP 4 in the same file: + + ## MID-RUN CLARIFICATION (the channel executors halt into) + + An executor cannot talk to the human. It halts with `NEED-DECISION`, + the exact question, the options it sees, and a `CLASS:` tag (visible | + public-name | scope | internal). `/hotfix`: the hotfixer keeps + `DONE | BLOCKED`; a BLOCKED carrying the tag follows the same routing + instead of escalating to `/bugfix`. The orchestrator re-reads the + class — the tag is a hint, not a verdict — then routes: + - visible / public-name / scope → ASK THE HUMAN, verbatim question and + options. Never decide these yourself, never spend a round-trip + guessing. + - internal → decide here, note the decision, re-dispatch. The only case + the orchestrator settles alone; max 2 such round-trips → escalate. + + Every answer, human or orchestrator, appends to the contract's + CLARIFICATIONS marked `[gated ]` — the same micro-gate as + scope enrichment — and to the plan handed to the FRESH re-dispatched + executor, which reads the decision from disk, never from a transcript. + + ## HOW TO ASK (LRN-102) + + The harness reliably renders only the turn's FINAL text; text printed + before a tool call may be swallowed. So: + - up to 4 questions → one `AskUserQuestion` call; option descriptions + carry the context; print nothing the user needs before the call. + - more than 4, or a list handed back for re-specification → plain + text, end the turn. + +The per-flow weight table row for hotfix changes from "Zero questions ever" +to "Pass A silent autofill. Pass B runs at LOCATE against the 1-2 target +files' visible effect; a typo fix asks nothing." + +### 3. Where pass B fires, per flow + +| Flow | Pass B runs at | Against | +|---|---|---| +| feat | STEP 1 PLAN, before 1b CHALLENGE | the PLAN checklist | +| bugfix | STEP 3 FIX PLAN | the FIX PLAN | +| hotfix | STEP 1 LOCATE | the 1-2 target files' visible effect | +| ship-feature | STEP 2 PLAN, after the brainstorm | the plan, minus what the brainstorm already settled (in CLARIFICATIONS) | +| init-project | STEP 3 DESIGN, before VALIDATION GATE #1 | the DESIGN, minus what the interview and brainstorm settled | +| onboard | unchanged | its STEP 3 interview already asks scope in one block; the global rule covers leftovers | + +Each listed step gains one line: "Run pass B of +`$HOME/.claude/lib/contract-interview.md` against this plan; ask the batch +before continuing." + +### 4. Per-flow edits + +- `skills/feat/SKILL.md`: STEP 1's "If the approach is ambiguous: ask the + user ONE focused question BEFORE dispatching — never after (the executor + cannot relay questions)" is replaced by the pass B line. STEP 3's + `NEED-DECISION` handling is replaced by a pointer to MID-RUN CLARIFICATION. + STEP 1b's "surfacing any deferred BLOCKER via STEP 1's one-question gate" + is repointed to the pass B batch. +- `skills/bugfix/SKILL.md`: STEP 3 gains the pass B line; STEP 5's + `NEED-DECISION` handling is replaced by the pointer. The RULES line + "re-dispatched FRESH on every round-trip (NEED-DECISION, …)" stays true. +- `skills/hotfix/SKILL.md`: STEP 1 gains the pass B line. STEP 1.7's + "**zero questions ever**" becomes "pass A silent autofill; pass B was + asked at STEP 1". The hotfixer keeps `DONE | BLOCKED` and its + revert-not-loop identity; STEP 4 relays a tagged BLOCKED as a question + instead of escalating to `/bugfix`. +- `skills/ship-feature/SKILL.md`: STEP 2 PLAN gains the pass B line. +- `skills/init-project/SKILL.md`: line 62's "No new questions (the interview + already asked)" becomes "Pass A is covered by the interview; pass B runs + at STEP 3 against the DESIGN". STEP 3 gains the pass B line. +- `agents/interviewer.md`: the FAILURE MODES rows and the 2-round budget + keep working for gaps. A class 1-3 item still open after round 2 gets ONE + more targeted question; it never lands in OPEN DECISIONS as `(assumed)`. + The DO NOT entry "Exceed the 2-round budget" gains "except the one + targeted class 1-3 question". +- `agents/feater.md`, `agents/bugfixer.md`: the halt trigger "A plan hole or + an open choice (naming, data shape, API surface, dependency)" gains "a + user-visible choice (placement, wording, behavior)". The NOTES grammar for + `NEED-DECISION` gains `CLASS: visible | public-name | scope | internal`. +- `agents/hotfixer.md`: the NOTES grammar for BLOCKED gains the same + `CLASS:` tag when the blocker is an open choice. + +### 5. Locks and tests + +`lib/tests/contract-verifier.test.sh`: drop `"silent when complete" "ZERO +questions"` and `"question budget" "max 3 questions"`. Add locks on: +`"goes through silently"`, `"No question cap"`, `"PUBLIC NAME"`, `"NEVER ask +class 4"`, `"More than 5 open choices"`, `"delegated —"`, `"## MID-RUN +CLARIFICATION"`, `"CLASS:"`, `"## HOW TO ASK"`. + +`lib/tests/loops-light.test.sh:84`: `"hotfix zero questions" "questions +ever"` becomes `"hotfix pass B at locate" "pass B"`. + +`lib/tests/gates.test.sh` locks on `contract-interview.md` (oracle doctrine) +are untouched; the ORACLES section does not move. + +Behavioral check, run once by hand after the edits, in a fixture repo: +`/feat "add a share icon to the header"` must ask placement before +dispatching; `/feat "add a share icon at the right end of the header, label +Share, opens the native share sheet"` must ask nothing. + +### 6. Out of scope + +- `README.md`, `USAGE.md`, `ARCHITECTURE.md`: none mentions the question + doctrine (grep 2026-09-16). A `/doc` pass after merge covers any flow + description that drifts. +- `CHANGELOG.md` entry and registries (a BDR superseding the "one question + upfront" rule) happen at the CAPITALIZE step, not in this spec. +- TODO.md line 199 (C2 self-contradiction audit of `CLAUDE.global.md`) stays + open; this spec fixes only the contradiction it exposes. + +### 7. Risks + +- Chattiness. Three classes, a mid-run channel, no cap. The class 4 + exclusion and the over-5 guard are the two brakes. Watch in real use; + LRN-047 records that a frequent ignored nag is itself a risk. +- `/hotfix` identity. Its value is speed and silence. Pass B at LOCATE adds + one possible batch before touching anything. If it fires on most + hotfixes, the class definitions are too wide, not the flow. +- Executor mis-tagging. A class 4 tagged as visible offloads a decision to + the user. The orchestrator re-reads the class; the tag is a hint. From 0d52f3a888b01b80abd238de2c509111536ed432 Mon Sep 17 00:00:00 2001 From: Bastien Chanot Date: Wed, 16 Sep 2026 22:10:24 +0200 Subject: [PATCH 02/12] docs(plan): ask-don't-guess implementation plan, TODO trace --- .claude/tasks/TODO.md | 28 + .../plans/2026-09-16-ask-dont-guess.md | 709 ++++++++++++++++++ 2 files changed, 737 insertions(+) create mode 100644 docs/superpowers/plans/2026-09-16-ask-dont-guess.md diff --git a/.claude/tasks/TODO.md b/.claude/tasks/TODO.md index 7404bef..927a12a 100644 --- a/.claude/tasks/TODO.md +++ b/.claude/tasks/TODO.md @@ -1,5 +1,33 @@ # TODO +## 2026-09-16 — ask, don't guess: orchestrators ask about open choices (feature/ask-dont-guess) +User: the orchestrators (ship-feature, feat, hotfix, bugfix, init-project) +settle choices they should ask about ("cet icône, plutôt à gauche ou à +droite ?"), even mid-run. Diagnosis: contract-interview STEP 2 only fires on +gaps (outcome / scope / constraints), so a taste choice never triggers a +question; feat:153 and bugfix:165 tell the orchestrator to "make the +decision HERE" on NEED-DECISION. Decisions (user, 2026-09-15/16): global +rule changes for all work, hotfix included; classes VISIBLE / PUBLIC NAME / +SCOPE ask, internal technical choices never. Spec: +`docs/superpowers/specs/2026-09-16-ask-dont-guess-design.md`; plan: +`docs/superpowers/plans/2026-09-16-ask-dont-guess.md` (9 tasks, lock-first). +- [ ] P1 `lib/contract-interview.md` — STEP 2 CLARIFY (pass A gaps, pass B + open choices), MID-RUN CLARIFICATION, HOW TO ASK; 9 locks in + `contract-verifier.test.sh` +- [ ] P2 `CLAUDE.global.md:51-55` — "Ask rather than guess" replaces the + one-question rule; bug line reconciled +- [ ] P3 `skills/feat/SKILL.md` — pass B at STEP 1, NEED-DECISION routed on class +- [ ] P4 `skills/bugfix/SKILL.md` — pass B at STEP 3, NEED-DECISION routed on class +- [ ] P5 `skills/hotfix/SKILL.md` — pass B at LOCATE, tagged BLOCKED relayed; + lock `loops-light.test.sh:84` +- [ ] P6 `skills/ship-feature` STEP 2 + `skills/init-project` contract §/STEP 3 +- [ ] P7 `agents/interviewer.md` — visible/public/scope item never `(assumed)` +- [ ] P8 `agents/{feater,bugfixer,hotfixer}.md` — CLASS tag; 3 locks in `gates.test.sh` +- [ ] P9 `make test`, CHANGELOG, TODO tick; manual behavioral check before merge +- [ ] P10 registries at capitalize: BDR (supersedes the one-question rule), + LRN (taste is invisible to a gap-only trigger; fresh re-dispatch cost + favors plan-time questions) + ## 2026-09-15 — align config + deployment on the hand-edited settings.json (feature/automode-config-alignment) User edited global `settings.json` by hand: 4 destructive rules moved deny→ask (`rsync`, `kill -9`, `killall`, `pkill`), 4 removed from ask diff --git a/docs/superpowers/plans/2026-09-16-ask-dont-guess.md b/docs/superpowers/plans/2026-09-16-ask-dont-guess.md new file mode 100644 index 0000000..c632bcd --- /dev/null +++ b/docs/superpowers/plans/2026-09-16-ask-dont-guess.md @@ -0,0 +1,709 @@ +# Ask, don't guess — Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** The orchestrators ask the user about every open VISIBLE / PUBLIC NAME / SCOPE choice, upfront at plan time and mid-run through the executor's `NEED-DECISION`, instead of settling it themselves. + +**Architecture:** One shared doctrine file (`lib/contract-interview.md`) gains a two-pass STEP 2 (gaps, then open choices), a MID-RUN CLARIFICATION channel and a HOW TO ASK section. The parent rule in `CLAUDE.global.md` changes. Each orchestrator wires pass B at its plan step with one line and routes `NEED-DECISION` on a `CLASS:` tag the executors now emit. Structure-lock tests are the reviewer: every doctrine change is preceded by its lock. + +**Tech Stack:** Markdown doctrine files, bash structure-lock tests (`lib/tests/*.test.sh`, `make test`), gitflow on `feature/ask-dont-guess`. + +**Spec:** `docs/superpowers/specs/2026-09-16-ask-dont-guess-design.md` + +## Global Constraints + +- A locked phrase must sit on ONE unbroken line in the doctrine file (learnings.md:1386). Reflow around it, never through it. +- `lib/tests/gates.test.sh` oracle locks on `contract-interview.md` (`### ORACLES`, `Both attributes or neither`, `url-guard.sh`, `**ABANDONMENT**`, `NEVER deleted`…) must keep passing: the ORACLES section and the Lifecycle section do not move. +- Skill and agent files keep their house style (`→`, `—`, bold markers). The writing-style rule applies to user-facing prose, not to these templates. +- No attribution trailers in commits. Commit on `feature/ask-dont-guess` only; never `finish`. +- `CLAUDE.global.md` stays under the 320-line density budget (308 lines today). +- Every task ends with the relevant test file green, then a commit. + +--- + +### Task 1: Shared doctrine — `lib/contract-interview.md` + +**Files:** +- Modify: `lib/contract-interview.md` (intro §, STEP 2, template CLARIFICATIONS line, new sections before `## Lifecycle`, weight table hotfix row) +- Test: `lib/tests/contract-verifier.test.sh:51-52` + +**Interfaces:** +- Produces: section names `## STEP 2 — CLARIFY`, `## MID-RUN CLARIFICATION`, `## HOW TO ASK`, the phrase "pass B", the tag grammar `CLASS: visible | public-name | scope | internal`. Every later task points at these by name. + +- [ ] **Step 1: Replace the two obsolete locks and add nine** + +In `lib/tests/contract-verifier.test.sh`, replace: +``` +tf "silent when complete" "$LIB" "ZERO questions" +tf "question budget" "$LIB" "max 3 questions" +``` +with: +``` +tf "silent when nothing open" "$LIB" "goes through silently" +tf "no question cap" "$LIB" "No question cap" +tf "pass B classes" "$LIB" "PUBLIC NAME" +tf "class 4 excluded" "$LIB" "NEVER ask class 4" +tf "over-5 guard" "$LIB" "More than 5 open choices" +tf "delegated answer" "$LIB" "delegated —" +tf "mid-run channel" "$LIB" "## MID-RUN CLARIFICATION" +tf "class tag" "$LIB" "CLASS:" +tf "how to ask" "$LIB" "## HOW TO ASK" +``` + +- [ ] **Step 2: Run the lock test, expect the nine new locks red** + +Run: `bash lib/tests/contract-verifier.test.sh 2>&1 | grep -E "FAIL"` +Expected: nine `FAIL` lines (silent when nothing open … how to ask); the verifier.md locks stay PASS. + +- [ ] **Step 3: Edit the intro paragraph** + +Old: +``` +Run this in the ORCHESTRATOR MAIN LOOP, never in a subagent — STEP 2 may +talk to the human. Mandatory passage in every flow; questions are optional +and proportional — a complete request goes through silently. +``` +New: +``` +Run this in the ORCHESTRATOR MAIN LOOP, never in a subagent — STEP 2 may +talk to the human, at contract time (pass A) and again at the flow's PLAN +step (pass B). Questions follow the open choices, never a quota — a complete +request goes through silently. +``` + +- [ ] **Step 4: Replace STEP 2 entirely** + +Old (from `## STEP 2 — AMBIGUITY CHECK` through `verify paths/APIs/behavior yourself first.`): +``` +## STEP 2 — AMBIGUITY CHECK (questions optional, proportional) + +Ask ONLY if one of these is missing AND not derivable from the repo: +- a testable expected outcome +- an unambiguous scope (what is allowed to change) +- non-contradictory constraints + +Complete request → ZERO questions, stay silent. Otherwise: max 3 questions, +one single batch (house rule: one question upfront, never mid-task). Never +ask what the repo can answer — verify paths/APIs/behavior yourself first. +``` +New: +``` +## STEP 2 — CLARIFY (ask, never guess) + +Two passes, both in the main loop, both may talk to the human. + +**Pass A — gaps.** Run here, against the request. Ask if one of these is +missing AND not derivable from the repo: +- a testable expected outcome +- an unambiguous scope (what is allowed to change) +- non-contradictory constraints + +**Pass B — open choices.** Defined here, run ONCE at the flow's PLAN step +(see "Where pass B fires" below), against the plan just written — that is +where choices become concrete. Enumerate every choice the run will settle +that the request leaves open; keep those in these classes: +1. VISIBLE — the user would see it in the result: placement, label, wording, + color, order, what a click does. +2. PUBLIC NAME — a name that outlives the run: command, flag, endpoint, env + var, a file the human will read. +3. SCOPE — "should X change too?", where the request does not name X. + +NEVER ask class 4 — internal technical choices with no observable effect +(function decomposition, data shape, local naming, layout inside an +already-scoped zone). Those are delegated; asking them is the noise that +makes classes 1-3 ignorable. Never ask what the repo or the request already +answers — verify paths/APIs/behavior yourself first. + +No question cap. Each pass asks what it finds, in ONE batch. A request that +leaves nothing open goes through silently. More than 5 open choices in pass B += the request is under-specified: list them, say so, stop — do not fire a +questionnaire. "You decide" / "peu importe" is an answer: record it as +`A: delegated — ` and never re-ask it. + +Pass B answers land in the contract's CLARIFICATIONS marked +`[gated ]` — the contract is already on disk by then. + +### Where pass B fires + +| Flow | Pass B runs at | Against | +|------|----------------|---------| +| feat | STEP 1 PLAN, before 1b CHALLENGE | the PLAN checklist | +| bugfix | STEP 3 FIX PLAN, before 3b | the FIX PLAN | +| hotfix | STEP 1 LOCATE | the 1-2 target files' visible effect | +| ship-feature | STEP 2 PLAN, after the brainstorm | the plan, minus what the brainstorm settled | +| init-project | STEP 3 DESIGN, before VALIDATION GATE #1 | the DESIGN, minus what the interview and brainstorm settled | +| onboard | its STEP 3 interview, unchanged | scope, in one block | +``` + +- [ ] **Step 5: Mark gated entries in the template** + +Old: `Q: / A: ` +New: `Q: / A: (pass B and mid-run entries: [gated ])` + +- [ ] **Step 6: Insert the two new sections right before `## Lifecycle`** + +``` +## MID-RUN CLARIFICATION (the channel executors halt into) + +An executor cannot talk to the human. It halts with `NEED-DECISION`, the +exact question, the options it sees, and a `CLASS:` tag (visible | +public-name | scope | internal). `/hotfix`: the hotfixer keeps +`DONE | BLOCKED`; a BLOCKED carrying the tag follows the same routing instead +of escalating to `/bugfix`. The orchestrator re-reads the class — the tag is +a hint, not a verdict — then routes: +- visible / public-name / scope → ASK THE HUMAN, verbatim question and + options. Never decide these yourself, never spend a round-trip guessing. +- internal → decide here, note the decision, re-dispatch. The only case the + orchestrator settles alone; max 2 such round-trips → escalate. + +Every answer, human or orchestrator, appends to the contract's +CLARIFICATIONS marked `[gated ]` — the same micro-gate as scope +enrichment — and to the plan handed to the FRESH re-dispatched executor, +which reads the decision from disk, never from a transcript. + +## HOW TO ASK (LRN-102) + +The harness reliably renders only the turn's FINAL text; text printed before +a tool call may be swallowed. So: +- up to 4 questions → one `AskUserQuestion` call; option descriptions carry + the context; print nothing the user needs before the call. +- more than 4, or a list handed back for re-specification → plain text, end + the turn. + +``` + +- [ ] **Step 7: Rewrite the hotfix row of the weight table** + +Old: +``` +| hotfix | Silent autofill — criteria: "symptom gone; build/tests green"; scope = the 1-2 target files. Zero questions ever. | +``` +New: +``` +| hotfix | Pass A silent autofill — criteria: "symptom gone; build/tests green"; scope = the 1-2 target files. Pass B runs at LOCATE against the 1-2 target files' visible effect; a typo fix asks nothing. | +``` + +- [ ] **Step 8: Run both lock suites, expect green** + +Run: `bash lib/tests/contract-verifier.test.sh 2>&1 | grep -E "FAIL|PASS="; bash lib/tests/gates.test.sh 2>&1 | grep -E "FAIL|bad|PASS=|ok=" | tail -3` +Expected: no `FAIL` line in contract-verifier; gates oracle locks all `ok`. + +- [ ] **Step 9: Commit** + +```bash +git add lib/contract-interview.md lib/tests/contract-verifier.test.sh +git commit -m "feat(contract): STEP 2 CLARIFY, mid-run channel, how-to-ask" +``` + +--- + +### Task 2: Parent rule — `CLAUDE.global.md` + +**Files:** +- Modify: `CLAUDE.global.md:51-55` +- Test: `wc -l CLAUDE.global.md` (≤ 320) and `bash lib/tests/curated-config-guard.test.sh` + +**Interfaces:** +- Produces: the sentence "Ask rather than guess." as the house rule every skill inherits. + +- [ ] **Step 1: Replace lines 51-53** + +Old: +``` +- One question upfront if needed — don't interrupt mid-task. + *Exception: skill-mandated gates and checkpoints (orchestrator + validation gates, approval gates, darwin checkpoints) always fire.* +``` +New: +``` +- Ask rather than guess. A choice visible in the result (placement, + wording, order, behavior), a name that becomes public (command, flag, + endpoint, file), or a scope the request does not settle → ask, even + mid-task. Batch what can be batched. Internal technical choices with + no observable effect stay yours. + *Exception: skill-mandated gates and checkpoints (orchestrator + validation gates, approval gates, darwin checkpoints) always fire.* +``` + +- [ ] **Step 2: Reconcile the bug line** + +Old: +``` +- Bug received → fix directly: check logs, find root cause, resolve + autonomously. +``` +New: +``` +- Bug received → fix directly: check logs, find root cause, resolve + autonomously; a visible choice in the fix still gets asked. +``` + +- [ ] **Step 3: Verify budget and guard** + +Run: `wc -l CLAUDE.global.md; grep -c "One question upfront" CLAUDE.global.md lib/contract-interview.md; bash lib/tests/curated-config-guard.test.sh 2>&1 | tail -2` +Expected: ≤ 320 lines; both grep counts `0`; guard test green. + +- [ ] **Step 4: Commit** + +```bash +git add CLAUDE.global.md +git commit -m "feat(rules): ask rather than guess replaces one-question-upfront" +``` + +--- + +### Task 3: `/feat` wiring + +**Files:** +- Modify: `skills/feat/SKILL.md` (STEP 0.7 §, STEP 1 tail, STEP 1b tail, STEP 3 parse) +- Test: `bash lib/tests/loops-light.test.sh` + +**Interfaces:** +- Consumes: `pass B`, `MID-RUN CLARIFICATION`, `CLASS:` from Task 1. + +- [ ] **Step 1: STEP 0.7 wording** + +Old: +``` +captures the request verbatim, asks 0-3 questions PROPORTIONAL to ambiguity +(a complete request → zero questions, silent), derives testable acceptance +criteria + file scope, and writes the contract to +``` +New: +``` +captures the request verbatim, runs pass A (gaps: outcome, scope, +constraints — a complete request goes through silently), derives testable +acceptance criteria + file scope, and writes the contract to +``` + +- [ ] **Step 2: STEP 1 tail — replace the one-question rule with pass B** + +Old: +``` +If the approach is ambiguous: ask the user ONE focused question BEFORE +dispatching — never after (the executor cannot relay questions). +``` +New: +``` +Then run pass B of `$HOME/.claude/lib/contract-interview.md` against this +plan: every VISIBLE / PUBLIC NAME / SCOPE choice the plan settles that the +request left open → one batch of questions BEFORE dispatching; answers land +in the contract's CLARIFICATIONS `[gated]` and in the plan. A choice that +surfaces only during execution comes back as `NEED-DECISION` (STEP 3). +``` + +- [ ] **Step 3: STEP 1b tail** + +Old: `surfacing any deferred BLOCKER via\nSTEP 1's one-question gate.` +New: `surfacing any deferred BLOCKER in\nthe STEP 1 pass B batch.` + +- [ ] **Step 4: STEP 3 parse** + +Old: +``` +- `STATUS : NEED-DECISION` → make the decision HERE (that is reflection), + append it to the plan, re-dispatch a FRESH feater with plan + decision. + Max 2 decision round-trips → escalate to the user. +``` +New: +``` +- `STATUS : NEED-DECISION` → route on its `CLASS:` per MID-RUN CLARIFICATION + in `$HOME/.claude/lib/contract-interview.md`: visible / public-name / scope + → ask the user, verbatim; internal → decide HERE (max 2 such round-trips + → escalate). Append the answer to the contract `[gated]` and to the plan, + re-dispatch a FRESH feater with plan + decision. +``` + +- [ ] **Step 5: Test and commit** + +Run: `grep -n "ONE focused question\|decision HERE (that is reflection)" skills/feat/SKILL.md; bash lib/tests/loops-light.test.sh 2>&1 | grep -E "FAIL|PASS="` +Expected: grep empty; `FAIL=0`. +```bash +git add skills/feat/SKILL.md +git commit -m "feat(feat): pass B at PLAN, NEED-DECISION routed on class" +``` + +--- + +### Task 4: `/bugfix` wiring + +**Files:** +- Modify: `skills/bugfix/SKILL.md` (STEP 3 bullets, STEP 3.5 §, STEP 5 parse) +- Test: `bash lib/tests/loops-light.test.sh` + +- [ ] **Step 1: STEP 3 — add pass B after the approval bullets** + +After: +``` +- If the fix is significant (>10 lines, multiple files, + behavior change): wait for user approval. +``` +add: +``` +- Then run pass B of `$HOME/.claude/lib/contract-interview.md` against the + FIX PLAN: every VISIBLE / PUBLIC NAME / SCOPE choice it settles that the + bug report left open → one batch of questions, before STEP 3b. The trivial + fast-path is not exempt: a 1-line fix with a visible choice still asks. +``` + +- [ ] **Step 2: STEP 3.5 wording** + +Old: `Questions stay proportional (a clear,\nreproduced bug → zero). It writes the contract to` +New: `Pass A only here (pass B ran at STEP 3); a clear,\nreproduced bug asks nothing. It writes the contract to` + +- [ ] **Step 3: STEP 5 parse** + +Old: +``` +- `STATUS : NEED-DECISION` → make the decision HERE (that is reflection), + append it to the plan, re-dispatch a FRESH bugfixer with plan + decision. + Max 2 decision round-trips → escalate to the user. +``` +New: +``` +- `STATUS : NEED-DECISION` → route on its `CLASS:` per MID-RUN CLARIFICATION + in `$HOME/.claude/lib/contract-interview.md`: visible / public-name / scope + → ask the user, verbatim; internal → decide HERE (max 2 such round-trips + → escalate). Append the answer to the contract `[gated]` and to the plan, + re-dispatch a FRESH bugfixer with plan + decision. +``` + +- [ ] **Step 4: Test and commit** + +Run: `grep -n "decision HERE (that is reflection)\|stay proportional" skills/bugfix/SKILL.md; bash lib/tests/loops-light.test.sh 2>&1 | grep -E "FAIL|PASS="` +Expected: grep empty; `FAIL=0`. +```bash +git add skills/bugfix/SKILL.md +git commit -m "feat(bugfix): pass B at FIX PLAN, NEED-DECISION routed on class" +``` + +--- + +### Task 5: `/hotfix` wiring (lock first) + +**Files:** +- Modify: `skills/hotfix/SKILL.md` (STEP 1 bullet, STEP 1.7 §, STEP 3 parse, RULES) +- Test: `lib/tests/loops-light.test.sh:84` + +- [ ] **Step 1: Replace the lock** + +Old: `tf "hotfix zero questions" "$HSKL" "questions ever"` +New: `tf "hotfix pass B at locate" "$HSKL" "run pass B of"` + +- [ ] **Step 2: Run, expect red** + +Run: `bash lib/tests/loops-light.test.sh 2>&1 | grep -E "FAIL"` +Expected: one FAIL, `hotfix pass B at locate`. + +- [ ] **Step 3: STEP 1 — add pass B after the "Settle the proposed fix HERE" bullet** + +After: +``` +- Settle the proposed fix HERE — the executor cannot ask questions, so the + exact edit (what changes, in which file(s)) must be closed before dispatch. +``` +add: +``` +- Then run pass B of `$HOME/.claude/lib/contract-interview.md` against that + edit: a VISIBLE / PUBLIC NAME / SCOPE choice the bug description leaves + open (which way the icon aligns, the label's wording) → ask before + dispatch. A typo or a wrong value asks nothing. +``` + +- [ ] **Step 4: STEP 1.7 wording** + +Old: +``` +Run `$HOME/.claude/lib/contract-interview.md` at hotfix weight: **zero +questions ever** (a hotfix is an obvious fix by definition). Autofill the +``` +New: +``` +Run `$HOME/.claude/lib/contract-interview.md` at hotfix weight: pass A is a +silent autofill (a hotfix is an obvious fix by definition); pass B already +ran at STEP 1, ask nothing more here. Autofill the +``` + +- [ ] **Step 5: STEP 3 parse — a tagged BLOCKED is a question, not a revert** + +Insert before the existing `- \`STATUS : BLOCKED\` → if any edits were made, revert ONLY the executor's` bullet: +``` +- `STATUS : BLOCKED` with `CLASS: visible | public-name | scope` in NOTES → + the executor halted at an open choice before editing (nothing to revert): + ask the user per MID-RUN CLARIFICATION in + `$HOME/.claude/lib/contract-interview.md`, append the answer to the + contract `[gated]`, re-dispatch ONCE with the closed choice. This is the + one re-dispatch hotfix allows; it is not a retry of a failed attempt. +``` +and change the following bullet's head from `- \`STATUS : BLOCKED\` → if any edits were made,` to `- \`STATUS : BLOCKED\` otherwise → if any edits were made,`. + +- [ ] **Step 6: RULES** + +Old: +``` +- The executor is dispatched FRESH, once — hotfix never re-dispatches (no + decision round-trips; a blocked or failed attempt reverts and escalates + to `/bugfix`, it does not retry). +``` +New: +``` +- The executor is dispatched FRESH, once — hotfix never re-dispatches after + a failed or blocked attempt (it reverts and escalates to `/bugfix`, it + does not retry). Sole exception: a class-tagged BLOCKED answered by the + user (STEP 3), re-dispatched once with the closed choice. +``` + +- [ ] **Step 7: Run, expect green, commit** + +Run: `grep -n "questions ever" skills/hotfix/SKILL.md; bash lib/tests/loops-light.test.sh 2>&1 | grep -E "FAIL|PASS="` +Expected: grep empty; `FAIL=0`. +```bash +git add skills/hotfix/SKILL.md lib/tests/loops-light.test.sh +git commit -m "feat(hotfix): pass B at LOCATE, class-tagged BLOCKED relayed as a question" +``` + +--- + +### Task 6: `/ship-feature` and `/init-project` wiring + +**Files:** +- Modify: `skills/ship-feature/SKILL.md` (STEP 2), `skills/init-project/SKILL.md` (contract §, STEP 3) +- Test: `bash lib/tests/loops-heavy.test.sh` + +- [ ] **Step 1: ship-feature STEP 2** + +After: +``` +note the ID inline. Break design into tasks (2-5 min each). Each task: exact file paths, full code, verification steps. +``` +add: +``` +Then run pass B of `$HOME/.claude/lib/contract-interview.md` against the plan: +every VISIBLE / PUBLIC NAME / SCOPE choice the plan settles that neither the +request nor the STEP 1 brainstorm settled (check the contract's CLARIFICATIONS +first) → one batch before STEP 2b; answers append to the contract `[gated]`. +``` + +- [ ] **Step 2: init-project contract paragraph** + +Old: `FILE SCOPE = the planned tree. No new questions\n(the interview already asked). It writes` +New: `FILE SCOPE = the planned tree. Pass A is covered by\nthe interview; pass B runs at STEP 3 against the DESIGN. It writes` + +- [ ] **Step 3: init-project STEP 3** + +After the line ending `test strategy, resolved decisions, prereqs list.` add: +``` +Then run pass B of `$HOME/.claude/lib/contract-interview.md` against the DESIGN +(minus what the BRIEF and the brainstorm settled): one batch before STEP 4; +answers append to the contract `[gated]`. +``` + +- [ ] **Step 4: Test and commit** + +Run: `grep -n "No new questions" skills/init-project/SKILL.md; bash lib/tests/loops-heavy.test.sh 2>&1 | grep -E "FAIL|PASS="` +Expected: grep empty; `FAIL=0`. +```bash +git add skills/ship-feature/SKILL.md skills/init-project/SKILL.md +git commit -m "feat(ship-feature,init-project): pass B at the plan and design steps" +``` + +--- + +### Task 7: interviewer — no `(assumed)` on a visible choice + +**Files:** +- Modify: `agents/interviewer.md:17,23,80` +- Test: `bash lib/tests/run-review-guards.sh` (G3 strict-YAML frontmatter) + +- [ ] **Step 1: Budget line (17)** + +Old: +``` +- Hard budget: 2 question rounds total (initial block + one follow-up). The BRIEF ships after round 2 no matter what — gaps become OPEN DECISIONS, never a third round. +``` +New: +``` +- Hard budget: 2 question rounds total (initial block + one follow-up) for gaps. The BRIEF ships after round 2 — gaps become OPEN DECISIONS. Sole exception: a VISIBLE, PUBLIC NAME or SCOPE choice (a user-facing placement or wording, a public command/flag/endpoint name, whether X is in scope) still open after round 2 gets ONE more targeted question; it never ships as `(assumed)`. +``` + +- [ ] **Step 2: Failure-mode row (23)** + +Old: +``` +| Answer vague/ambiguous | One targeted follow-up on that item only | Record item in OPEN DECISIONS with the safest reading, marked `(assumed)` — never invent a confident value | +``` +New: +``` +| Answer vague/ambiguous | One targeted follow-up on that item only | Gap: record it in OPEN DECISIONS with the safest reading, marked `(assumed)` — never invent a confident value. Visible / public-name / scope item: one more targeted question instead, never `(assumed)` | +``` + +- [ ] **Step 3: DO NOT line (80)** + +Old: `- Exceed the 2-round budget, whatever is still missing.` +New: `- Exceed the 2-round budget for gaps; the only extra question is the single targeted one a visible / public-name / scope item earns.` + +- [ ] **Step 4: Test and commit** + +Run: `bash lib/tests/run-review-guards.sh 2>&1 | grep -E "G3|RED"` +Expected: `GREEN ✓ G3`, no RED. +```bash +git add agents/interviewer.md +git commit -m "feat(interviewer): a visible or public choice is asked, never assumed" +``` + +--- + +### Task 8: Executors emit the class (lock first) + +**Files:** +- Modify: `agents/feater.md` (halt bullet, NOTES), `agents/bugfixer.md` (halt bullet, NOTES), `agents/hotfixer.md` (new rule bullet, NOTES) +- Test: `lib/tests/gates.test.sh` after line 313 + +- [ ] **Step 1: Add the locks** + +After `lock "bugfixer test must fail" "$BF" "A test that passes both ways"` add: +``` +lock "feater class tag" "$FE" "CLASS:" +lock "bugfixer class tag" "$BF" "CLASS:" +lock "hotfixer class tag" "$REPO/agents/hotfixer.md" "CLASS:" +``` + +- [ ] **Step 2: Run, expect three red** + +Run: `bash lib/tests/gates.test.sh 2>&1 | grep -E "bad|FAIL" | head` +Expected: three `bad` lines (class tag). + +- [ ] **Step 3: feater halt bullet** + +Old: +``` +- Follow the plan to the letter. A plan hole or an open choice (naming, + data shape, API surface, dependency) → STOP, report `NEED-DECISION` with + the precise question. Never improvise a design decision. +``` +New: +``` +- Follow the plan to the letter. A plan hole or an open choice (naming, + data shape, API surface, dependency, a user-visible choice such as + placement, wording or behavior) → STOP, report `NEED-DECISION` with the + precise question and its `CLASS:`. Never improvise a design decision. +``` + +- [ ] **Step 4: feater NOTES** + +Old: +``` +NOTES : +``` +New: +``` +NOTES : +``` + +- [ ] **Step 5: bugfixer halt bullet** + +Old: +``` +- Apply the FIX PLAN to the letter — fix the ROOT CAUSE named in DIAGNOSIS, + not the symptom. A plan hole or an open choice (naming, data shape, API + surface, dependency) → STOP, report `NEED-DECISION` with the precise + question. Never re-investigate or improvise a different fix. +``` +New: +``` +- Apply the FIX PLAN to the letter — fix the ROOT CAUSE named in DIAGNOSIS, + not the symptom. A plan hole or an open choice (naming, data shape, API + surface, dependency, a user-visible choice such as placement, wording or + behavior) → STOP, report `NEED-DECISION` with the precise question and + its `CLASS:`. Never re-investigate or improvise a different fix. +``` + +- [ ] **Step 6: bugfixer NOTES** + +Old: +``` +NOTES : +``` +New: +``` +NOTES : +``` + +- [ ] **Step 7: hotfixer rule + NOTES** + +After the `- Stay inside the scope you were given.` bullet (ends `apply only those.`) add: +``` +- An open user-visible choice the contract does not settle (placement, + wording, behavior) → `STATUS BLOCKED` with `CLASS: visible | public-name | + scope` in NOTES, BEFORE editing anything. The orchestrator asks the user + and re-dispatches once. +``` +Old NOTES: `NOTES : ` +New NOTES: +``` +NOTES : +``` + +- [ ] **Step 8: Run, expect green, commit** + +Run: `bash lib/tests/gates.test.sh 2>&1 | grep -E "bad|FAIL|PASS=|ok=" | tail -3; bash lib/tests/run-review-guards.sh 2>&1 | grep -E "G3|RED"` +Expected: no `bad`; G3 GREEN. +```bash +git add agents/feater.md agents/bugfixer.md agents/hotfixer.md lib/tests/gates.test.sh +git commit -m "feat(executors): NEED-DECISION and BLOCKED carry a CLASS tag" +``` + +--- + +### Task 9: Full suite, changelog, closure + +**Files:** +- Modify: `CHANGELOG.md` ([Unreleased] → Changed), `.claude/tasks/TODO.md` +- Test: `make test` + +- [ ] **Step 1: Full suite** + +Run: `timeout 300 make test 2>&1 | grep -E "RED|FAIL=" | grep -vE " 0 RED|FAIL=0"` +Expected: empty output. + +- [ ] **Step 2: CHANGELOG entry under `### Changed`** + +``` +- **Ask, don't guess: the orchestrators ask about open choices instead of + settling them.** `CLAUDE.global.md` replaces "one question upfront, never + mid-task" with: a choice visible in the result, a name that becomes + public, or a scope the request does not settle → ask, even mid-task; + internal technical choices stay Claude's. `lib/contract-interview.md` + STEP 2 becomes CLARIFY: pass A (the three gap checks, at contract time) + and pass B (the open-choice sweep in three classes, run once at each + flow's PLAN step, no question cap, over-5 guard, "you decide" recorded as + delegated). New MID-RUN CLARIFICATION section: an executor's + `NEED-DECISION` carries a `CLASS:` tag; visible / public-name / scope go + to the user verbatim, internal is decided in the loop; answers land in + the contract `[gated]`. New HOW TO ASK section (LRN-102). `/feat`, + `/bugfix`, `/hotfix`, `/ship-feature`, `/init-project` wire pass B at + their plan step; `/feat` and `/bugfix` stop deciding `NEED-DECISION` + themselves; `/hotfix` drops "zero questions ever" and allows one + re-dispatch for a class-tagged BLOCKED; the interviewer never ships a + visible / public-name / scope item as `(assumed)`; feater, bugfixer and + hotfixer report the class. Locks updated in the `contract-verifier`, + `loops-light` and `gates` tests. +``` + +- [ ] **Step 3: Tick the TODO section, commit** + +```bash +git add CHANGELOG.md .claude/tasks/TODO.md +git commit -m "docs(changelog): ask-don't-guess doctrine" +``` + +- [ ] **Step 4: Behavioral check (manual, before merge)** + +In a fixture repo: `/feat "add a share icon to the header"` must ask placement before dispatching; `/feat "add a share icon at the right end of the header, label Share, opens the native share sheet"` must ask nothing. Record the outcome in `evals.md` at capitalize. From a2978b6f239ba26abbd30d2dd8cdb9c217adb1b3 Mon Sep 17 00:00:00 2001 From: Bastien Chanot Date: Wed, 16 Sep 2026 22:12:13 +0200 Subject: [PATCH 03/12] feat(contract): STEP 2 CLARIFY, mid-run channel, how-to-ask --- lib/contract-interview.md | 81 +++++++++++++++++++++++++---- lib/tests/contract-verifier.test.sh | 11 +++- 2 files changed, 81 insertions(+), 11 deletions(-) diff --git a/lib/contract-interview.md b/lib/contract-interview.md index 47256b7..63aa9af 100644 --- a/lib/contract-interview.md +++ b/lib/contract-interview.md @@ -7,8 +7,9 @@ subagents = execution + report only; gates and loop decisions live in the main loop). Run this in the ORCHESTRATOR MAIN LOOP, never in a subagent — STEP 2 may -talk to the human. Mandatory passage in every flow; questions are optional -and proportional — a complete request goes through silently. +talk to the human, at contract time (pass A) and again at the flow's PLAN +step (pass B). Questions follow the open choices, never a quota — a complete +request goes through silently. ## STEP 1 — CAPTURE (verbatim) @@ -17,16 +18,51 @@ message). No paraphrase, no cleanup, no translation, no summarizing. This section is IMMUTABLE for the life of the run — every later consumer (planner, dev, verifier) reads THESE words, never a restatement. -## STEP 2 — AMBIGUITY CHECK (questions optional, proportional) +## STEP 2 — CLARIFY (ask, never guess) -Ask ONLY if one of these is missing AND not derivable from the repo: +Two passes, both in the main loop, both may talk to the human. + +**Pass A — gaps.** Run here, against the request. Ask if one of these is +missing AND not derivable from the repo: - a testable expected outcome - an unambiguous scope (what is allowed to change) - non-contradictory constraints -Complete request → ZERO questions, stay silent. Otherwise: max 3 questions, -one single batch (house rule: one question upfront, never mid-task). Never -ask what the repo can answer — verify paths/APIs/behavior yourself first. +**Pass B — open choices.** Defined here, run ONCE at the flow's PLAN step +(see "Where pass B fires" below), against the plan just written — that is +where choices become concrete. Enumerate every choice the run will settle +that the request leaves open; keep those in these classes: +1. VISIBLE — the user would see it in the result: placement, label, wording, + color, order, what a click does. +2. PUBLIC NAME — a name that outlives the run: command, flag, endpoint, env + var, a file the human will read. +3. SCOPE — "should X change too?", where the request does not name X. + +NEVER ask class 4 — internal technical choices with no observable effect +(function decomposition, data shape, local naming, layout inside an +already-scoped zone). Those are delegated; asking them is the noise that +makes classes 1-3 ignorable. Never ask what the repo or the request already +answers — verify paths/APIs/behavior yourself first. + +No question cap. Each pass asks what it finds, in ONE batch. A request that +leaves nothing open goes through silently. More than 5 open choices in pass B += the request is under-specified: list them, say so, stop — do not fire a +questionnaire. "You decide" / "peu importe" is an answer: record it as +`A: delegated — ` and never re-ask it. + +Pass B answers land in the contract's CLARIFICATIONS marked +`[gated ]` — the contract is already on disk by then. + +### Where pass B fires + +| Flow | Pass B runs at | Against | +|------|----------------|---------| +| feat | STEP 1 PLAN, before 1b CHALLENGE | the PLAN checklist | +| bugfix | STEP 3 FIX PLAN, before 3b | the FIX PLAN | +| hotfix | STEP 1 LOCATE | the 1-2 target files' visible effect | +| ship-feature | STEP 2 PLAN, after the brainstorm | the plan, minus what the brainstorm settled | +| init-project | STEP 3 DESIGN, before VALIDATION GATE #1 | the DESIGN, minus what the interview and brainstorm settled | +| onboard | its STEP 3 interview, unchanged | scope, in one block | ## STEP 3 — DERIVE @@ -85,7 +121,7 @@ Template: ## CLARIFICATIONS -Q: / A: +Q: / A: (pass B and mid-run entries: [gated ]) (or: none — request complete) ## ACCEPTANCE CRITERIA @@ -105,6 +141,33 @@ Q: / A: Print one line to the user, then continue the flow: `CONTRACT: — criteria, scope , questions asked` +## MID-RUN CLARIFICATION (the channel executors halt into) + +An executor cannot talk to the human. It halts with `NEED-DECISION`, the +exact question, the options it sees, and a `CLASS:` tag (visible | +public-name | scope | internal). `/hotfix`: the hotfixer keeps +`DONE | BLOCKED`; a BLOCKED carrying the tag follows the same routing instead +of escalating to `/bugfix`. The orchestrator re-reads the class — the tag is +a hint, not a verdict — then routes: +- visible / public-name / scope → ASK THE HUMAN, verbatim question and + options. Never decide these yourself, never spend a round-trip guessing. +- internal → decide here, note the decision, re-dispatch. The only case the + orchestrator settles alone; max 2 such round-trips → escalate. + +Every answer, human or orchestrator, appends to the contract's +CLARIFICATIONS marked `[gated ]` — the same micro-gate as scope +enrichment — and to the plan handed to the FRESH re-dispatched executor, +which reads the decision from disk, never from a transcript. + +## HOW TO ASK (LRN-102) + +The harness reliably renders only the turn's FINAL text; text printed before +a tool call may be swallowed. So: +- up to 4 questions → one `AskUserQuestion` call; option descriptions carry + the context; print nothing the user needs before the call. +- more than 4, or a list handed back for re-specification → plain text, end + the turn. + ## Lifecycle - **REQUEST**: immutable, for the life of the run. Never rewritten, never @@ -134,7 +197,7 @@ Print one line to the user, then continue the flow: | Flow | Weight | |------|--------| -| hotfix | Silent autofill — criteria: "symptom gone; build/tests green"; scope = the 1-2 target files. Zero questions ever. | +| hotfix | Pass A silent autofill — criteria: "symptom gone; build/tests green"; scope = the 1-2 target files. Pass B runs at LOCATE against the 1-2 target files' visible effect; a typo fix asks nothing. | | feat / bugfix | Proportional. bugfix: the DIAGNOSIS feeds the criteria (symptom reproduced-then-gone + regression test present). | | ship-feature | Full. Design decisions approved at the validation gate append criteria `[gated ]` — the human validates the enriched contract, the verifier receives that version. | | init-project | Full. The interviewer's PROJECT BRIEF pours into the contract (V1 features → criteria). | diff --git a/lib/tests/contract-verifier.test.sh b/lib/tests/contract-verifier.test.sh index ce77347..36b02c1 100644 --- a/lib/tests/contract-verifier.test.sh +++ b/lib/tests/contract-verifier.test.sh @@ -48,8 +48,15 @@ fi tf "verbatim request immutable" "$LIB" "REQUEST (verbatim — IMMUTABLE)" tf "contracts dir committed path" "$LIB" ".claude/tasks/contracts/" tf "unique per-run slug" "$LIB" "--" -tf "silent when complete" "$LIB" "ZERO questions" -tf "question budget" "$LIB" "max 3 questions" +tf "silent when nothing open" "$LIB" "goes through silently" +tf "no question cap" "$LIB" "No question cap" +tf "pass B classes" "$LIB" "PUBLIC NAME" +tf "class 4 excluded" "$LIB" "NEVER ask class 4" +tf "over-5 guard" "$LIB" "More than 5 open choices" +tf "delegated answer" "$LIB" "delegated —" +tf "mid-run channel" "$LIB" "## MID-RUN CLARIFICATION" +tf "class tag" "$LIB" "CLASS:" +tf "how to ask" "$LIB" "## HOW TO ASK" tf "aborted status" "$LIB" "status: aborted" tf "never left dirty" "$LIB" "NEVER left dirty" tf "scope enrichment micro-gate" "$LIB" "micro-gate" From 5f9a9c0f6ba4888544ce7c17342127cc306d38d5 Mon Sep 17 00:00:00 2001 From: Bastien Chanot Date: Wed, 16 Sep 2026 22:12:56 +0200 Subject: [PATCH 04/12] feat(rules): ask rather than guess replaces one-question-upfront --- CLAUDE.global.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CLAUDE.global.md b/CLAUDE.global.md index 3ab8178..a51c3b6 100644 --- a/CLAUDE.global.md +++ b/CLAUDE.global.md @@ -48,11 +48,15 @@ Apply unless repo-specific instructions override. calls. Skill-mandated gates (fresh verifier/security/challenge) always dispatch as written. Don't redo delegated work by hand — failed gates re-dispatch fresh executors instead. -- One question upfront if needed — don't interrupt mid-task. +- Ask rather than guess. A choice visible in the result (placement, + wording, order, behavior), a name that becomes public (command, flag, + endpoint, file), or a scope the request does not settle → ask, even + mid-task. Batch what can be batched. Internal technical choices with + no observable effect stay yours. *Exception: skill-mandated gates and checkpoints (orchestrator validation gates, approval gates, darwin checkpoints) always fire.* - Bug received → fix directly: check logs, find root cause, resolve - autonomously. + autonomously; a visible choice in the fix still gets asked. - Something goes wrong → STOP, re-plan. Never push through. - Deviations: minor or clearly justified → do, explain after. Significant or shaky justification → ask before deviating. From 6d9a3497a2a8be469e52baba7d1339d5835bda40 Mon Sep 17 00:00:00 2001 From: Bastien Chanot Date: Wed, 16 Sep 2026 22:13:06 +0200 Subject: [PATCH 05/12] feat(feat): pass B at PLAN, NEED-DECISION routed on class --- skills/feat/SKILL.md | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/skills/feat/SKILL.md b/skills/feat/SKILL.md index 54b96f4..65393be 100644 --- a/skills/feat/SKILL.md +++ b/skills/feat/SKILL.md @@ -85,9 +85,9 @@ MEMORY; feed STEP 1 PLAN. Inline consumption — reader = planner, no injection. ## STEP 0.7 — CONTRACT Run `$HOME/.claude/lib/contract-interview.md` (main loop — you are it). It -captures the request verbatim, asks 0-3 questions PROPORTIONAL to ambiguity -(a complete request → zero questions, silent), derives testable acceptance -criteria + file scope, and writes the contract to +captures the request verbatim, runs pass A (gaps: outcome, scope, +constraints — a complete request goes through silently), derives testable +acceptance criteria + file scope, and writes the contract to `.claude/tasks/contracts/--.md`. Keep the path — the executor reads it first and GATE 1 (STEP 4) hands it to a fresh verifier. @@ -114,8 +114,11 @@ PLAN: [ ] — ``` -If the approach is ambiguous: ask the user ONE focused question BEFORE -dispatching — never after (the executor cannot relay questions). +Then run pass B of `$HOME/.claude/lib/contract-interview.md` against this +plan: every VISIBLE / PUBLIC NAME / SCOPE choice the plan settles that the +request left open → one batch of questions BEFORE dispatching; answers land +in the contract's CLARIFICATIONS `[gated]` and in the plan. A choice that +surfaces only during execution comes back as `NEED-DECISION` (STEP 3). ## STEP 1b — CHALLENGE THE PLAN (before branching) The STEP 1 plan is a reflection worth attacking before a branch is spent on it. @@ -125,8 +128,8 @@ Persist it to `.claude/tasks/plans/--.md`, then run Three blind challengers attack it; RE-THINK every aspect a BLOCKER lands (a named plan change, or `[deferred]`), re-challenge once if the plan materially changed. The STEP 3 executor receives the REVISED plan. Before dispatch, print a CHALLENGE SUMMARY -(BLOCKERs addressed / deferred / lenses returned), surfacing any deferred BLOCKER via -STEP 1's one-question gate. +(BLOCKERs addressed / deferred / lenses returned), surfacing any deferred BLOCKER in +the STEP 1 pass B batch. ## STEP 2 — BRANCH @@ -150,9 +153,11 @@ Finish with the FEAT-EXEC REPORT." Parse the `FEAT-EXEC REPORT`: - `STATUS : DONE` → STEP 4. -- `STATUS : NEED-DECISION` → make the decision HERE (that is reflection), - append it to the plan, re-dispatch a FRESH feater with plan + decision. - Max 2 decision round-trips → escalate to the user. +- `STATUS : NEED-DECISION` → route on its `CLASS:` per MID-RUN CLARIFICATION + in `$HOME/.claude/lib/contract-interview.md`: visible / public-name / scope + → ask the user, verbatim; internal → decide HERE (max 2 such round-trips + → escalate). Append the answer to the contract `[gated]` and to the plan, + re-dispatch a FRESH feater with plan + decision. - `STATUS : BLOCKED` → surface the blocker to the user, stop. ## STEP 4 — VERIFY + SECURE (fresh gates, bounded loops) From 590482b622618e8b10eba35d5bbcd0731fb881e9 Mon Sep 17 00:00:00 2001 From: Bastien Chanot Date: Wed, 16 Sep 2026 22:13:16 +0200 Subject: [PATCH 06/12] feat(bugfix): pass B at FIX PLAN, NEED-DECISION routed on class --- skills/bugfix/SKILL.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/skills/bugfix/SKILL.md b/skills/bugfix/SKILL.md index 30d3cce..bbd730b 100644 --- a/skills/bugfix/SKILL.md +++ b/skills/bugfix/SKILL.md @@ -116,6 +116,10 @@ RISK: obvious fix. - If the fix is significant (>10 lines, multiple files, behavior change): wait for user approval. +- Then run pass B of `$HOME/.claude/lib/contract-interview.md` against the + FIX PLAN: every VISIBLE / PUBLIC NAME / SCOPE choice it settles that the + bug report left open → one batch of questions, before STEP 3b. The trivial + fast-path is not exempt: a 1-line fix with a visible choice still asks. ## STEP 3b — CHALLENGE THE FIX PLAN (before the contract) Unless the fix is the trivial 1-2 line case STEP 3 already fast-paths, the @@ -135,8 +139,8 @@ the STEP 3 approval gate. Run `$HOME/.claude/lib/contract-interview.md` (main loop). The DIAGNOSIS feeds it: REQUEST verbatim = the bug report as received; ACCEPTANCE CRITERIA = the symptom reproduced-then-gone + a regression test present and passing; -FILE SCOPE = the FIX PLAN files. Questions stay proportional (a clear, -reproduced bug → zero). It writes the contract to +FILE SCOPE = the FIX PLAN files. Pass A only here (pass B ran at STEP 3); a +clear, reproduced bug asks nothing. It writes the contract to `.claude/tasks/contracts/--.md`; keep the path — the executor reads it first and GATE 1 (STEP 6) hands it to a fresh verifier. @@ -162,9 +166,11 @@ ops, no security dispatch. Finish with the BUGFIX-EXEC REPORT." Parse the `BUGFIX-EXEC REPORT`: - `STATUS : DONE` → STEP 6. -- `STATUS : NEED-DECISION` → make the decision HERE (that is reflection), - append it to the plan, re-dispatch a FRESH bugfixer with plan + decision. - Max 2 decision round-trips → escalate to the user. +- `STATUS : NEED-DECISION` → route on its `CLASS:` per MID-RUN CLARIFICATION + in `$HOME/.claude/lib/contract-interview.md`: visible / public-name / scope + → ask the user, verbatim; internal → decide HERE (max 2 such round-trips + → escalate). Append the answer to the contract `[gated]` and to the plan, + re-dispatch a FRESH bugfixer with plan + decision. - `STATUS : BLOCKED` → surface the blocker to the user, stop. ## STEP 6 — VERIFY + SECURE + PRE-COMMIT GATE + COMMIT (main loop, LRN-083) From 97591ca7376e020396b068f94d5f5f2b932a9d5d Mon Sep 17 00:00:00 2001 From: Bastien Chanot Date: Wed, 16 Sep 2026 22:13:32 +0200 Subject: [PATCH 07/12] feat(hotfix): pass B at LOCATE, class-tagged BLOCKED relayed as a question --- lib/tests/loops-light.test.sh | 2 +- skills/hotfix/SKILL.md | 26 +++++++++++++++++++------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/lib/tests/loops-light.test.sh b/lib/tests/loops-light.test.sh index 092cf6c..d5c698f 100644 --- a/lib/tests/loops-light.test.sh +++ b/lib/tests/loops-light.test.sh @@ -81,7 +81,7 @@ tf "hotfixer report grammar" "$HOT" "HOTFIX-EXEC REPORT" echo "── skills/hotfix/SKILL.md (hotfix wiring — revert, not loop) ──" tf "hotfix silent contract" "$HSKL" "STEP 1.7 — CONTRACT (silent autofill)" -tf "hotfix zero questions" "$HSKL" "questions ever" +tf "hotfix pass B at locate" "$HSKL" "run pass B of" tf "hotfix security gate" "$HSKL" "Security gate (fresh auditor)" tf "hotfix block reverts" "$HSKL" "failure REVERTS, never loops" tf "hotfix no verifier" "$HSKL" "No verifier is dispatched at hotfix weight" diff --git a/skills/hotfix/SKILL.md b/skills/hotfix/SKILL.md index 5782223..d08682c 100644 --- a/skills/hotfix/SKILL.md +++ b/skills/hotfix/SKILL.md @@ -47,6 +47,10 @@ git log --oneline -3 as `/bugfix` (root-cause investigation, then a scoped fix)." - Settle the proposed fix HERE — the executor cannot ask questions, so the exact edit (what changes, in which file(s)) must be closed before dispatch. +- Then run pass B of `$HOME/.claude/lib/contract-interview.md` against that + edit: a VISIBLE / PUBLIC NAME / SCOPE choice the bug description leaves + open (which way the icon aligns, the label's wording) → ask before + dispatch. A typo or a wrong value asks nothing. OPTIONAL — memory check (exempt by default; hotfix = obvious fix, mirror of its capitalize skip). For a RECURRING or urgent bug only, a quick blockers-only glance may save time: @@ -66,8 +70,9 @@ Follow `$HOME/.claude/lib/design-gate.md`: ## STEP 1.7 — CONTRACT (silent autofill) -Run `$HOME/.claude/lib/contract-interview.md` at hotfix weight: **zero -questions ever** (a hotfix is an obvious fix by definition). Autofill the +Run `$HOME/.claude/lib/contract-interview.md` at hotfix weight: pass A is a +silent autofill (a hotfix is an obvious fix by definition); pass B already +ran at STEP 1, ask nothing more here. Autofill the contract — REQUEST verbatim = the bug description as given; ACCEPTANCE CRITERIA = "symptom gone; build/tests green"; FILE SCOPE = the 1-2 target files from STEP 1. It writes `.claude/tasks/contracts/--.md`. @@ -135,8 +140,14 @@ security dispatch, no revert. Finish with the HOTFIX-EXEC REPORT." Parse the `HOTFIX-EXEC REPORT`: - `STATUS : DONE` → STEP 4 (the SMOKE line in the report decides pass/fail there; DONE here means execution completed, not that it verified clean). -- `STATUS : BLOCKED` → if any edits were made, revert ONLY the executor's - files: `git restore --source=$PRE -- ` and delete +- `STATUS : BLOCKED` with `CLASS: visible | public-name | scope` in NOTES → + the executor halted at an open choice before editing (nothing to revert): + ask the user per MID-RUN CLARIFICATION in + `$HOME/.claude/lib/contract-interview.md`, append the answer to the + contract `[gated]`, re-dispatch ONCE with the closed choice. This is the + one re-dispatch hotfix allows; it is not a retry of a failed attempt. +- `STATUS : BLOCKED` otherwise → if any edits were made, revert ONLY the + executor's files: `git restore --source=$PRE -- ` and delete any NEW file the report lists (untracked, absent from $PRE). Never `git restore .` — it would wipe the tolerated pre-existing edits too. Surface the blocker to the user; STOP. One attempt only — hotfix never @@ -227,9 +238,10 @@ trivial hotfix still produces a `chore(memory): journal — …` commit (Frame 2 - Reflection (LOCATE, contract, gate decisions) NEVER leaves this main loop; execution NEVER stays in it — the executor is the sonnet-pinned hotfixer subagent (BDR-066). -- The executor is dispatched FRESH, once — hotfix never re-dispatches (no - decision round-trips; a blocked or failed attempt reverts and escalates - to `/bugfix`, it does not retry). +- The executor is dispatched FRESH, once — hotfix never re-dispatches after + a failed or blocked attempt (it reverts and escalates to `/bugfix`, it + does not retry). Sole exception: a class-tagged BLOCKED answered by the + user (STEP 3), re-dispatched once with the closed choice. - Design gate only if CSS/style signals detected. See STEP 1.5. - **Revert-not-loop preserved**: smoke FAIL or security BLOCK → file-scoped revert from `$PRE` (STEP 4's protocol — never `git From a1357a6ab5f258a7664c968255699801f417ea79 Mon Sep 17 00:00:00 2001 From: Bastien Chanot Date: Wed, 16 Sep 2026 22:13:42 +0200 Subject: [PATCH 08/12] feat(ship-feature,init-project): pass B at the plan and design steps --- skills/init-project/SKILL.md | 7 +++++-- skills/ship-feature/SKILL.md | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/skills/init-project/SKILL.md b/skills/init-project/SKILL.md index f9a3655..3e603b2 100644 --- a/skills/init-project/SKILL.md +++ b/skills/init-project/SKILL.md @@ -58,8 +58,8 @@ In both cases: MANDATORY STOP until user answers remaining questions. Produce PR **Then run `$HOME/.claude/lib/contract-interview.md`** seeded from the BRIEF: REQUEST verbatim = the user's project description; ACCEPTANCE CRITERIA = the -V1 FEATURES (each testable); FILE SCOPE = the planned tree. No new questions -(the interview already asked). It writes +V1 FEATURES (each testable); FILE SCOPE = the planned tree. Pass A is covered +by the interview; pass B runs at STEP 3 against the DESIGN. It writes `.claude/tasks/contracts/--.md`; the DESIGN approved at STEP 4 ENRICHES it, and STEP 9's verifier judges the MVP against the enriched contract. @@ -70,6 +70,9 @@ Load `$HOME/.claude/agents/analyzer.md`. Analyze BRIEF: existing code, stack con ## STEP 3 — DESIGN Invoke `superpowers:brainstorming` with BRIEF + ANALYSIS REPORT. Produce DESIGN: stack+versions, full folder tree, module responsibilities, data flow, interfaces (signatures only), config+tooling, test strategy, resolved decisions, prereqs list. +Then run pass B of `$HOME/.claude/lib/contract-interview.md` against the DESIGN +(minus what the BRIEF and the brainstorm settled): one batch before STEP 4; +answers append to the contract `[gated]`. ## STEP 4 — VALIDATION GATE #1 ★ MANDATORY STOP Present: diff --git a/skills/ship-feature/SKILL.md b/skills/ship-feature/SKILL.md index 7c02b18..2803514 100644 --- a/skills/ship-feature/SKILL.md +++ b/skills/ship-feature/SKILL.md @@ -116,6 +116,10 @@ Refine request into validated design via Socratic questioning. Don't proceed unt Invoke `superpowers:writing-plans` with the validated design AND the 0d digest: every task must be consistent with the in-force constraints; where a task implements or affects one, note the ID inline. Break design into tasks (2-5 min each). Each task: exact file paths, full code, verification steps. +Then run pass B of `$HOME/.claude/lib/contract-interview.md` against the plan: +every VISIBLE / PUBLIC NAME / SCOPE choice the plan settles that neither the +request nor the STEP 1 brainstorm settled (check the contract's CLARIFICATIONS +first) → one batch before STEP 2b; answers append to the contract `[gated]`. ## STEP 2b — CHALLENGE THE PLAN (adversarial, before the gate) Before the human sees the plan, harden it. Run `$HOME/.claude/lib/challenge-plan.md`: From 7cc95952bde8648beb9149953c5d66930f6f34eb Mon Sep 17 00:00:00 2001 From: Bastien Chanot Date: Wed, 16 Sep 2026 22:13:50 +0200 Subject: [PATCH 09/12] feat(interviewer): a visible or public choice is asked, never assumed --- agents/interviewer.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/agents/interviewer.md b/agents/interviewer.md index 0321318..b90a1f1 100644 --- a/agents/interviewer.md +++ b/agents/interviewer.md @@ -14,13 +14,13 @@ Gather context. Produce complete PROJECT BRIEF as single source of truth. - If the initial prompt already provides name + purpose + stack + features + architecture → skip questions and generate the BRIEF directly. - Otherwise ask only what's genuinely missing, in a single structured block. - After answers: produce BRIEF. One follow-up allowed if answer is ambiguous. -- Hard budget: 2 question rounds total (initial block + one follow-up). The BRIEF ships after round 2 no matter what — gaps become OPEN DECISIONS, never a third round. +- Hard budget: 2 question rounds total (initial block + one follow-up) for gaps. The BRIEF ships after round 2 — gaps become OPEN DECISIONS. Sole exception: a VISIBLE, PUBLIC NAME or SCOPE choice (a user-facing placement or wording, a public command/flag/endpoint name, whether X is in scope) still open after round 2 gets ONE more targeted question; it never ships as `(assumed)`. ## FAILURE MODES | Trigger | First response | If still unresolved | |---|---|---| -| Answer vague/ambiguous | One targeted follow-up on that item only | Record item in OPEN DECISIONS with the safest reading, marked `(assumed)` — never invent a confident value | +| Answer vague/ambiguous | One targeted follow-up on that item only | Gap: record it in OPEN DECISIONS with the safest reading, marked `(assumed)` — never invent a confident value. Visible / public-name / scope item: one more targeted question instead, never `(assumed)` | | "I don't know / you decide" | Propose ONE concrete default + why, ask yes/no | Take the default, mark `(assumed)`, list in OPEN DECISIONS | | Contradictory answers (e.g. embedded runtime + managed cloud DB) | Name the contradiction, ask which side wins | Put BOTH options in OPEN DECISIONS; do not silently pick one | | Partial answer to the block | Re-ask ONLY the missing items in the follow-up round | Missing fields → `none stated` + OPEN DECISIONS entry | @@ -77,6 +77,6 @@ Stop after BRIEF. Orchestrator handles next step. - Design, architect, or implement anything — the BRIEF is the entire deliverable. - Recommend a stack/framework unless the user asks or a FAILURE MODES default applies. - Re-ask a question the initial prompt or a previous answer already covered. -- Exceed the 2-round budget, whatever is still missing. +- Exceed the 2-round budget for gaps; the only extra question is the single targeted one a visible / public-name / scope item earns. - Fill any BRIEF field with an invented value — `(assumed)` + OPEN DECISIONS is the only path for gaps. - Editorialize on the user's choices (no "great choice", no unsolicited warnings — one factual flag in OPEN DECISIONS if a choice conflicts with a stated constraint). From 17370d7e4ca55af9d0c1dc0b9ba3d7d3deea3677 Mon Sep 17 00:00:00 2001 From: Bastien Chanot Date: Wed, 16 Sep 2026 22:14:13 +0200 Subject: [PATCH 10/12] feat(executors): NEED-DECISION and BLOCKED carry a CLASS tag --- agents/bugfixer.md | 8 +++++--- agents/feater.md | 8 +++++--- agents/hotfixer.md | 7 ++++++- lib/tests/gates.test.sh | 3 +++ 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/agents/bugfixer.md b/agents/bugfixer.md index f24cd33..cbff813 100644 --- a/agents/bugfixer.md +++ b/agents/bugfixer.md @@ -27,8 +27,9 @@ Every choice was made in the plan or is a NEED-DECISION to report. - Apply the FIX PLAN to the letter — fix the ROOT CAUSE named in DIAGNOSIS, not the symptom. A plan hole or an open choice (naming, data shape, API - surface, dependency) → STOP, report `NEED-DECISION` with the precise - question. Never re-investigate or improvise a different fix. + surface, dependency, a user-visible choice such as placement, wording or + behavior) → STOP, report `NEED-DECISION` with the precise question and + its `CLASS:`. Never re-investigate or improvise a different fix. - Stay inside the contract FILE SCOPE. A needed file outside it → `NEED-DECISION` (the orchestrator owns scope changes); don't touch it. - Add or update the regression test the plan names — it must fail before the @@ -73,5 +74,6 @@ FILE(S) : TEST(S) : SMOKE : NOTES : + question + the options you see + CLASS: visible | public-name | + scope | internal | BLOCKED: the blocker verbatim> ``` diff --git a/agents/feater.md b/agents/feater.md index 1346f59..6144fdd 100644 --- a/agents/feater.md +++ b/agents/feater.md @@ -37,8 +37,9 @@ report below is optional on this path (the dispatcher needs the edit applied ## EXECUTION RULES - Follow the plan to the letter. A plan hole or an open choice (naming, - data shape, API surface, dependency) → STOP, report `NEED-DECISION` with - the precise question. Never improvise a design decision. + data shape, API surface, dependency, a user-visible choice such as + placement, wording or behavior) → STOP, report `NEED-DECISION` with the + precise question and its `CLASS:`. Never improvise a design decision. - Stay inside the contract FILE SCOPE. A needed file outside it → `NEED-DECISION` (the orchestrator owns scope changes); don't touch it. On the applier path the scope is the files named in the bundle item — apply @@ -84,5 +85,6 @@ STATUS : DONE | NEED-DECISION | BLOCKED FILES : TESTS : NOTES : + question + the options you see + CLASS: visible | public-name | + scope | internal | BLOCKED: the blocker verbatim> ``` diff --git a/agents/hotfixer.md b/agents/hotfixer.md index 89843fe..d766591 100644 --- a/agents/hotfixer.md +++ b/agents/hotfixer.md @@ -43,6 +43,10 @@ the edit applied + self-verified, not the report grammar). BLOCKED`, report why (the orchestrator escalates to `/bugfix`), never expand scope yourself. On the applier path it is the files named in the bundle item — apply only those. +- An open user-visible choice the contract does not settle (placement, + wording, behavior) → `STATUS BLOCKED` with `CLASS: visible | public-name | + scope` in NOTES, BEFORE editing anything. The orchestrator asks the user + and re-dispatches once. - If tests exist for the affected code, run them. Detection cascade: ```bash # JS/TS @@ -78,5 +82,6 @@ STATUS : DONE | BLOCKED FILE(S) : FIX : SMOKE : -NOTES : +NOTES : ``` diff --git a/lib/tests/gates.test.sh b/lib/tests/gates.test.sh index 76d5fa7..efdbb0a 100644 --- a/lib/tests/gates.test.sh +++ b/lib/tests/gates.test.sh @@ -311,6 +311,9 @@ lock "bugfixer passes" "$BF" "## FOUR PASSES" lock "bugfixer stays minimal" "$BF" "keep the fix minimal" lock "bugfixer neg control" "$BF" "**Negative control.**" lock "bugfixer test must fail" "$BF" "A test that passes both ways" +lock "feater class tag" "$FE" "CLASS:" +lock "bugfixer class tag" "$BF" "CLASS:" +lock "hotfixer class tag" "$REPO/agents/hotfixer.md" "CLASS:" echo "" echo "gates: $PASS pass, $FAIL fail" From 22ce57f323081d36017928f331707b87e719b66b Mon Sep 17 00:00:00 2001 From: Bastien Chanot Date: Wed, 16 Sep 2026 22:15:48 +0200 Subject: [PATCH 11/12] docs(changelog): ask-don't-guess doctrine --- .claude/tasks/TODO.md | 18 +++++++++--------- CHANGELOG.md | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/.claude/tasks/TODO.md b/.claude/tasks/TODO.md index 927a12a..cadf255 100644 --- a/.claude/tasks/TODO.md +++ b/.claude/tasks/TODO.md @@ -11,19 +11,19 @@ rule changes for all work, hotfix included; classes VISIBLE / PUBLIC NAME / SCOPE ask, internal technical choices never. Spec: `docs/superpowers/specs/2026-09-16-ask-dont-guess-design.md`; plan: `docs/superpowers/plans/2026-09-16-ask-dont-guess.md` (9 tasks, lock-first). -- [ ] P1 `lib/contract-interview.md` — STEP 2 CLARIFY (pass A gaps, pass B +- [x] P1 `lib/contract-interview.md` — STEP 2 CLARIFY (pass A gaps, pass B open choices), MID-RUN CLARIFICATION, HOW TO ASK; 9 locks in `contract-verifier.test.sh` -- [ ] P2 `CLAUDE.global.md:51-55` — "Ask rather than guess" replaces the +- [x] P2 `CLAUDE.global.md:51-55` — "Ask rather than guess" replaces the one-question rule; bug line reconciled -- [ ] P3 `skills/feat/SKILL.md` — pass B at STEP 1, NEED-DECISION routed on class -- [ ] P4 `skills/bugfix/SKILL.md` — pass B at STEP 3, NEED-DECISION routed on class -- [ ] P5 `skills/hotfix/SKILL.md` — pass B at LOCATE, tagged BLOCKED relayed; +- [x] P3 `skills/feat/SKILL.md` — pass B at STEP 1, NEED-DECISION routed on class +- [x] P4 `skills/bugfix/SKILL.md` — pass B at STEP 3, NEED-DECISION routed on class +- [x] P5 `skills/hotfix/SKILL.md` — pass B at LOCATE, tagged BLOCKED relayed; lock `loops-light.test.sh:84` -- [ ] P6 `skills/ship-feature` STEP 2 + `skills/init-project` contract §/STEP 3 -- [ ] P7 `agents/interviewer.md` — visible/public/scope item never `(assumed)` -- [ ] P8 `agents/{feater,bugfixer,hotfixer}.md` — CLASS tag; 3 locks in `gates.test.sh` -- [ ] P9 `make test`, CHANGELOG, TODO tick; manual behavioral check before merge +- [x] P6 `skills/ship-feature` STEP 2 + `skills/init-project` contract §/STEP 3 +- [x] P7 `agents/interviewer.md` — visible/public/scope item never `(assumed)` +- [x] P8 `agents/{feater,bugfixer,hotfixer}.md` — CLASS tag; 3 locks in `gates.test.sh` +- [x] P9 `make test` green (2026-09-16), CHANGELOG, TODO tick; manual behavioral check still OPEN before merge - [ ] P10 registries at capitalize: BDR (supersedes the one-question rule), LRN (taste is invisible to a gap-only trigger; fresh re-dispatch cost favors plan-time questions) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ff4bbf..b82c69b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,25 @@ Format follows [Keep a Changelog](https://keepachangelog.com/). destructive command under auto mode. ### Changed +- **Ask, don't guess: the orchestrators ask about open choices instead of + settling them.** `CLAUDE.global.md` replaces "one question upfront, never + mid-task" with: a choice visible in the result, a name that becomes + public, or a scope the request does not settle → ask, even mid-task; + internal technical choices stay Claude's. `lib/contract-interview.md` + STEP 2 becomes CLARIFY: pass A (the three gap checks, at contract time) + and pass B (the open-choice sweep in three classes, run once at each + flow's PLAN step, no question cap, over-5 guard, "you decide" recorded as + delegated). New MID-RUN CLARIFICATION section: an executor's + `NEED-DECISION` carries a `CLASS:` tag; visible / public-name / scope go + to the user verbatim, internal is decided in the loop; answers land in + the contract `[gated]`. New HOW TO ASK section (LRN-102). `/feat`, + `/bugfix`, `/hotfix`, `/ship-feature`, `/init-project` wire pass B at + their plan step; `/feat` and `/bugfix` stop deciding `NEED-DECISION` + themselves; `/hotfix` drops "zero questions ever" and allows one + re-dispatch for a class-tagged BLOCKED; the interviewer never ships a + visible / public-name / scope item as `(assumed)`; feater, bugfixer and + hotfixer report the class. Locks updated in the `contract-verifier`, + `loops-light` and `gates` tests. - **The classifier, not `permissions.ask`, now guards destructive shell work** (BDR-090). Ten rules left the static tiers: `rsync`, `kill -9`, `killall`, `pkill` out of `deny`, and `python3 -c`, `python -c`, From cd98bfafe4c607a44bbdf42b0cabbfd78dd5b829 Mon Sep 17 00:00:00 2001 From: Bastien Chanot Date: Thu, 17 Sep 2026 11:41:39 +0200 Subject: [PATCH 12/12] chore: purge transient planning artifacts (BDR-065) --- .../plans/2026-09-16-ask-dont-guess.md | 709 ------------------ .../specs/2026-09-16-ask-dont-guess-design.md | 221 ------ 2 files changed, 930 deletions(-) delete mode 100644 docs/superpowers/plans/2026-09-16-ask-dont-guess.md delete mode 100644 docs/superpowers/specs/2026-09-16-ask-dont-guess-design.md diff --git a/docs/superpowers/plans/2026-09-16-ask-dont-guess.md b/docs/superpowers/plans/2026-09-16-ask-dont-guess.md deleted file mode 100644 index c632bcd..0000000 --- a/docs/superpowers/plans/2026-09-16-ask-dont-guess.md +++ /dev/null @@ -1,709 +0,0 @@ -# Ask, don't guess — Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** The orchestrators ask the user about every open VISIBLE / PUBLIC NAME / SCOPE choice, upfront at plan time and mid-run through the executor's `NEED-DECISION`, instead of settling it themselves. - -**Architecture:** One shared doctrine file (`lib/contract-interview.md`) gains a two-pass STEP 2 (gaps, then open choices), a MID-RUN CLARIFICATION channel and a HOW TO ASK section. The parent rule in `CLAUDE.global.md` changes. Each orchestrator wires pass B at its plan step with one line and routes `NEED-DECISION` on a `CLASS:` tag the executors now emit. Structure-lock tests are the reviewer: every doctrine change is preceded by its lock. - -**Tech Stack:** Markdown doctrine files, bash structure-lock tests (`lib/tests/*.test.sh`, `make test`), gitflow on `feature/ask-dont-guess`. - -**Spec:** `docs/superpowers/specs/2026-09-16-ask-dont-guess-design.md` - -## Global Constraints - -- A locked phrase must sit on ONE unbroken line in the doctrine file (learnings.md:1386). Reflow around it, never through it. -- `lib/tests/gates.test.sh` oracle locks on `contract-interview.md` (`### ORACLES`, `Both attributes or neither`, `url-guard.sh`, `**ABANDONMENT**`, `NEVER deleted`…) must keep passing: the ORACLES section and the Lifecycle section do not move. -- Skill and agent files keep their house style (`→`, `—`, bold markers). The writing-style rule applies to user-facing prose, not to these templates. -- No attribution trailers in commits. Commit on `feature/ask-dont-guess` only; never `finish`. -- `CLAUDE.global.md` stays under the 320-line density budget (308 lines today). -- Every task ends with the relevant test file green, then a commit. - ---- - -### Task 1: Shared doctrine — `lib/contract-interview.md` - -**Files:** -- Modify: `lib/contract-interview.md` (intro §, STEP 2, template CLARIFICATIONS line, new sections before `## Lifecycle`, weight table hotfix row) -- Test: `lib/tests/contract-verifier.test.sh:51-52` - -**Interfaces:** -- Produces: section names `## STEP 2 — CLARIFY`, `## MID-RUN CLARIFICATION`, `## HOW TO ASK`, the phrase "pass B", the tag grammar `CLASS: visible | public-name | scope | internal`. Every later task points at these by name. - -- [ ] **Step 1: Replace the two obsolete locks and add nine** - -In `lib/tests/contract-verifier.test.sh`, replace: -``` -tf "silent when complete" "$LIB" "ZERO questions" -tf "question budget" "$LIB" "max 3 questions" -``` -with: -``` -tf "silent when nothing open" "$LIB" "goes through silently" -tf "no question cap" "$LIB" "No question cap" -tf "pass B classes" "$LIB" "PUBLIC NAME" -tf "class 4 excluded" "$LIB" "NEVER ask class 4" -tf "over-5 guard" "$LIB" "More than 5 open choices" -tf "delegated answer" "$LIB" "delegated —" -tf "mid-run channel" "$LIB" "## MID-RUN CLARIFICATION" -tf "class tag" "$LIB" "CLASS:" -tf "how to ask" "$LIB" "## HOW TO ASK" -``` - -- [ ] **Step 2: Run the lock test, expect the nine new locks red** - -Run: `bash lib/tests/contract-verifier.test.sh 2>&1 | grep -E "FAIL"` -Expected: nine `FAIL` lines (silent when nothing open … how to ask); the verifier.md locks stay PASS. - -- [ ] **Step 3: Edit the intro paragraph** - -Old: -``` -Run this in the ORCHESTRATOR MAIN LOOP, never in a subagent — STEP 2 may -talk to the human. Mandatory passage in every flow; questions are optional -and proportional — a complete request goes through silently. -``` -New: -``` -Run this in the ORCHESTRATOR MAIN LOOP, never in a subagent — STEP 2 may -talk to the human, at contract time (pass A) and again at the flow's PLAN -step (pass B). Questions follow the open choices, never a quota — a complete -request goes through silently. -``` - -- [ ] **Step 4: Replace STEP 2 entirely** - -Old (from `## STEP 2 — AMBIGUITY CHECK` through `verify paths/APIs/behavior yourself first.`): -``` -## STEP 2 — AMBIGUITY CHECK (questions optional, proportional) - -Ask ONLY if one of these is missing AND not derivable from the repo: -- a testable expected outcome -- an unambiguous scope (what is allowed to change) -- non-contradictory constraints - -Complete request → ZERO questions, stay silent. Otherwise: max 3 questions, -one single batch (house rule: one question upfront, never mid-task). Never -ask what the repo can answer — verify paths/APIs/behavior yourself first. -``` -New: -``` -## STEP 2 — CLARIFY (ask, never guess) - -Two passes, both in the main loop, both may talk to the human. - -**Pass A — gaps.** Run here, against the request. Ask if one of these is -missing AND not derivable from the repo: -- a testable expected outcome -- an unambiguous scope (what is allowed to change) -- non-contradictory constraints - -**Pass B — open choices.** Defined here, run ONCE at the flow's PLAN step -(see "Where pass B fires" below), against the plan just written — that is -where choices become concrete. Enumerate every choice the run will settle -that the request leaves open; keep those in these classes: -1. VISIBLE — the user would see it in the result: placement, label, wording, - color, order, what a click does. -2. PUBLIC NAME — a name that outlives the run: command, flag, endpoint, env - var, a file the human will read. -3. SCOPE — "should X change too?", where the request does not name X. - -NEVER ask class 4 — internal technical choices with no observable effect -(function decomposition, data shape, local naming, layout inside an -already-scoped zone). Those are delegated; asking them is the noise that -makes classes 1-3 ignorable. Never ask what the repo or the request already -answers — verify paths/APIs/behavior yourself first. - -No question cap. Each pass asks what it finds, in ONE batch. A request that -leaves nothing open goes through silently. More than 5 open choices in pass B -= the request is under-specified: list them, say so, stop — do not fire a -questionnaire. "You decide" / "peu importe" is an answer: record it as -`A: delegated — ` and never re-ask it. - -Pass B answers land in the contract's CLARIFICATIONS marked -`[gated ]` — the contract is already on disk by then. - -### Where pass B fires - -| Flow | Pass B runs at | Against | -|------|----------------|---------| -| feat | STEP 1 PLAN, before 1b CHALLENGE | the PLAN checklist | -| bugfix | STEP 3 FIX PLAN, before 3b | the FIX PLAN | -| hotfix | STEP 1 LOCATE | the 1-2 target files' visible effect | -| ship-feature | STEP 2 PLAN, after the brainstorm | the plan, minus what the brainstorm settled | -| init-project | STEP 3 DESIGN, before VALIDATION GATE #1 | the DESIGN, minus what the interview and brainstorm settled | -| onboard | its STEP 3 interview, unchanged | scope, in one block | -``` - -- [ ] **Step 5: Mark gated entries in the template** - -Old: `Q: / A: ` -New: `Q: / A: (pass B and mid-run entries: [gated ])` - -- [ ] **Step 6: Insert the two new sections right before `## Lifecycle`** - -``` -## MID-RUN CLARIFICATION (the channel executors halt into) - -An executor cannot talk to the human. It halts with `NEED-DECISION`, the -exact question, the options it sees, and a `CLASS:` tag (visible | -public-name | scope | internal). `/hotfix`: the hotfixer keeps -`DONE | BLOCKED`; a BLOCKED carrying the tag follows the same routing instead -of escalating to `/bugfix`. The orchestrator re-reads the class — the tag is -a hint, not a verdict — then routes: -- visible / public-name / scope → ASK THE HUMAN, verbatim question and - options. Never decide these yourself, never spend a round-trip guessing. -- internal → decide here, note the decision, re-dispatch. The only case the - orchestrator settles alone; max 2 such round-trips → escalate. - -Every answer, human or orchestrator, appends to the contract's -CLARIFICATIONS marked `[gated ]` — the same micro-gate as scope -enrichment — and to the plan handed to the FRESH re-dispatched executor, -which reads the decision from disk, never from a transcript. - -## HOW TO ASK (LRN-102) - -The harness reliably renders only the turn's FINAL text; text printed before -a tool call may be swallowed. So: -- up to 4 questions → one `AskUserQuestion` call; option descriptions carry - the context; print nothing the user needs before the call. -- more than 4, or a list handed back for re-specification → plain text, end - the turn. - -``` - -- [ ] **Step 7: Rewrite the hotfix row of the weight table** - -Old: -``` -| hotfix | Silent autofill — criteria: "symptom gone; build/tests green"; scope = the 1-2 target files. Zero questions ever. | -``` -New: -``` -| hotfix | Pass A silent autofill — criteria: "symptom gone; build/tests green"; scope = the 1-2 target files. Pass B runs at LOCATE against the 1-2 target files' visible effect; a typo fix asks nothing. | -``` - -- [ ] **Step 8: Run both lock suites, expect green** - -Run: `bash lib/tests/contract-verifier.test.sh 2>&1 | grep -E "FAIL|PASS="; bash lib/tests/gates.test.sh 2>&1 | grep -E "FAIL|bad|PASS=|ok=" | tail -3` -Expected: no `FAIL` line in contract-verifier; gates oracle locks all `ok`. - -- [ ] **Step 9: Commit** - -```bash -git add lib/contract-interview.md lib/tests/contract-verifier.test.sh -git commit -m "feat(contract): STEP 2 CLARIFY, mid-run channel, how-to-ask" -``` - ---- - -### Task 2: Parent rule — `CLAUDE.global.md` - -**Files:** -- Modify: `CLAUDE.global.md:51-55` -- Test: `wc -l CLAUDE.global.md` (≤ 320) and `bash lib/tests/curated-config-guard.test.sh` - -**Interfaces:** -- Produces: the sentence "Ask rather than guess." as the house rule every skill inherits. - -- [ ] **Step 1: Replace lines 51-53** - -Old: -``` -- One question upfront if needed — don't interrupt mid-task. - *Exception: skill-mandated gates and checkpoints (orchestrator - validation gates, approval gates, darwin checkpoints) always fire.* -``` -New: -``` -- Ask rather than guess. A choice visible in the result (placement, - wording, order, behavior), a name that becomes public (command, flag, - endpoint, file), or a scope the request does not settle → ask, even - mid-task. Batch what can be batched. Internal technical choices with - no observable effect stay yours. - *Exception: skill-mandated gates and checkpoints (orchestrator - validation gates, approval gates, darwin checkpoints) always fire.* -``` - -- [ ] **Step 2: Reconcile the bug line** - -Old: -``` -- Bug received → fix directly: check logs, find root cause, resolve - autonomously. -``` -New: -``` -- Bug received → fix directly: check logs, find root cause, resolve - autonomously; a visible choice in the fix still gets asked. -``` - -- [ ] **Step 3: Verify budget and guard** - -Run: `wc -l CLAUDE.global.md; grep -c "One question upfront" CLAUDE.global.md lib/contract-interview.md; bash lib/tests/curated-config-guard.test.sh 2>&1 | tail -2` -Expected: ≤ 320 lines; both grep counts `0`; guard test green. - -- [ ] **Step 4: Commit** - -```bash -git add CLAUDE.global.md -git commit -m "feat(rules): ask rather than guess replaces one-question-upfront" -``` - ---- - -### Task 3: `/feat` wiring - -**Files:** -- Modify: `skills/feat/SKILL.md` (STEP 0.7 §, STEP 1 tail, STEP 1b tail, STEP 3 parse) -- Test: `bash lib/tests/loops-light.test.sh` - -**Interfaces:** -- Consumes: `pass B`, `MID-RUN CLARIFICATION`, `CLASS:` from Task 1. - -- [ ] **Step 1: STEP 0.7 wording** - -Old: -``` -captures the request verbatim, asks 0-3 questions PROPORTIONAL to ambiguity -(a complete request → zero questions, silent), derives testable acceptance -criteria + file scope, and writes the contract to -``` -New: -``` -captures the request verbatim, runs pass A (gaps: outcome, scope, -constraints — a complete request goes through silently), derives testable -acceptance criteria + file scope, and writes the contract to -``` - -- [ ] **Step 2: STEP 1 tail — replace the one-question rule with pass B** - -Old: -``` -If the approach is ambiguous: ask the user ONE focused question BEFORE -dispatching — never after (the executor cannot relay questions). -``` -New: -``` -Then run pass B of `$HOME/.claude/lib/contract-interview.md` against this -plan: every VISIBLE / PUBLIC NAME / SCOPE choice the plan settles that the -request left open → one batch of questions BEFORE dispatching; answers land -in the contract's CLARIFICATIONS `[gated]` and in the plan. A choice that -surfaces only during execution comes back as `NEED-DECISION` (STEP 3). -``` - -- [ ] **Step 3: STEP 1b tail** - -Old: `surfacing any deferred BLOCKER via\nSTEP 1's one-question gate.` -New: `surfacing any deferred BLOCKER in\nthe STEP 1 pass B batch.` - -- [ ] **Step 4: STEP 3 parse** - -Old: -``` -- `STATUS : NEED-DECISION` → make the decision HERE (that is reflection), - append it to the plan, re-dispatch a FRESH feater with plan + decision. - Max 2 decision round-trips → escalate to the user. -``` -New: -``` -- `STATUS : NEED-DECISION` → route on its `CLASS:` per MID-RUN CLARIFICATION - in `$HOME/.claude/lib/contract-interview.md`: visible / public-name / scope - → ask the user, verbatim; internal → decide HERE (max 2 such round-trips - → escalate). Append the answer to the contract `[gated]` and to the plan, - re-dispatch a FRESH feater with plan + decision. -``` - -- [ ] **Step 5: Test and commit** - -Run: `grep -n "ONE focused question\|decision HERE (that is reflection)" skills/feat/SKILL.md; bash lib/tests/loops-light.test.sh 2>&1 | grep -E "FAIL|PASS="` -Expected: grep empty; `FAIL=0`. -```bash -git add skills/feat/SKILL.md -git commit -m "feat(feat): pass B at PLAN, NEED-DECISION routed on class" -``` - ---- - -### Task 4: `/bugfix` wiring - -**Files:** -- Modify: `skills/bugfix/SKILL.md` (STEP 3 bullets, STEP 3.5 §, STEP 5 parse) -- Test: `bash lib/tests/loops-light.test.sh` - -- [ ] **Step 1: STEP 3 — add pass B after the approval bullets** - -After: -``` -- If the fix is significant (>10 lines, multiple files, - behavior change): wait for user approval. -``` -add: -``` -- Then run pass B of `$HOME/.claude/lib/contract-interview.md` against the - FIX PLAN: every VISIBLE / PUBLIC NAME / SCOPE choice it settles that the - bug report left open → one batch of questions, before STEP 3b. The trivial - fast-path is not exempt: a 1-line fix with a visible choice still asks. -``` - -- [ ] **Step 2: STEP 3.5 wording** - -Old: `Questions stay proportional (a clear,\nreproduced bug → zero). It writes the contract to` -New: `Pass A only here (pass B ran at STEP 3); a clear,\nreproduced bug asks nothing. It writes the contract to` - -- [ ] **Step 3: STEP 5 parse** - -Old: -``` -- `STATUS : NEED-DECISION` → make the decision HERE (that is reflection), - append it to the plan, re-dispatch a FRESH bugfixer with plan + decision. - Max 2 decision round-trips → escalate to the user. -``` -New: -``` -- `STATUS : NEED-DECISION` → route on its `CLASS:` per MID-RUN CLARIFICATION - in `$HOME/.claude/lib/contract-interview.md`: visible / public-name / scope - → ask the user, verbatim; internal → decide HERE (max 2 such round-trips - → escalate). Append the answer to the contract `[gated]` and to the plan, - re-dispatch a FRESH bugfixer with plan + decision. -``` - -- [ ] **Step 4: Test and commit** - -Run: `grep -n "decision HERE (that is reflection)\|stay proportional" skills/bugfix/SKILL.md; bash lib/tests/loops-light.test.sh 2>&1 | grep -E "FAIL|PASS="` -Expected: grep empty; `FAIL=0`. -```bash -git add skills/bugfix/SKILL.md -git commit -m "feat(bugfix): pass B at FIX PLAN, NEED-DECISION routed on class" -``` - ---- - -### Task 5: `/hotfix` wiring (lock first) - -**Files:** -- Modify: `skills/hotfix/SKILL.md` (STEP 1 bullet, STEP 1.7 §, STEP 3 parse, RULES) -- Test: `lib/tests/loops-light.test.sh:84` - -- [ ] **Step 1: Replace the lock** - -Old: `tf "hotfix zero questions" "$HSKL" "questions ever"` -New: `tf "hotfix pass B at locate" "$HSKL" "run pass B of"` - -- [ ] **Step 2: Run, expect red** - -Run: `bash lib/tests/loops-light.test.sh 2>&1 | grep -E "FAIL"` -Expected: one FAIL, `hotfix pass B at locate`. - -- [ ] **Step 3: STEP 1 — add pass B after the "Settle the proposed fix HERE" bullet** - -After: -``` -- Settle the proposed fix HERE — the executor cannot ask questions, so the - exact edit (what changes, in which file(s)) must be closed before dispatch. -``` -add: -``` -- Then run pass B of `$HOME/.claude/lib/contract-interview.md` against that - edit: a VISIBLE / PUBLIC NAME / SCOPE choice the bug description leaves - open (which way the icon aligns, the label's wording) → ask before - dispatch. A typo or a wrong value asks nothing. -``` - -- [ ] **Step 4: STEP 1.7 wording** - -Old: -``` -Run `$HOME/.claude/lib/contract-interview.md` at hotfix weight: **zero -questions ever** (a hotfix is an obvious fix by definition). Autofill the -``` -New: -``` -Run `$HOME/.claude/lib/contract-interview.md` at hotfix weight: pass A is a -silent autofill (a hotfix is an obvious fix by definition); pass B already -ran at STEP 1, ask nothing more here. Autofill the -``` - -- [ ] **Step 5: STEP 3 parse — a tagged BLOCKED is a question, not a revert** - -Insert before the existing `- \`STATUS : BLOCKED\` → if any edits were made, revert ONLY the executor's` bullet: -``` -- `STATUS : BLOCKED` with `CLASS: visible | public-name | scope` in NOTES → - the executor halted at an open choice before editing (nothing to revert): - ask the user per MID-RUN CLARIFICATION in - `$HOME/.claude/lib/contract-interview.md`, append the answer to the - contract `[gated]`, re-dispatch ONCE with the closed choice. This is the - one re-dispatch hotfix allows; it is not a retry of a failed attempt. -``` -and change the following bullet's head from `- \`STATUS : BLOCKED\` → if any edits were made,` to `- \`STATUS : BLOCKED\` otherwise → if any edits were made,`. - -- [ ] **Step 6: RULES** - -Old: -``` -- The executor is dispatched FRESH, once — hotfix never re-dispatches (no - decision round-trips; a blocked or failed attempt reverts and escalates - to `/bugfix`, it does not retry). -``` -New: -``` -- The executor is dispatched FRESH, once — hotfix never re-dispatches after - a failed or blocked attempt (it reverts and escalates to `/bugfix`, it - does not retry). Sole exception: a class-tagged BLOCKED answered by the - user (STEP 3), re-dispatched once with the closed choice. -``` - -- [ ] **Step 7: Run, expect green, commit** - -Run: `grep -n "questions ever" skills/hotfix/SKILL.md; bash lib/tests/loops-light.test.sh 2>&1 | grep -E "FAIL|PASS="` -Expected: grep empty; `FAIL=0`. -```bash -git add skills/hotfix/SKILL.md lib/tests/loops-light.test.sh -git commit -m "feat(hotfix): pass B at LOCATE, class-tagged BLOCKED relayed as a question" -``` - ---- - -### Task 6: `/ship-feature` and `/init-project` wiring - -**Files:** -- Modify: `skills/ship-feature/SKILL.md` (STEP 2), `skills/init-project/SKILL.md` (contract §, STEP 3) -- Test: `bash lib/tests/loops-heavy.test.sh` - -- [ ] **Step 1: ship-feature STEP 2** - -After: -``` -note the ID inline. Break design into tasks (2-5 min each). Each task: exact file paths, full code, verification steps. -``` -add: -``` -Then run pass B of `$HOME/.claude/lib/contract-interview.md` against the plan: -every VISIBLE / PUBLIC NAME / SCOPE choice the plan settles that neither the -request nor the STEP 1 brainstorm settled (check the contract's CLARIFICATIONS -first) → one batch before STEP 2b; answers append to the contract `[gated]`. -``` - -- [ ] **Step 2: init-project contract paragraph** - -Old: `FILE SCOPE = the planned tree. No new questions\n(the interview already asked). It writes` -New: `FILE SCOPE = the planned tree. Pass A is covered by\nthe interview; pass B runs at STEP 3 against the DESIGN. It writes` - -- [ ] **Step 3: init-project STEP 3** - -After the line ending `test strategy, resolved decisions, prereqs list.` add: -``` -Then run pass B of `$HOME/.claude/lib/contract-interview.md` against the DESIGN -(minus what the BRIEF and the brainstorm settled): one batch before STEP 4; -answers append to the contract `[gated]`. -``` - -- [ ] **Step 4: Test and commit** - -Run: `grep -n "No new questions" skills/init-project/SKILL.md; bash lib/tests/loops-heavy.test.sh 2>&1 | grep -E "FAIL|PASS="` -Expected: grep empty; `FAIL=0`. -```bash -git add skills/ship-feature/SKILL.md skills/init-project/SKILL.md -git commit -m "feat(ship-feature,init-project): pass B at the plan and design steps" -``` - ---- - -### Task 7: interviewer — no `(assumed)` on a visible choice - -**Files:** -- Modify: `agents/interviewer.md:17,23,80` -- Test: `bash lib/tests/run-review-guards.sh` (G3 strict-YAML frontmatter) - -- [ ] **Step 1: Budget line (17)** - -Old: -``` -- Hard budget: 2 question rounds total (initial block + one follow-up). The BRIEF ships after round 2 no matter what — gaps become OPEN DECISIONS, never a third round. -``` -New: -``` -- Hard budget: 2 question rounds total (initial block + one follow-up) for gaps. The BRIEF ships after round 2 — gaps become OPEN DECISIONS. Sole exception: a VISIBLE, PUBLIC NAME or SCOPE choice (a user-facing placement or wording, a public command/flag/endpoint name, whether X is in scope) still open after round 2 gets ONE more targeted question; it never ships as `(assumed)`. -``` - -- [ ] **Step 2: Failure-mode row (23)** - -Old: -``` -| Answer vague/ambiguous | One targeted follow-up on that item only | Record item in OPEN DECISIONS with the safest reading, marked `(assumed)` — never invent a confident value | -``` -New: -``` -| Answer vague/ambiguous | One targeted follow-up on that item only | Gap: record it in OPEN DECISIONS with the safest reading, marked `(assumed)` — never invent a confident value. Visible / public-name / scope item: one more targeted question instead, never `(assumed)` | -``` - -- [ ] **Step 3: DO NOT line (80)** - -Old: `- Exceed the 2-round budget, whatever is still missing.` -New: `- Exceed the 2-round budget for gaps; the only extra question is the single targeted one a visible / public-name / scope item earns.` - -- [ ] **Step 4: Test and commit** - -Run: `bash lib/tests/run-review-guards.sh 2>&1 | grep -E "G3|RED"` -Expected: `GREEN ✓ G3`, no RED. -```bash -git add agents/interviewer.md -git commit -m "feat(interviewer): a visible or public choice is asked, never assumed" -``` - ---- - -### Task 8: Executors emit the class (lock first) - -**Files:** -- Modify: `agents/feater.md` (halt bullet, NOTES), `agents/bugfixer.md` (halt bullet, NOTES), `agents/hotfixer.md` (new rule bullet, NOTES) -- Test: `lib/tests/gates.test.sh` after line 313 - -- [ ] **Step 1: Add the locks** - -After `lock "bugfixer test must fail" "$BF" "A test that passes both ways"` add: -``` -lock "feater class tag" "$FE" "CLASS:" -lock "bugfixer class tag" "$BF" "CLASS:" -lock "hotfixer class tag" "$REPO/agents/hotfixer.md" "CLASS:" -``` - -- [ ] **Step 2: Run, expect three red** - -Run: `bash lib/tests/gates.test.sh 2>&1 | grep -E "bad|FAIL" | head` -Expected: three `bad` lines (class tag). - -- [ ] **Step 3: feater halt bullet** - -Old: -``` -- Follow the plan to the letter. A plan hole or an open choice (naming, - data shape, API surface, dependency) → STOP, report `NEED-DECISION` with - the precise question. Never improvise a design decision. -``` -New: -``` -- Follow the plan to the letter. A plan hole or an open choice (naming, - data shape, API surface, dependency, a user-visible choice such as - placement, wording or behavior) → STOP, report `NEED-DECISION` with the - precise question and its `CLASS:`. Never improvise a design decision. -``` - -- [ ] **Step 4: feater NOTES** - -Old: -``` -NOTES : -``` -New: -``` -NOTES : -``` - -- [ ] **Step 5: bugfixer halt bullet** - -Old: -``` -- Apply the FIX PLAN to the letter — fix the ROOT CAUSE named in DIAGNOSIS, - not the symptom. A plan hole or an open choice (naming, data shape, API - surface, dependency) → STOP, report `NEED-DECISION` with the precise - question. Never re-investigate or improvise a different fix. -``` -New: -``` -- Apply the FIX PLAN to the letter — fix the ROOT CAUSE named in DIAGNOSIS, - not the symptom. A plan hole or an open choice (naming, data shape, API - surface, dependency, a user-visible choice such as placement, wording or - behavior) → STOP, report `NEED-DECISION` with the precise question and - its `CLASS:`. Never re-investigate or improvise a different fix. -``` - -- [ ] **Step 6: bugfixer NOTES** - -Old: -``` -NOTES : -``` -New: -``` -NOTES : -``` - -- [ ] **Step 7: hotfixer rule + NOTES** - -After the `- Stay inside the scope you were given.` bullet (ends `apply only those.`) add: -``` -- An open user-visible choice the contract does not settle (placement, - wording, behavior) → `STATUS BLOCKED` with `CLASS: visible | public-name | - scope` in NOTES, BEFORE editing anything. The orchestrator asks the user - and re-dispatches once. -``` -Old NOTES: `NOTES : ` -New NOTES: -``` -NOTES : -``` - -- [ ] **Step 8: Run, expect green, commit** - -Run: `bash lib/tests/gates.test.sh 2>&1 | grep -E "bad|FAIL|PASS=|ok=" | tail -3; bash lib/tests/run-review-guards.sh 2>&1 | grep -E "G3|RED"` -Expected: no `bad`; G3 GREEN. -```bash -git add agents/feater.md agents/bugfixer.md agents/hotfixer.md lib/tests/gates.test.sh -git commit -m "feat(executors): NEED-DECISION and BLOCKED carry a CLASS tag" -``` - ---- - -### Task 9: Full suite, changelog, closure - -**Files:** -- Modify: `CHANGELOG.md` ([Unreleased] → Changed), `.claude/tasks/TODO.md` -- Test: `make test` - -- [ ] **Step 1: Full suite** - -Run: `timeout 300 make test 2>&1 | grep -E "RED|FAIL=" | grep -vE " 0 RED|FAIL=0"` -Expected: empty output. - -- [ ] **Step 2: CHANGELOG entry under `### Changed`** - -``` -- **Ask, don't guess: the orchestrators ask about open choices instead of - settling them.** `CLAUDE.global.md` replaces "one question upfront, never - mid-task" with: a choice visible in the result, a name that becomes - public, or a scope the request does not settle → ask, even mid-task; - internal technical choices stay Claude's. `lib/contract-interview.md` - STEP 2 becomes CLARIFY: pass A (the three gap checks, at contract time) - and pass B (the open-choice sweep in three classes, run once at each - flow's PLAN step, no question cap, over-5 guard, "you decide" recorded as - delegated). New MID-RUN CLARIFICATION section: an executor's - `NEED-DECISION` carries a `CLASS:` tag; visible / public-name / scope go - to the user verbatim, internal is decided in the loop; answers land in - the contract `[gated]`. New HOW TO ASK section (LRN-102). `/feat`, - `/bugfix`, `/hotfix`, `/ship-feature`, `/init-project` wire pass B at - their plan step; `/feat` and `/bugfix` stop deciding `NEED-DECISION` - themselves; `/hotfix` drops "zero questions ever" and allows one - re-dispatch for a class-tagged BLOCKED; the interviewer never ships a - visible / public-name / scope item as `(assumed)`; feater, bugfixer and - hotfixer report the class. Locks updated in the `contract-verifier`, - `loops-light` and `gates` tests. -``` - -- [ ] **Step 3: Tick the TODO section, commit** - -```bash -git add CHANGELOG.md .claude/tasks/TODO.md -git commit -m "docs(changelog): ask-don't-guess doctrine" -``` - -- [ ] **Step 4: Behavioral check (manual, before merge)** - -In a fixture repo: `/feat "add a share icon to the header"` must ask placement before dispatching; `/feat "add a share icon at the right end of the header, label Share, opens the native share sheet"` must ask nothing. Record the outcome in `evals.md` at capitalize. diff --git a/docs/superpowers/specs/2026-09-16-ask-dont-guess-design.md b/docs/superpowers/specs/2026-09-16-ask-dont-guess-design.md deleted file mode 100644 index 97143cd..0000000 --- a/docs/superpowers/specs/2026-09-16-ask-dont-guess-design.md +++ /dev/null @@ -1,221 +0,0 @@ -# Ask, don't guess: clarification doctrine for the orchestrators - -Date: 2026-09-16. Branch: `feature/ask-dont-guess`. Status: draft for user review. - -## Problem - -The orchestrators (`/ship-feature`, `/feat`, `/bugfix`, `/hotfix`, -`/init-project`) settle choices the user never made. Reported symptom: "add a -share icon" ships with the icon wherever the executor put it; nobody asked -left or right. - -Two causes, both written in the doctrine: - -1. `lib/contract-interview.md` STEP 2 asks a question only when a testable - outcome, a scope, or a non-contradictory constraint is missing. A request - can pass all three and still leave every visible choice open. Taste is - invisible to the trigger, so raising the 3-question cap would change - nothing. -2. When an executor halts with `NEED-DECISION`, `skills/feat/SKILL.md:153` - and `skills/bugfix/SKILL.md:165` instruct the orchestrator to "make the - decision HERE", twice, before escalating. The question reaches the user - after two guesses. - -The parent rule both inherit is `CLAUDE.global.md:51`: "One question upfront -if needed — don't interrupt mid-task." - -## Decisions taken with the user (2026-09-15/16) - -- The global rule changes, for all work, skill or not. `/hotfix` included. -- Three classes of open choice trigger a question: VISIBLE (placement, label, - wording, color, order, click behavior), PUBLIC NAME (command, flag, - endpoint, env var, file the user reads), SCOPE (should X change too, X - unnamed in the request). Class 4, internal technical choices with no - observable effect, never triggers one. -- Questions are asked when they arise, mid-task included, batched when - possible. - -## Design - -### 1. Parent rule, `CLAUDE.global.md` - -Line 51 becomes: - - - Ask rather than guess. A choice visible in the result (placement, - wording, order, behavior), a name that becomes public (command, flag, - endpoint, file), or a scope the request does not settle → ask, even - mid-task. Batch what can be batched. Internal technical choices with - no observable effect stay yours. - *Exception: skill-mandated gates and checkpoints (orchestrator - validation gates, approval gates, darwin checkpoints) always fire.* - -The exception line is kept as is. - -The rule "Bug received → fix directly: check logs, find root cause, resolve -autonomously." gains "; a visible choice in the fix still gets asked" so the -two lines do not contradict each other. `link.sh:20` symlinks this file to -`~/.claude/CLAUDE.md`; no other copy exists. - -### 2. Shared trigger, `lib/contract-interview.md` STEP 2 - -STEP 2 is renamed CLARIFY and split into two passes. Pass A keeps the three -existing gap checks and runs at contract time, against the request. Pass B is -the open-choice sweep: defined here, run once per flow at the PLAN step -(section 3), against the plan just written, because that is where a visible -choice becomes concrete. Replacement text: - - ## STEP 2 — CLARIFY (ask, never guess) - - Two passes, both in the main loop, both may talk to the human. - - **Pass A — gaps.** Run here, against the request. Ask if one of these is - missing AND not derivable from the repo: - - a testable expected outcome - - an unambiguous scope (what is allowed to change) - - non-contradictory constraints - - **Pass B — open choices.** Defined here, run ONCE at the flow's PLAN step - (see "Where pass B fires"), against the plan just written — that is - where choices become concrete. Enumerate every choice the run will - settle that the request leaves open; keep those in these classes: - 1. VISIBLE — the user would see it in the result: placement, label, - wording, color, order, what a click does. - 2. PUBLIC NAME — a name that outlives the run: command, flag, endpoint, - env var, a file the human will read. - 3. SCOPE — "should X change too?", where the request does not name X. - - NEVER ask class 4 — internal technical choices with no observable - effect (function decomposition, data shape, local naming, layout inside - an already-scoped zone). Those are delegated; asking them is the noise - that makes classes 1-3 ignorable. Never ask what the repo or the - request already answers — verify paths/APIs/behavior yourself first. - - No question cap. Each pass asks what it finds, in ONE batch. A request - that leaves nothing open goes through silently. More than 5 open - choices in pass B = the request is under-specified: list them, say so, - stop — do not fire a questionnaire. "You decide" / "peu importe" is an - answer: record it as `A: delegated — ` and never re-ask. - - Pass B answers land in the contract's CLARIFICATIONS marked - `[gated ]` — the contract is already on disk by then. - -Two new sections follow STEP 4 in the same file: - - ## MID-RUN CLARIFICATION (the channel executors halt into) - - An executor cannot talk to the human. It halts with `NEED-DECISION`, - the exact question, the options it sees, and a `CLASS:` tag (visible | - public-name | scope | internal). `/hotfix`: the hotfixer keeps - `DONE | BLOCKED`; a BLOCKED carrying the tag follows the same routing - instead of escalating to `/bugfix`. The orchestrator re-reads the - class — the tag is a hint, not a verdict — then routes: - - visible / public-name / scope → ASK THE HUMAN, verbatim question and - options. Never decide these yourself, never spend a round-trip - guessing. - - internal → decide here, note the decision, re-dispatch. The only case - the orchestrator settles alone; max 2 such round-trips → escalate. - - Every answer, human or orchestrator, appends to the contract's - CLARIFICATIONS marked `[gated ]` — the same micro-gate as - scope enrichment — and to the plan handed to the FRESH re-dispatched - executor, which reads the decision from disk, never from a transcript. - - ## HOW TO ASK (LRN-102) - - The harness reliably renders only the turn's FINAL text; text printed - before a tool call may be swallowed. So: - - up to 4 questions → one `AskUserQuestion` call; option descriptions - carry the context; print nothing the user needs before the call. - - more than 4, or a list handed back for re-specification → plain - text, end the turn. - -The per-flow weight table row for hotfix changes from "Zero questions ever" -to "Pass A silent autofill. Pass B runs at LOCATE against the 1-2 target -files' visible effect; a typo fix asks nothing." - -### 3. Where pass B fires, per flow - -| Flow | Pass B runs at | Against | -|---|---|---| -| feat | STEP 1 PLAN, before 1b CHALLENGE | the PLAN checklist | -| bugfix | STEP 3 FIX PLAN | the FIX PLAN | -| hotfix | STEP 1 LOCATE | the 1-2 target files' visible effect | -| ship-feature | STEP 2 PLAN, after the brainstorm | the plan, minus what the brainstorm already settled (in CLARIFICATIONS) | -| init-project | STEP 3 DESIGN, before VALIDATION GATE #1 | the DESIGN, minus what the interview and brainstorm settled | -| onboard | unchanged | its STEP 3 interview already asks scope in one block; the global rule covers leftovers | - -Each listed step gains one line: "Run pass B of -`$HOME/.claude/lib/contract-interview.md` against this plan; ask the batch -before continuing." - -### 4. Per-flow edits - -- `skills/feat/SKILL.md`: STEP 1's "If the approach is ambiguous: ask the - user ONE focused question BEFORE dispatching — never after (the executor - cannot relay questions)" is replaced by the pass B line. STEP 3's - `NEED-DECISION` handling is replaced by a pointer to MID-RUN CLARIFICATION. - STEP 1b's "surfacing any deferred BLOCKER via STEP 1's one-question gate" - is repointed to the pass B batch. -- `skills/bugfix/SKILL.md`: STEP 3 gains the pass B line; STEP 5's - `NEED-DECISION` handling is replaced by the pointer. The RULES line - "re-dispatched FRESH on every round-trip (NEED-DECISION, …)" stays true. -- `skills/hotfix/SKILL.md`: STEP 1 gains the pass B line. STEP 1.7's - "**zero questions ever**" becomes "pass A silent autofill; pass B was - asked at STEP 1". The hotfixer keeps `DONE | BLOCKED` and its - revert-not-loop identity; STEP 4 relays a tagged BLOCKED as a question - instead of escalating to `/bugfix`. -- `skills/ship-feature/SKILL.md`: STEP 2 PLAN gains the pass B line. -- `skills/init-project/SKILL.md`: line 62's "No new questions (the interview - already asked)" becomes "Pass A is covered by the interview; pass B runs - at STEP 3 against the DESIGN". STEP 3 gains the pass B line. -- `agents/interviewer.md`: the FAILURE MODES rows and the 2-round budget - keep working for gaps. A class 1-3 item still open after round 2 gets ONE - more targeted question; it never lands in OPEN DECISIONS as `(assumed)`. - The DO NOT entry "Exceed the 2-round budget" gains "except the one - targeted class 1-3 question". -- `agents/feater.md`, `agents/bugfixer.md`: the halt trigger "A plan hole or - an open choice (naming, data shape, API surface, dependency)" gains "a - user-visible choice (placement, wording, behavior)". The NOTES grammar for - `NEED-DECISION` gains `CLASS: visible | public-name | scope | internal`. -- `agents/hotfixer.md`: the NOTES grammar for BLOCKED gains the same - `CLASS:` tag when the blocker is an open choice. - -### 5. Locks and tests - -`lib/tests/contract-verifier.test.sh`: drop `"silent when complete" "ZERO -questions"` and `"question budget" "max 3 questions"`. Add locks on: -`"goes through silently"`, `"No question cap"`, `"PUBLIC NAME"`, `"NEVER ask -class 4"`, `"More than 5 open choices"`, `"delegated —"`, `"## MID-RUN -CLARIFICATION"`, `"CLASS:"`, `"## HOW TO ASK"`. - -`lib/tests/loops-light.test.sh:84`: `"hotfix zero questions" "questions -ever"` becomes `"hotfix pass B at locate" "pass B"`. - -`lib/tests/gates.test.sh` locks on `contract-interview.md` (oracle doctrine) -are untouched; the ORACLES section does not move. - -Behavioral check, run once by hand after the edits, in a fixture repo: -`/feat "add a share icon to the header"` must ask placement before -dispatching; `/feat "add a share icon at the right end of the header, label -Share, opens the native share sheet"` must ask nothing. - -### 6. Out of scope - -- `README.md`, `USAGE.md`, `ARCHITECTURE.md`: none mentions the question - doctrine (grep 2026-09-16). A `/doc` pass after merge covers any flow - description that drifts. -- `CHANGELOG.md` entry and registries (a BDR superseding the "one question - upfront" rule) happen at the CAPITALIZE step, not in this spec. -- TODO.md line 199 (C2 self-contradiction audit of `CLAUDE.global.md`) stays - open; this spec fixes only the contradiction it exposes. - -### 7. Risks - -- Chattiness. Three classes, a mid-run channel, no cap. The class 4 - exclusion and the over-5 guard are the two brakes. Watch in real use; - LRN-047 records that a frequent ignored nag is itself a risk. -- `/hotfix` identity. Its value is speed and silence. Pass B at LOCATE adds - one possible batch before touching anything. If it fires on most - hotfixes, the class definitions are too wide, not the flow. -- Executor mis-tagging. A class 4 tagged as visible offloads a decision to - the user. The orchestrator re-reads the class; the tag is a hint.