step.21.diff.txt -- narrative retro for step.21 (U3.8: THE M1 ORACLE -- the one-complete-UoW arc, mock AND real provider; the frozen DAG passes the substrate's round-trip oracle. P3 CLOSED.) Executed: 2026-07-21, same session as the draft (single pass, the step.7/9/10/11/14-20 convention -- arc.py, the ArcError addition, the `run` CLI subcommand, the additive M1 mock scenarios, the 16 test additions, scripts/m1_oracle.sh, the committed m1_exec corpus + registration, the end_to_end.md/testing.md/state-machine-doc edits, and the step file all built together). Companion artifact: step.21.gdiff.txt (automated, scripts/step_gdiff). Per the operator's explicit per-UoW checkpoint rule (HARD), this session STOPS after U3.8 -- P3 is CLOSED; P4/P5/P6 are not started. ## Plan vs actual ``` +---+----------------------------------+------------------------------------------+ | # | Planned (brief) | Actual | +---+----------------------------------+------------------------------------------+ | 1 | Decide the continuation shape | STATE-DISPATCH loop (the brief's LEAN | | | (`b3ubot run` or justified | sharpened): each iteration dispatches | | | alternative) | the one stage the engine state admits, | | | | so resumption falls out FREE (halted | | | | execute, escalated verify, amend stop | | | | all re-enter the same command) and the | | | | arc composes U3.3-U3.7 unchanged -- no | | | | new edge, no reimplementation. Stops | | | | only at design section 4's two gates. | | 2 | The M1 oracle's form (pytest + | BOTH, permanent (the U2.2 rule): | | | script LEAN) | tests/test_m1_arc.py (16, hermetic, | | | | mock, form assertions mechanical) + | | | | scripts/m1_oracle.sh (p1_gates shape: | | | | real CLI, mock leg always 19/19, keyed | | | | leg standing). test_synthetic_uow_arc | | | | read and left as-is: it honestly proves | | | | the U2.4 LEDGER machinery, so the M1 | | | | test is a SIBLING, not an edit. | | 3 | Substrate round-trip decision | BOTH legs (the brief's LEAN): per-run | | | ((i) in-oracle and/or (ii) | roundtrip_verify on both frozen | | | committed corpus) | instances in test AND script; PLUS the | | | | live smoke's own frozen execution DAG | | | | committed VERBATIM as | | | | dag/instances/m1_execution_example.dag, | | | | registered as m1_exec (corpora 7->8); | | | | full dag regression green with it: 8/8 | | | | 4-way byte-identity + npass + quirks. | | 4 | Keyed leg: three-outcome honest | Implemented IN the oracle ((a) skip / | | | handling; check key without | (b) live battery / (c) BLOCKED(env) | | | printing it | counted separately from FAIL). Actual | | | | outcome here: (a) -- and sharper than | | | | the brief expected: ~/.anthropic_api.env | | | | exists but is an UNFILLED template | | | | (value length 0, checked via awk | | | | length() + a subshell sourcing probe; | | | | no key byte printed, no call attempted | | | | against an empty credential). M1's | | | | real-provider clause recorded as | | | | pending-on-environment, held by the | | | | standing keyed leg. | | 5 | Form assertions ("indistinguish- | Mechanical, all green: closure step | | | able in form") | file parses (parser.py) with all 4 | | | | engine stamps; ledger table byte- | | | | round-trips (U2.3) with the REAL hash; | | | | both frozen DAGs byte-round-trip via | | | | bin/dagModel; retro pair = the corpus | | | | MACHINE-DRAFTED shape; 3 commits + | | | | clean porcelain. | | 6 | Tests + testing.md same commit | 16 new tests; testing.md same-commit | | | | (new section, phase overview, totals | | | | 27 files/402 funcs/565 collected, | | | | guard + 8-corpora notes) -- its own | | | | maintenance rule honored. | | 7 | Docs: section 5 row DONE + P3 | Row DONE in the U3.2-U3.7 style with | | | banner/exit note; ledger via | the caveat stated; NEW "P3 exit state" | | | CLI; design.md if drifted | note under the exit condition; ledger | | | | via the ledger CLI (dogfooded); | | | | design.md checked -- NO drift, left | | | | untouched (named, not skipped). | +---+----------------------------------+------------------------------------------+ ``` ## Surprises / genuine findings 1. **The arc composed clean on the first run** -- the 5-stage mock arc test passed on its first execution, and so did the live CLI smoke. Steps 14-20's insistence on per-stage legality-first, injectable I/O, and files-as-truth meant the continuation had NOTHING to patch around: the M1 arc is 200 lines of dispatch, not an integration battle. The composition discipline's payout, measured twice now (step.20 recorded the same for closure). 2. **The key file is an unfilled template** -- the brief prepared for outcome (c) (key present, billing-blocked); the truth was sharper: `~/.anthropic_api.env` exists (0600, correctly commented) but its `export ANTHROPIC_API_KEY=` line carries a ZERO-LENGTH value -- the operator scaffolded the file (step.3's own instructions) and never filled it, consistent with the blocked credit purchase making a key pointless to install. Established without printing anything (length-only probes); recorded as outcome (a). The BLOCKED(environment) branch is built and standing but has never yet fired. 3. **The engine's own instance IS corpus-grade** -- the frozen execution DAG from the live smoke ran the full 4-language byte-identity matrix (C++/Py/JS/Rust), N-pass, and quirk legs with ZERO adjustment: the step.18/19 kv-sanitization and ASCII lessons (learned against exactly these substrates) held. The corpus upgrade from hand-authored exemplar to engine-produced evidence cost one `cp` and one registration line. 4. **The state-dispatch shape made `--title/--intent` optional-by- state** -- a fixed five-call script would have required them always; dispatch means a resumed arc doesn't need them at all, which fell out as a small honest UX win (and its own refusal message when a FRESH arc lacks them). 5. **The synthetic-arc test survived unedited** -- the brief flagged it for extend-or-sibling; reading it settled the question in one docstring: it proves the U2.4 ledger MACHINERY and says so explicitly, so the M1 test is its P3 sibling, and both now bracket the arc from below (machinery) and above (orchestrator). ## Scope discipline P3 CLOSES with this step. P4 (Track B), P5 (CCS plane -- T3 still refuses naming U5.2), P6 (second adapter/agnosticism/T4), the PAL-narrated retro (now eligible, Q-21-C records where it could land), Q-17-A, d9's final policy, pushing anything anywhere: none started (G7). The operator's explicit P3-closes-here boundary is honored -- control returns after the retro pair. ## Numbers 536 passed / 11 skipped before -> 552 passed / 11 skipped after the build (+16 tests/test_m1_arc.py) -> 554 passed / 11 skipped with this step's files in the corpus (+2 self-inclusive pickups). Zero regressions; zero existing tests edited (the fixture builder's new `gates=` param is default-byte-identical -- DRAFT_SKELETON_UT1's bytes unchanged, proven by the untouched step.16-20 suites). Live: one `b3ubot run --yes` arc rc=0 (the exemplar's source run); m1_oracle.sh mock leg 19/19 PASS + keyed leg honest SKIP, exit 0; dag regression 10 subprojects PASS with the new 8th corpus 4-way byte-identical. End of retro.