step.18.diff.txt -- narrative retro for step.18 (U3.5: EXECUTING loop
-- scheduling from the execution DAG's ready frontier; tool acting +
evidence recorded into the step file AND node kv state; Q-15-B
resolved)

Executed: 2026-07-21, same session as the draft (single pass, the
step.7/9/10/11/14-17 convention -- executing.py, the two new error
classes, the shell.py env scrub, the compiler ASCII fix, the mock
execution fixture, the `execute` CLI subcommand, the 32+4 test
additions, the design.md/end_to_end.md/tool_surface.md/
orchestrator_state_machine.md edits, and the step file all built
together). Companion artifact: step.18.gdiff.txt (automated,
scripts/step_gdiff). Per the operator's explicit per-UoW checkpoint
rule (HARD), this session STOPS after U3.5 -- U3.6 is not started.

## Plan vs actual

```
+---+----------------------------------+------------------------------------------+
| # | Planned (brief)                  | Actual                                   |
+---+----------------------------------+------------------------------------------+
| 1 | Decide where instances live for  | steps/<step>.execution.dag BESIDE the    |
|   |   a target workspace (steps/    | skeleton -- the U3.4 amendments-file     |
|   |   sibling or a dag/ dir), check  | precedent; §2.12's "file trees for ...   |
|   |   §2.12's wording                | workflow DAG instances" + §4's "the      |
|   |                                  | step's evidence graph" read as per-step  |
|   |                                  | workspace content. A workspace dag/ dir  |
|   |                                  | rejected: in the one real workspace      |
|   |                                  | (this repo) dag/ IS the substrate --     |
|   |                                  | name collision with the grammar fork.    |
| 2 | Check whether bin/dagModel is    | IT IS BUILT AND WORKS -- checked live    |
|   |   available; design honestly     | before designing (from_dag ->            |
|   |   around absence if not          | to_dag_text byte-identical on            |
|   |                                  | execution_example.dag). So instances     |
|   |                                  | are round-trip-verified at write +       |
|   |                                  | completion, droppings cleaned; absence   |
|   |                                  | still handled honestly BOTH ways         |
|   |                                  | (named "skipped" outcome for writes;     |
|   |                                  | loud refusal for RESUME -- reading      |
|   |                                  | back IS parsing).                        |
| 3 | Map each V0 node kind to REAL    | context_assembly = U3.3's bounded probe  |
|   |   actions using ONLY what        | (intent = title + §(2)); provider_run =  |
|   |   exists; decide provider_run's  | one PAL call, first entry "EXECUTE       |
|   |   prompt shape and tool_call's   | <uow>: <title>" (Q-4-B routing kept),    |
|   |   "the work"                     | proposal = path line + complete content  |
|   |                                  | (the ask arc widened to let the          |
|   |                                  | provider pick the file); human_gate =    |
|   |                                  | in-loop diff review, injectable I/O,     |
|   |                                  | --yes bypass; tool_call = the gated      |
|   |                                  | write_file apply (C-4-B AT the write);   |
|   |                                  | oracle_gate = run_gate on the            |
|   |                                  | scripts/oracle_gate.sh convention, only  |
|   |                                  | when the node exists, exit-as-data.      |
| 4 | Keep the loop generic over the   | run_ready_frontier() is shape-agnostic:  |
|   |   DAG; test with a synthetic     | ready = precedes-preds all done; other   |
|   |   branching DAG                  | edge kinds are annotations. Proven on a  |
|   |                                  | diamond, a multi-root join, an           |
|   |                                  | annotates back-edge (would be a cycle    |
|   |                                  | if it constrained), a real cycle         |
|   |                                  | (refuses), an unknown kind (refuses).    |
| 5 | Q-15-B MUST be answered here     | RESOLVED: scrubbed child env,            |
|   |                                  | deny-by-pattern on secret-shaped names   |
|   |                                  | (no bare AUTH -- SSH_AUTH_SOCK is a      |
|   |                                  | socket path, not a credential). In       |
|   |                                  | _execute, so run_shell AND run_gate      |
|   |                                  | share it. Proven with real python3 -c    |
|   |                                  | children printing ABSENT.                |
| 6 | Decide the end-of-loop state and | Completion -> VERIFYING with             |
|   |   V0 failure semantics           | verification ABSENT (U3.6's job; the     |
|   |                                  | RATIFIED-sat-until-U3.5 mirror).         |
|   |                                  | Failure/decline: halt loudly, node kvs   |
|   |                                  | persisted, state STAYS EXECUTING,        |
|   |                                  | re-run resumes -- NOT d9's budget.       |
| 7 | Evidence into the step file AND  | Node kvs on every change (running        |
|   |   node kv state                  | first -- crash-honest); §(0) ENGINE      |
|   |                                  | EXECUTION RECORD stamped ONCE on         |
|   |                                  | completion (the step.16/17 pattern +     |
|   |                                  | re-parse + double-stamp guards). A halt  |
|   |                                  | stamps nothing in §(0) -- the DAG is     |
|   |                                  | the LIVE surface (§2.13), the step file  |
|   |                                  | the durable ledger fact (Q-18-A).        |
+---+----------------------------------+------------------------------------------+
```

## Surprises / genuine findings

1. **The section-sign character broke the instance round-trip** -- the
   one real mid-build defect. U3.1's compiler wrote "design.md §4" into
   generated node descriptions; tools.write_file writes utf-8 while the
   substrate reads/writes .dag as latin-1 bytes, so the first
   roundtrip_verify FAILED (utf-8 "§" reread as latin-1 "Â§"). The
   hand-authored execution_example.dag turned out to already avoid the
   character ("design.md section 4") -- upstream knew. Fixed UPSTREAM
   in compiler.py (ASCII "section 4", with the why as a comment), plus
   a BYTES-level comparison in roundtrip_verify (the honest invariant:
   what is on disk vs what the substrate would rewrite).
2. **The egress trail doubles as the resume source, for free.** A
   resumed human_gate/tool_call needs the proposal, which lives in no
   engine state -- but B-5's unconditional egress record (C-4-C)
   already holds the full response text, keyed by the run_id the
   provider_run node's own kv preserves. The resume-after-decline test
   passes a MustNotBeCalledAdapter: zero provider calls, identical
   applied result. Evidence discipline paying rent, not costing it.
3. **`running` written BEFORE the handler** fell out of the
   files-as-truth posture rather than being planned: if the instance is
   the live progress surface, a crash mid-node must be visible in the
   file. It also made the resume set (pending/failed/declined/running)
   self-evident.
4. NOT a surprise, verified as hoped: the generic frontier engine
   needed no special-casing for the linear template -- the template is
   just the degenerate DAG, exactly the point of scheduling from a
   frontier.

## Scope discipline

U3.6/U3.7/U3.8, Q-17-A's auto re-draft, Track B, and content-aware
compilation: none started (G8). No VERIFYING-tier code exists; no
VERIFYING->RETRO or VERIFYING->EXECUTING call site anywhere. The
operator's explicit stop-after-U3.5 boundary is honored -- control
returns after the retro pair.

## Numbers

443 passed / 11 skipped before -> 479 passed / 11 skipped after the
build (+32 tests/test_orchestrator_executing.py, +4 Q-15-B tests in
tests/test_tools_shell.py) -> 481 passed / 11 skipped with this
step's files in the corpus (+2 self-inclusive pickups). Zero
regressions; zero existing tests edited (the stale-docstring updates
touched no pinned strings -- checked by grep before editing).

End of retro.
