step.21.txt -- b3ubot U3.8: THE M1 ORACLE -- the one-complete-UoW arc,
               mock AND real provider; the frozen DAG passes the
               substrate's round-trip oracle. end_to_end.md §5 P3's
               FINAL item -- the phase's exit milestone, "the engine's
               first light": ONE invocation drives intent -> skeleton
               -> human ratify -> execute -> verify -> retro closure,
               leaving a ledger AND a frozen execution DAG
               indistinguishable in form from hand-run b3u artifacts.
               Stops here -- P3 is CLOSED; does NOT continue into
               P4/P5/P6 or any other phase.

Step:     21
Parent:   end_to_end.md §5 U3.8 row verbatim ("M1 oracle: the
          one-complete-UoW arc, mock AND real provider; the frozen
          DAG passes the substrate's round-trip oracle") + §5's exit
          condition ("M1, the engine's first light"). design.md §4
          ("Everything between the gates is machine" -- the sentence
          this step finally takes literally; ratify + escalation are
          the ONLY gates). app/orchestrator/ U3.1-U3.7 (steps 14-20)
          are the five delivered stages this step COMPOSES -- U3.6
          (step.19) and U3.7 (step.20) both explicitly deferred
          "automatic continuation" to exactly this UoW. The U2.2
          "oracle is a regression suite" rule and the U1.4
          p1_gates.sh live-gates pattern shape the oracle's form;
          the step.6/U3.9a corpus-registration precedent shapes the
          substrate-oracle decision.
UoW:      U3.8
Date:     2026-07-21
Origin:   Operator directive 2026-07-21 (same dispatch shape as
          step.14-20's): draft AND execute steps/step.21.txt for
          U3.8 in a single pass (the step.7/9/10/11/14-20
          convention, NOT a skeleton-split), with the SAME explicit
          HARD scope boundary -- U3.8 closes P3; stop after it, do
          not continue into P4/P5/P6 or any later phase; the
          operator directs one UoW at a time.

## (0) Status

EXECUTED in this pass. Not a skeleton-then-later-execute split --
app/orchestrator/arc.py, the ArcError addition, the `b3ubot run` CLI
subcommand, the M1 mock-fixture scenarios, tests/test_m1_arc.py,
scripts/m1_oracle.sh, the committed engine-produced regression corpus
dag/instances/m1_execution_example.dag (+ corpora.sh registration +
instances README), the end_to_end.md §5 row + P3-exit-state note +
§11 ledger row, tests/testing.md (same commit, its own maintenance
rule), the stale "U3.8 pending" docstring sweep, and this step file
were all built together, per the operator's explicit "draft AND
execute (single pass)" direction. Per the operator's explicit scope
boundary (Origin, above), this session STOPS after U3.8 -- **P3 is
CLOSED**; P4 (Track B), P5 (CCS plane), P6 (agnosticism) are NOT
started: the engine returns control to the human between phases.

M1's honest achieved state, stated plainly (the row and the §5 exit
note carry the same words): the arc is FULLY proven on the mock
provider -- hermetically (pytest) and live (the real CLI, whose
frozen execution DAG is committed as a permanent corpus) -- and the
"with a real provider" clause is held by a STANDING keyed oracle leg
that runs the identical arc live the moment the environment provides
a usable $ANTHROPIC_API_KEY. In THIS environment the key is genuinely
absent (outcome (a) of the three the operator's brief named): the
live real-provider confirmation is recorded as PENDING-ON-ENVIRONMENT,
never fake-passed. See §(1) CONFIRMED for the exact environment fact.

## (1) Ground truth probed at drafting (outputs -> named writers)

    READ    end_to_end.md §5 in full: the exit condition verbatim
            ("M1, the engine's first light: ... with a real provider,
            leaving a ledger AND a frozen execution DAG
            indistinguishable in form from hand-run b3u artifacts")
            and the U3.8 row ("mock AND real provider; the frozen
            DAG passes the substrate's round-trip oracle").
    READ    app/orchestrator/ in full (steps 14-20's five stages +
            state machine + persistence + compiler): every stage's
            signature, result dataclass, legality-first rule, and
            resume semantics -- the arc COMPOSES these, so their
            exact contracts are the design input. app/cli.py in
            full: the five arc subcommands, the Q-8-B guard
            per-command reasoning (the `run` guard is their union),
            _make_adapter (§2.11 -- provider names stay out of the
            orchestrator).
    READ    step.19/step.20's explicit deferrals ("automatic
            execute->verify continuation is U3.8's M1 arc";
            "automatic verify->close continuation is U3.8's") --
            the wiring this step owns.
    READ    tests/test_synthetic_uow_arc.py in full (the U2.4-era
            arc test the brief flagged): it walks the U2.4 LEDGER
            MACHINERY (draft-step template/table ops/step_gdiff)
            through a synthetic UoW with ratify/execute/verify as
            explicit stand-ins -- P3's orchestrator is named out of
            its scope in its own docstring. Decision: EXTEND-AS-
            SIBLING, not edit -- it stays exactly what it honestly
            is (the U2.4 machinery proof); tests/test_m1_arc.py is
            the REAL-orchestrator arc oracle. Zero edits to it.
    READ    tests/test_orchestrator_closing.py (the toy-workspace +
            git fixture + ledger-doc conventions, reused);
            adapters/mock/fixtures.py (the UT.1 skeleton builder +
            EXECUTE routing key -- the M1 scenarios are additive
            siblings); scripts/p1_gates.sh in full (the live-gates
            shape m1_oracle.sh mirrors); dag/regression/lib/
            corpora.sh + bin/run_regression (corpus registration +
            what the matrix runs); tests/testing.md (the live doc's
            own same-commit maintenance rule).
    CONFIRMED baseline: full suite 536 passed / 11 skipped before
            any edit (matches step.20's reported final).
    CONFIRMED the keyed environment, WITHOUT ever printing key
            material (the operator's brief demanded exactly this):
            `claude` binary IS on PATH; $ANTHROPIC_API_KEY is NOT in
            this process's env; ~/.anthropic_api.env EXISTS but is
            an UNFILLED TEMPLATE -- its one variable line is
            `export ANTHROPIC_API_KEY=` with a ZERO-LENGTH value
            (checked via awk length(), names/lengths only; a
            sourcing probe in a throwaway subshell confirmed the
            var stays empty). So the keyed leg's outcome here is
            (a) key absent -> the established honest skip -- NOT
            (c) billing-blocked: no live call was possible at all,
            and none was attempted against an empty credential.
    CONFIRMED live CLI smoke BEFORE committing (scratch workspace +
            isolated $B3UBOT_HOME): ONE `b3ubot run <ws> UM.1
            --title ... --intent ... --yes` invocation drove
            draft -> ratify -> execute -> verify -> close end to
            end, rc=0: 3 commits (founding + work 61d818d + gdiff
            ef9982b in the scratch repo), porcelain clean, real
            hash in §(7) AND the ledger row, both instances frozen,
            "arc complete ... engine state CLOSED". THIS run's
            frozen step.1.execution.dag is the committed exemplar
            (2.5) -- captured verbatim, not re-generated.
    CONFIRMED `git log --oneline -30` for the 2-commit phrasing
            (step.20's precedent, 7cbee41/8c72bc4).

## (2) What U3.8 delivers

  2.1 **app/orchestrator/arc.py** (new; the package's capstone):
      `run_uow_arc(workspace_root, uow, *, adapter, provider,
      output_fn, input_fn, uow_title=, intent=, assume_yes=, home=,
      date=, max_repair_attempts=, scripts_home=)` -> ArcResult.
      **The continuation shape (THE core decision, made and owned): a
      STATE-DISPATCH loop, not a fixed five-call script.** Each
      iteration reads the engine state and dispatches the one stage
      that state admits (untracked/INTENT->draft, SKELETON->ratify,
      RATIFIED/EXECUTING->execute -- EXECUTING resumes, executing.py's
      own rule -- VERIFYING->verify, RETRO->close), looping to CLOSED.
      Why dispatch: design §4 IS a state machine, so the honest
      continuation advances whatever state the record is in;
      RESUMPTION FALLS OUT FOR FREE (a halted execution, an escalated
      verify, or an arc stopped at amend all re-enter with the same
      `b3ubot run`); and every per-stage behavior (bounded repair,
      freeze walls, egress, trails, legality-first) is exercised
      UNCHANGED -- the arc composes U3.3-U3.7, it adds NO transition
      and reimplements NOTHING. A bounded dispatch cap (7) turns a
      state-not-advancing bug into a loud ArcError instead of a spin.
  2.2 **Where the arc stops -- the gates, and ONLY the gates (design
      §4's own set):** ratify (interactive per-LEAN, or --yes
      ratify-as-is; a non-ratified decision -- amended/discarded/
      aborted -- returns closed=False with a named stop_reason: a
      DECISION, not an error, CLI exit 0) and escalation
      (EscalationError propagates unchanged; state stays VERIFYING; a
      re-run re-enters with a fresh budget). The in-loop proposal
      review inside EXECUTING keeps its own gate (or --yes), exactly
      as `b3ubot execute` runs it. `uow_title`/`intent` are required
      ONLY when the arc actually starts at draft (ArcError names
      --title/--intent otherwise); a terminal UoW refuses loudly
      (never revived -- the U3.7 rule). Plus ArcError in errors.py;
      arc exports through app/orchestrator/__init__.
  2.3 **CLI: top-level `b3ubot run`** (the draft/ratify/execute/
      verify/close sibling row's capstone -- ALL five per-stage
      commands remain first-class; `run` composes them). Q-8-B as the
      UNION of the stages' rules: --provider human needs a TTY
      regardless of --yes (the relay is interactive); a non-TTY stdin
      refuses unless --yes (--yes = ratify-as-is + auto-approved
      in-loop gate, each stage's established semantics). Presentation-
      layer errors only; an honest gate stop prints "arc stopped:
      <reason>" and exits 0.
  2.4 **The M1 oracle -- a PERMANENT regression in two halves (the
      U2.2 rule; LEAN taken):**
      (a) tests/test_m1_arc.py (16 new tests; pytestmark on git AND
      the built bin/dagModel -- the arc genuinely needs both): the
      full mock arc on a git toy workspace with the M1 clause
      "indistinguishable in form from hand-run b3u artifacts" made
      MECHANICAL -- the closure-produced step file PARSES under
      app/ledger/parser.py with all FOUR engine stamps in §(0)
      (ratification/execution/verification/closure); the workspace
      ledger table byte-round-trips through the U2.3 machinery with
      the REAL work-commit hash in the row; BOTH frozen instances
      byte-round-trip through the REAL bin/dagModel; the retro pair
      is the MACHINE-DRAFTED corpus shape with a real gdiff; 3
      commits + clean porcelain; plus the honest-stop (amend),
      mid-arc resume (SKELETON entry, no re-draft), missing-title/
      intent, terminal, own-repo-untouched, and 4 CLI legs.
      tests/test_synthetic_uow_arc.py deliberately NOT edited --
      extend-as-sibling (§(1)'s reading: it proves the U2.4 ledger
      machinery, and still does).
      (b) scripts/m1_oracle.sh (the p1_gates.sh live half: pass/fail
      counters, scratch WORK dir, real CLI invocations): boot (venv +
      import + git + bin/dagModel, each refusal naming the fix); the
      MOCK ARC LEG always -- one `b3ubot run --yes`, then 15 named
      checks incl. the in-oracle substrate round-trips and the
      egress-trail count; the KEYED LEG (below); B-4 hygiene (the key
      value appears nowhere under $WORK -- and is never echoed by the
      script itself). Live this session: mock leg 19/19 PASS, keyed
      leg honest SKIP, exit 0.
  2.5 **The substrate round-trip oracle -- decision: BOTH legs (the
      brief's LEAN, taken):** (i) per-run, in-oracle: both frozen
      instances re-verified through executing.roundtrip_verify (the
      REAL bin/dagModel parse + byte-compare) inside the pytest arc
      AND the live script; (ii) permanent, in-corpus:
      dag/instances/m1_execution_example.dag -- the frozen evidence
      graph of THIS session's real `b3ubot run` mock arc, captured
      VERBATIM from the closed scratch workspace (4 nodes/3 edges,
      the execution kind, every node carrying the live evidence kvs
      the engine stamped + the dag-level frozen_at) -- registered as
      the `m1_exec` corpus in dag/regression/lib/corpora.sh (7 -> 8;
      header comment updated; instances README row + scope section
      added). The step.6/U3.9a hand-authored exemplar thereby
      UPGRADED: the regression now re-proves on every run that what
      the ENGINE WRITES round-trips -- and the full dag suite ran
      green with it THIS session: matrix 8/8 corpora 4-way
      byte-identical (C++/Py/JS/Rust), npass 8/8, quirks 8/8
      baseline, 10 subprojects PASS. tests/test_m1_arc.py pins the
      exemplar permanently (exists + frozen + round-trips +
      registered).
  2.6 **The keyed/real-provider leg -- the three-outcome honest
      handling (the operator's brief, implemented verbatim):** the
      leg gates on ClaudeCodeAdapter.is_available() (the p1_gates.sh
      faithful-mirror pattern -- never a bash re-derivation; the key
      VALUE never echoed): (a) unavailable -> named SKIP; the leg
      STANDS and auto-runs when the environment allows (the step.3/9
      keyless discipline); (b) available + arc completes -> the M1
      real-provider clause proven live (the same 15-check battery
      runs on the keyed workspace; the keyed intent adds "keep the
      change minimal" -- smallest possible spend); (c) available but
      the call fails on a billing/credit/auth condition -> BLOCKED
      (environment): the named evidence line is printed, counted
      SEPARATELY from FAIL (an environment fact is not an engine
      defect), and the oracle exits 0 with a loud
      "PASS with N BLOCKED(environment) leg(s)" -- never fake-passed
      as live, never misreported as an engine failure. THIS session's
      actual outcome: (a) -- §(1) CONFIRMED: the key file is an
      unfilled template (zero-length value), so no live call was
      possible; M1's "with a real provider" clause is recorded in the
      §5 row and exit note as PENDING-ON-ENVIRONMENT, held by the
      standing leg.
  2.7 **Mock-fixture scenarios (additive; zero edited fixtures):**
      adapters/mock/fixtures.py's `_skeleton()` gained a `gates=`
      param (default byte-identical to the pre-step fixture --
      DRAFT_SKELETON_UT1 unchanged); DRAFT_SKELETON_M1 = the UM.1
      skeleton whose §(3) carries the U3.6 tier markers
      ([T0:py_compile] + [T0:ledger_roundtrip] -- both hermetic,
      deterministic, and §2.5's own named T0 pair; T1 pytest
      deliberately not in the M1 toy: the tier machinery is U3.6's
      already-proven ground, and the M1 arc's job is the
      CONTINUATION, not re-proving tiers). Exactly TWO scripted
      scenarios cover the whole arc (the draft intent key + the
      deterministic "EXECUTE UM.1: ..." key) -- the green verify pass
      and the deterministic closure touch no provider at all.
  2.8 **Docs (same commit):** end_to_end.md §5 U3.8 row -> DONE in
      the U3.2-U3.7 row style (with the keyed-leg caveat stated) +
      the NEW "P3 exit state" note under §5's exit condition (M1
      ACHIEVED with one named caveat -- the actual achieved state,
      not the aspiration); §11 ledger THROUGH the ledger CLI
      (transition U3.8 active --step step.21; close U3.8 PENDING;
      hash backfilled in the gdiff commit -- the step.13-20 shape).
      tests/testing.md updated in the SAME commit as the tests it
      inventories (its own maintenance rule, operator-requested):
      new test_m1_arc.py section (16 rows), phase-overview and
      totals reconciled to 27 files / 402 functions / 565 collected,
      the pytestmark-guard note, the 8-corpora + m1_oracle.sh
      pointer. docs/orchestrator_state_machine.md's does-NOT-do list
      annotated (U3.8 delivered). Stale "is U3.8's M1" claims in
      app/cli.py + app/orchestrator/closing.py -> "DELIVERED at
      step.21"; app/orchestrator/__init__ docstring + exports.
      design.md deliberately UNTOUCHED -- checked for drift, found
      none: §4 already describes exactly what the arc now does, and
      naming U3.8 per-step delivery there would duplicate the
      ledger's job (the "only where genuinely drifted" rule).

EXPLICITLY OUT OF SCOPE (named here per the operator's brief, not
silently built -- and NOT continued into after this step, per the
operator's explicit P3-closes-here scope boundary):
  - P4 (Track B: B3UBOT.sgr -> SQLite -> OO API -> CLI).     -> next phase(s),
  - P5 (CCS plane / b3u client; T3 stays refusing).             operator-
  - P6 (second adapter / agnosticism gate / T4).                directed
  - PAL-narrated retro (step.20's named post-M1 upgrade --
    M1 is now closed, so it is ELIGIBLE, but not begun).     -> later
  - Auto re-draft on amendments (Q-17-A stays open).         -> later
  - Resolving d9 beyond its recorded V0 default.             -> later
  - Pushing anything anywhere (git_push still refuses; B-3). -> never
    (without explicit operator direction)

## (3) Verification gates

    G1  tests/test_m1_arc.py 16/16 PASS live: the full mock arc
        (one invocation, five stages, INTENT..CLOSED history), the
        mechanical form assertions (parser + all four §(0) stamps;
        ledger-table byte round-trip with the real hash; both frozen
        instances through the REAL bin/dagModel), the committed
        exemplar pin, honest stop/resume/terminal/refusal legs, CLI
        legs -- scripted adapters, injected I/O, zero network.
    G2  scripts/m1_oracle.sh live this session: boot 4/4, mock arc
        19/19 PASS (one real `b3ubot run --yes` through the real
        CLI on a scratch git workspace; in-oracle substrate
        round-trips both instances; egress-trail count), keyed leg
        honest SKIP naming the adapter's own is_available() reason,
        exit 0. The script is a PERMANENT standing oracle -- re-run
        in a keyed environment, the keyed leg goes live unchanged.
    G3  The three-outcome keyed handling exists IN THE ORACLE (not
        prose): (a) skip on unavailable; (b) the full check battery
        on a live completion; (c) BLOCKED(environment) on
        billing/credit/auth failure -- named evidence line, counted
        separately from FAIL, loud in the summary. The environment
        fact behind this session's (a) established WITHOUT printing
        key material: ~/.anthropic_api.env is an unfilled template
        (zero-length value -- awk length() + subshell sourcing
        probe, §(1) CONFIRMED).
    G4  The substrate round-trip oracle, both halves: (i) per-run
        roundtrip_verify on both frozen instances in test + script;
        (ii) dag/instances/m1_execution_example.dag committed
        (captured verbatim from the live smoke's closed workspace),
        registered as m1_exec, and the FULL dag regression suite
        run green with it this session: matrix 8/8 4-way
        (C++/Py/JS/Rust), npass 8/8, quirks 8/8 baseline, 10
        subproject PASS / 0 FAIL / 0 SKIP.
    G5  The continuation composes, never reimplements: arc.py calls
        the five delivered stage functions with their own
        signatures, adds NO state-machine edge, and the per-stage
        tests (steps 14-20's 220) all still pass unchanged --
        zero regressions, zero edited tests (the fixture builder's
        gates= param is default-byte-identical, asserted by the
        untouched drafting/ratify/executing/verifying/closing
        suites passing).
    G6  Full suite before this step's edits: 536 passed / 11
        skipped (confirmed live, matching step.20's reported
        final). After: 552p/11s pre-step-file (+16 m1_arc);
        554p/11s once this step.21.txt lands in the corpus (+2
        self-inclusive pickups -- the steps 10-20 growth pattern).
    G7  Scope boundary held: P3 CLOSED and NOTHING beyond it --
        no P4/P5/P6 artifact, no PAL retro, no Q-17-A, no d9
        re-resolution, nothing pushed anywhere; this repo's own
        steps/ + dag/instances/ untouched by every test (asserted
        in-suite; the ONE deliberate dag/instances addition is the
        committed exemplar, made by THIS session's own hand, not by
        a test); this session stops after U3.8.
    G8  §11 ledger row updated THROUGH the existing `ledger` CLI
        (transition -> active --step step.21, close -> done/PENDING),
        against the REAL end_to_end.md -- dogfooding, the
        step.13-20 precedent. tests/testing.md updated in the SAME
        commit as the tests (its own rule).
    G9  M1's "indistinguishable in form" bar checked artifact by
        artifact MECHANICALLY (G1's form assertions), and the
        engine-produced closure's artifact set matches what steps
        13-20 left by hand: step.N.txt with §(0) stamps + §(7) real
        hash, step.N.diff.txt, step.N.gdiff.txt, done ledger row
        with real hash, 2 machine commits, frozen evidence graphs.
    G10 b3ubot porcelain clean after each commit; retro pair via the
        SHARED v3_claude/scripts/step_gdiff (steps/step.21.gdiff.txt);
        exactly 2 commits (work + gdiff-with-hash-backfill-folded-in),
        the step.7-20 precedent.

## (4) LOCKs

    C-21-A  b3ubot porcelain clean after each commit.
    C-21-B  THE ARC COMPOSES, NEVER REIMPLEMENTS: run_uow_arc adds
            no state-machine edge, no stage logic, no gate of its
            own -- it dispatches the five delivered stage functions
            and stops exactly at design §4's two human gates. Any
            future stage change flows through to the arc for free;
            arc.py must never grow stage-local behavior.
    C-21-C  THE M1 CLAIM STAYS HONEST: anywhere M1 is stated (the
            §5 row, the exit note, this file), the real-provider
            clause carries its actual status. Until the keyed leg
            has run live, that status is pending-on-environment --
            held by scripts/m1_oracle.sh's standing keyed leg --
            and a future live run updates the record THEN, not
            before. Never fake-passed, never quietly dropped.
    C-21-D  KEY MATERIAL IS NEVER PRINTED: the environment probe
            reported presence/absence and value LENGTH only; the
            oracle script never echoes the key and greps its own
            logs for leakage (B-4). This session printed no key
            byte and attempted no call against an empty credential.
    C-21-E  THE ENGINE-PRODUCED EXEMPLAR IS EVIDENCE, NOT A BUILD
            ARTIFACT: dag/instances/m1_execution_example.dag is the
            verbatim frozen instance of a real arc run -- it is
            never regenerated in place, never hand-edited; a future
            better exemplar is a NEW file + corpus registration
            (the monotonic-id spirit applied to corpora).
    C-21-F  THE PER-STAGE COMMANDS REMAIN FIRST-CLASS: `run` is a
            composition, not a replacement -- draft/ratify/execute/
            verify/close stay documented, tested, and usable one
            stage at a time (the operator's own working style).
    C-21-G  NO SILENT SCOPE CREEP: P4/P5/P6, the PAL retro,
            Q-17-A, d9's final policy all named out of scope in (2)
            and confirmed not started (G7); the operator's explicit
            "P3 closes here" direction is honored.
    C-21-H  standing locks inherited: B-3 (no disclosure/no push);
            B-4/B-5 (keys env-only + never logged; every provider
            exchange egress-recorded -- the arc's stages record
            exactly as they did standalone); C-15-B (the tool
            trail); C-20-B (frozen evidence never rewritten -- the
            arc re-runs REFUSE over frozen instances via the
            existing walls); C-20-C (the machine retro transcribes,
            never invents).

## (5) Open questions

    Q-21-A  Should `run` re-verify a resumed arc's ALREADY-green
            stages (e.g. re-run verify when resuming at RETRO)?
            DECIDED HERE for V0: no -- the engine record + frozen
            evidence are the proof the stages ran (Q-20-B's own
            reasoning, inherited); a paranoid re-check would
            double-spend the oracle on every resume. Revisit only
            if a real staleness incident surfaces.
    Q-21-B  Should the keyed leg's outcome (b), when it eventually
            runs live, ALSO capture a real-provider exemplar corpus
            (a keyed sibling of m1_execution_example.dag)? LEAN:
            yes, as a one-time act in whatever step witnesses the
            first live run -- the corpus would then carry both a
            mock-provider and a real-provider engine-produced
            instance. Left for that step; not pre-built.
    Q-21-C  Where does the PAL-narrated retro upgrade (step.20's
            named post-M1 item, now eligible) get scheduled? LEAN:
            as a P3-numbered follow-up UoW (U3.10-style, §12's
            next-free-number rule) whenever the operator promotes
            it -- it is a quality upgrade inside a closed phase,
            not a new phase's entry. Operator's call; recorded
            only so eligibility is not lost.

## (6) Acceptance

    - [x] app/orchestrator/arc.py: the state-dispatch continuation
          -- one invocation, five composed stages, gates-only stops,
          resume-for-free, bounded dispatch (2.1, 2.2, G1, G5).
    - [x] `b3ubot run` CLI: the sibling row's capstone; Q-8-B as the
          union rule; honest gate-stop exit 0 (2.3, G1).
    - [x] The M1 oracle, both halves, both PERMANENT: 16-test pytest
          arc + scripts/m1_oracle.sh live (2.4, G1, G2).
    - [x] Substrate round-trip decided BOTH ways and proven: per-run
          in-oracle + the committed engine-produced m1_exec corpus,
          dag regression suite green 8/8 4-way (2.5, G4).
    - [x] Keyed leg: three-outcome handling in the oracle; THIS
          session's outcome (a) recorded with the exact environment
          fact, key material never printed (2.6, G3, C-21-C/D).
    - [x] Mock scenarios additive; DRAFT_SKELETON_UT1 byte-identical;
          zero edited tests/fixtures (2.7, G5).
    - [x] 16 new tests, zero regressions (536p/11s -> 552p/11s ->
          554p/11s with this file) (G1, G6).
    - [x] end_to_end.md §5 row + P3 exit-state note + §11 via the
          ledger CLI; tests/testing.md same-commit; state-machine
          doc; stale-docstring sweep; design.md checked, no drift
          (2.8, G8).
    - [x] Scope boundary held: P3 CLOSED, nothing beyond started
          (G7, C-21-G) -- this session stops after U3.8.
    - [x] Retro pair: this file + step.21.diff.txt + step.21.gdiff.txt
          via the SHARED scripts/step_gdiff (G10).
    - [x] Exactly 2 commits (work + gdiff-with-backfill), not 3 (G10).

## (7) Hash backfill
WORK commit (app/orchestrator/arc.py + errors + __init__ + cli +
mock fixtures + tests/test_m1_arc.py + scripts/m1_oracle.sh +
dag/instances/m1_execution_example.dag + corpora.sh + READMEs +
end_to_end.md/testing.md/docs + this file):  69c2df9
GDIFF commit (step.21.gdiff.txt via scripts/step_gdiff):  (HEAD)

End of step.
