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"