Files
claude/lib/contract-interview.md

11 KiB

Contract interview — mandatory upstream passage (all orchestrators)

Produces the CONTRACT: the single reference passed verbatim to the plan, the dev subagents, and the verifier. The contract is what lets the orchestrator delegate execution without subagents ever needing a human gate (LRN-083: 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, 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)

Copy the user's request EXACTLY as typed ($ARGUMENTS + the triggering 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 — 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 — <default taken> and never re-ask it.

Pass B answers land in the contract's CLARIFICATIONS marked [gated <YYYY-MM-DD>] — 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

  • ACCEPTANCE CRITERIA: numbered; each one testable — a fresh reader must be able to mark it MET / NOT-MET against the real code, without having seen this conversation.
  • FILE SCOPE: paths/zones expected to change, or repo-wide — <reason>.

ORACLES — a criterion a command can decide carries one

Give such a criterion an indented CHECK: (the command), EXPECT: (a success-only marker), and EVIDENCE: pending. bash ~/.claude/lib/gates.sh run <contract> executes it fail-closed — MET requires exit 0 AND the marker — and writes the result back over the EVIDENCE: line. That persisted evidence is what the fresh verifier reads as fact instead of trusting the executor's report (GATE 0 in lib/verify-secure-loop.md).

Both attributes or neither. CHECK: without EXPECT: is a parse error, not a manual criterion — the runner refuses the whole ledger. Leave a criterion oracle-free when no command can decide it; the verifier judges those.

Four authoring rules — a gate that cannot fail proves nothing:

  1. Observe the named artifact. The check reads the file, service, or measurement the criterion's own words name — never a proxy for it. 1. invoices reconcile + CHECK: echo ok is valid and worthless.
  2. Success-only marker. The script runs every assertion, exits nonzero on any failure, and prints the EXPECT: string only after all pass.
  3. Positive control before any absence check. Run the same logic against a fixture known to trip it and confirm it fails. A missing file, a wrong path, and a broken pattern all look exactly like valid absence.
  4. Recompute supplied numbers. Never copy a figure from the request into EXPECT: — the script derives it from source and prints its own marker. A number that is its own proof proves nothing.

CHECK: is shell code run with our privileges. It is safe only because we author it in our own repo — never build one out of externally-supplied text (a scraped URL, a client string); route those through lib/url-guard.sh.

STEP 4 — WRITE TO DISK (immediately, before any next step)

Path: .claude/tasks/contracts/<YYYY-MM-DD>-<slug>-<HHMM>.md (mkdir -p the directory; unique per run: date + short kebab slug + HHMM — two runs on the same day never collide). A contract that lives only in context dies at compaction, and the verbatim request with it.

Template:

# CONTRACT — <slug>
- date: <YYYY-MM-DD> | flow: <ship-feature|feat|bugfix|hotfix|init-project|onboard> | branch: <branch>
- status: active

## REQUEST (verbatim — IMMUTABLE)
<the user's exact words>

## CLARIFICATIONS
Q: <question> / A: <answer>   (pass B and mid-run entries: [gated <YYYY-MM-DD>])
(or: none — request complete)

## ACCEPTANCE CRITERIA
1. <criterion a command can decide>
   CHECK: <command>
   EXPECT: <success-only marker>
   EVIDENCE: pending
2. <criterion only human judgement can decide — no CHECK/EXPECT>

(ABANDON: <n> <non-blank reason> — only for a criterion proven impossible)

## FILE SCOPE
<paths/zones>
(or: repo-wide — <reason>)

Print one line to the user, then continue the flow: CONTRACT: <path> — <n> criteria, scope <files|repo-wide>, <q> 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 <YYYY-MM-DD>] — 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 "cleaned up".
  • CRITERIA / FILE SCOPE enrichment: ONLY at a human gate, each added entry marked [gated <YYYY-MM-DD>]. A dev subagent NEVER enriches the contract. An out-of-scope edit the dev justifies is accepted ONLY through this micro-gate: human approves → FILE SCOPE gains the entry [gated]; human declines → the dev removes the edit. Without this gate the dev justifies everything and scope constrains nothing.
  • ABANDONMENT: a criterion proven impossible within the authorized task is NEVER deleted and never quietly downgraded. Keep it, append ABANDON: <n> <non-blank reason + handoff> under the criteria, and name it in the final report. An abandonment is a visible handoff, not a pass: the verifier cannot return CONFORME while one stands, and the run cannot be described as fully complete. This is the structural half of the house rule "blocked on an independent sub-part → do the rest, state what's missing".
  • Deep re-scope (the request itself changes): NEW contract file with supersedes: <old path> in its header — never a rewrite of the old one.
  • Aborted run: delete the contract file, or commit it with status: aborted in the header. NEVER left dirty in the working tree.
  • Commit: the contract rides the existing memory commit — lib/capitalize-commit.md already covers the .claude/tasks pathspec. No new plumbing.

Weight per flow

Flow Weight
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 <date>] — 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).
onboard Audit-scope contract (interview answers → what to audit, which axes).

Oracles follow the same proportion. hotfix: none — that flow runs no floor (and no verifier); the hotfixer runs build/tests itself. feat / bugfix: the suite criterion at minimum, and for bugfix the regression test the DIAGNOSIS names — its CHECK: runs that test alone, so a green result means the reproduction actually flipped. ship-feature / init-project: build, suite, and every criterion a command can settle. onboard: audit criteria are mostly judgement — leave them oracle-free rather than invent a check that cannot fail.

Hand-off rule

Downstream consumers (plan step, dev subagents, verifier) receive the contract PATH, not a restatement of its content — the file on disk is the only authoritative copy, and reading it from disk is what makes the dev's reformulation structurally unable to interpose.