dr-spec-change · git:20260808.dba76ae · 2026-08-08 · sha256 972fe9f66edb85c1
dr-spec-change git:20260808.dba76aeA
Immutable. This exact content is served forever at /api/v1/blob/972fe9f66edb85c1.
---
name: dr-spec-change
description: Translate a captured request into a concrete, bounded change specification (SPEC.md) with per-requirement acceptance checks and recorded assumptions. Use after REQUEST.md exists or gains amendments.
---
# Specify the change
Input: REQUEST.md (re-read it in FULL first, including amendments).
Output: SPEC.md mapping every requirement to concrete work with a
machine-decidable acceptance check. This is the only phase where
interpretation happens, and it happens in writing.
## Procedure
1. For EVERY R in REQUEST.md (no skips — walk the numbers in order),
write a spec item: target files, behavior before → behavior after,
and an acceptance check (a command + expected output, or an
artifact-exists-with-content check). A requirement with no
acceptance check is not specified yet.
2. Resolve each open question Q:
- If the readings differ only in minor detail: pick the smallest
reasonable one and record it under Assumptions with the words
"assumed, operator may override".
- If the readings differ materially (different files, different
behavior, >2x effort): put it in "Questions for operator" and
STOP after committing SPEC.md — present the batched questions.
Never start implementation with a material ambiguity open.
First load `dr-ask-the-right-question` and run each candidate
question through it: the record or the operator's recorded values
answer most of them, and only survivors of its dominance test
belong in the batch (each with a recommendation).
- A mechanism the request NAMES — a fixture to reuse, a file to copy,
a pattern to follow — is a suggestion, not a requirement. Verify it
actually reaches the code this change touches (trace the call path)
before adopting it. If it cannot, that is a material contradiction:
deliver the PROPERTY the requirement wants and record the
contradiction in writing, or fork to the operator. Never adopt a
named mechanism unverified, and never deviate from it silently.
(Recorded misses this rule generalizes: docs/ERRATA.md E10 — a
handover-named fixture that never executed the migrated code;
docs/ERRATA_EXECUTOR.md X11 — a false premise in the authorization
itself.)
3. Frozen-surface contact forecast — mandatory, in writing. Diff the
planned target files against `docs/map/INV-frozen-surfaces.md`'s
surface list and record the verdict in SPEC.md's "Frozen-surface
contact forecast" section; "none expected" counts, but only after
actually checking. ANY plausible contact stops the tranche HERE:
commit SPEC.md and obtain the operator's words before `dr-plan-steps`
runs. Contact discovered at validation is three commits too late —
the tranche that proved it (docs/ERRATA_EXECUTOR.md X9, XE1) was
technically perfect and still could not deliver. For changes that add
data to the typed record, one more guardrail: the absence-tolerant
READER lands before the writer emits, so every existing committed
root stays valid with the new data absent (the rung-4 guardrail
generalized; X8 is the precedent for keeping new fields out of frozen
digests entirely). And a new typed-record OBSERVABLE (field, record
type, finding) needs a sweep probe proposed for it in the spec: a
sweep that never looks at the new data reports "byte-identical"
trivially while proving nothing about it. The probe change is its own
SEPARATE commit — extending `tools/root_sweep.py` resets the
byte-identity baseline, so it never rides the same commit as the
`src/` change it would judge, gets its own before/after capture on an
unchanged tree, and follows the tool's probe rule (assert the
attribute exists before reading it). Build every proposed test,
check, and probe to `dr-execute-step`'s "Durable tests, checks, and
probes" rules — they must survive dramatic repo changes, failing
only when the guarded claim stops being true.
4. Blast-radius census — mandatory, pasted, BEFORE any fixture-drift
prediction. For every symbol and file the spec changes, grep what
already asserts on it:
grep -rn "<symbol>" tests/ docs/map/
Paste the hit list (or "no hits") into SPEC.md's "Blast-radius
census" section and classify EVERY hit: EXPECTED TO MOVE (the
design predicts it) or MUST NOT MOVE. A drift forecast written
without this census is recall, and recall missed in two consecutive
specs — under the MORE capable model both times (rung-5 PARKED P6):
rung 4's prediction was too narrow; rung 5's spec predicted nothing
and missed a test pinning "exactly one backend", the exact state
that rung existed to change. The full gate caught both, three
commits later than the census would have.
5. DESIGN-AND-STOP shape. When the deliverable IS the spec (a
[DESIGN-AND-STOP] request), two more sections are mandatory, and
their discipline is measure-don't-reason (the rung-4 M1-M5
precedent, the one design spec that survived contact with the
tree unchanged):
- **Measurements**: every load-bearing design claim is a pasted
command output. A claim with no measurement is an assumption and
is moved to Assumptions, where the operator can see it.
- **Options**: every considered option priced — files touched,
frozen-surface contact, estimated lines, risk — and every
rejection cites a measurement, not a preference.
6. Set the budget: total estimated changed lines and commits. If over
~300 lines, propose a split into ordered sub-tranches (each with
its own delivery) rather than one sprawling one. The Budget
section's headline number(s) MUST equal the computed sum of the
itemized per-item estimates above — paste the arithmetic (e.g.
`python3 -c "print(sum([...]))"`), never restated by hand. This is
the number `tools/diff_budget.py`'s `DIFF_BUDGET_RESULT_V1.verdict`
is checked against at every `[COMMIT]` step (`dr-execute-step`); a
headline that contradicts its own itemization defeats the ceiling
before the first commit (Rung S5, REQUEST.md Amendments 2/3: its
headline said 220-300, its own itemization summed to 435).
7. Anti-invention pass: re-read SPEC.md and delete anything that does
not trace to an R or C number. If it felt necessary, it is either
an assumption (record it) or scope creep (PARKED.md).
8. Rubric pass — the last act before committing. Re-read the finished
SPEC.md as a REVIEWER, not the author; any "no" routes back to that
step before commit:
- every R has a spec item with a machine-decidable accept?
- blast-radius census pasted (or pasted-empty) and every hit
classified?
- frozen-surface contact forecast recorded?
- every mechanism the request names traced to code it actually
reaches?
- DESIGN-AND-STOP only: every claim measured, every option priced?
- nothing in the spec untraceable to an R/C number?
Record the outcome as one line in SPEC.md ("Rubric: n/n yes").
## SPEC.md template
# Spec for: <request headline>
Traces: every item cites R/C numbers. Untraceable items are bugs.
## Items
S1 (R1): <files> | before: <...> | after: <...>
accept: <command> -> <expected>
S2 (R2, C1): ...
## Assumptions (operator may override)
A1 (Q1): <chosen reading, one line, and why it is the smallest>
## Questions for operator (STOP if non-empty)
...
## Out of scope (explicit)
<nearest tempting neighbors, each with "not requested">
## Frozen-surface contact forecast
none expected — checked against INV-frozen-surfaces.md
| <surface>: <why contact is plausible> (STOP — operator words
required before dr-plan-steps)
## Blast-radius census
<symbol/file>: <test or map check hit> -> EXPECTED TO MOVE |
MUST NOT MOVE
(every grep hit listed, none omitted; "no hits" is a valid census)
## Measurements (DESIGN-AND-STOP only)
M1: <command> -> <pasted output> — supports <claim>
## Options (DESIGN-AND-STOP only)
A: <files, frozen contact, ~lines, risk> | rejected: cites M<n>
B: ... | CHOSEN: cites M<n>
## Budget
~<n> lines, <n> commit(s). Frozen surfaces touched: none | <flagged>
Rubric: <n>/<n> yes
## Exit criteria
- SPEC.md committed and pushed; every R number appears in some item
(or is explicitly marked deferred with the operator's words allowing
it).
- The rubric pass ran and its line is in SPEC.md; a spec with no
"Rubric:" line was committed without its last check.
- If "Questions for operator" is non-empty: stopped and asked.
- Return to the orchestrator.