step.5.diff.txt -- narrative retro for step.5 (U3.9 first slice: DAG
substrate adoption)

Executed: 2026-07-18, same day as the skeleton. Companion artifact:
step.5.gdiff.txt (automated, scripts/step_gdiff). This is the first
step to touch `dag/` since it was cloned in at founding (f1ebb47,
2026-07-12) -- six days of sitting completely unbuilt, unproven fork
content, now real and green.

## Plan vs actual

Plan named 12 files to fix, all one root cause (a directory-depth
miscount from the fork sitting one level shallower than the original).
Actual: the plan's own root-cause diagnosis was RIGHT, but its FILE
LIST was incomplete -- running the real cascade (not just fixing the
12 known files and declaring victory) surfaced 4 more instances of the
exact same bug, undiscoverable without actually executing the code.

```
+---+----------------------------------+------------------------------------------+
| # | Planned (§)                      | Actual                                   |
+---+----------------------------------+------------------------------------------+
| 1 | §2.1-2.3: 12 files (10 wc        | Confirmed correct AND complete for what  |
|   | scripts + run_regression +       | was named. All 12 fixed exactly as       |
|   | discovery.py)                    | planned -- one hop removed, path prefix  |
|   |                                  | rewritten, verified individually before  |
|   |                                  | moving on.                               |
| 2 | (not named at drafting -- could  | Running `bash dag/tests/run_tests.sh`    |
|   | not have been, since the         | surfaced 3 MORE instances of the SAME    |
|   | skeleton's research pass never   | miscount, in files the research pass     |
|   | actually EXECUTED the code, only | never had reason to inspect (they don't  |
|   | read it)                         | contain the words "wc" or "VERSION_HOME" |
|   |                                  | that the research grep-hunted for):      |
|   |                                  | dagModelRust/Cargo.toml (a Cargo path    |
|   |                                  | dependency, 4 ../ -> 3), dagModelJs/     |
|   |                                  | dag_model/loader.mjs (a JS resolve()     |
|   |                                  | chain), dagModelPy/dag_model/loader.py   |
|   |                                  | (the SAME chain in Python). Same fix     |
|   |                                  | pattern each time; found only because    |
|   |                                  | the actual import/build was attempted.   |
| 3 | (not named)                      | A FOURTH instance, more subtle:          |
|   |                                  | dagModelPy/tests/check_scb_quirks.py     |
|   |                                  | duplicates loader.py's path-resolution   |
|   |                                  | logic INDEPENDENTLY rather than          |
|   |                                  | importing it -- fixing loader.py did NOT |
|   |                                  | fix this file; it needed its own,        |
|   |                                  | separate fix. Found only because the     |
|   |                                  | regression suite's "quirks" leg          |
|   |                                  | specifically exercises this script,      |
|   |                                  | which a narrower "just build the OO      |
|   |                                  | APIs" pass would not have reached.       |
| 4 | (not named)                      | dagCommands/cli's "apply" test needs     |
|   |                                  | dag/dagCommands/cppcc/bin/dagCommands -- |
|   |                                  | a SECOND cppcc frontend (for the         |
|   |                                  | command-language grammar, separate from  |
|   |                                  | dag/cppcc's dagModel.sgr frontend) that  |
|   |                                  | nothing in the build cascade ever        |
|   |                                  | triggers. Not a path bug -- a genuinely  |
|   |                                  | missing build step in the inherited      |
|   |                                  | tooling. Built directly (`cd             |
|   |                                  | dag/dagCommands/cppcc/build && bash      |
|   |                                  | makeall`), not fixed structurally        |
|   |                                  | (C-5-C: this is fork content, minimal    |
|   |                                  | targeted intervention only).             |
+---+----------------------------------+------------------------------------------+
```

## Gate evidence

```
+------+---------------------------------------------------------------------+
| G1   | All 12 originally-named fixes re-run individually after editing:    |
|      | the 10 wc scripts each produce a real non-zero total with zero      |
|      | "No such file" errors; run_regression's VERSION_HOME computed and   |
|      | printed directly (= v3_claude, correct); discovery.py's             |
|      | _DEFAULT_VERSION_HOME checked via a standalone Python snippet (=    |
|      | v3_claude, correct). The 4 unplanned fixes verified the same way    |
|      | before the full cascade was re-attempted (Python import chain       |
|      | walked directly; JS loader imported standalone; Rust path checked   |
|      | via `ls -d`).                                                       |
| G2   | `bash dag/tests/run_tests.sh` (3rd attempt, after all 16 fixes +     |
|      | the missing dagCommands parser build): "bot/dag summary: 10         |
|      | subproject PASS, 0 subproject FAIL, 0 SKIP."                        |
| G3   | Zero skips confirmed in the final run's full log (grepped, not      |
|      | assumed) -- every one of the 4 renderers (cctdag, Py, JS, Rust) and  |
|      | every subproject ran for real.                                      |
| G4   | The regression report read directly: byte-identity matrix -- 6      |
|      | corpora x 4-way, PASS=6 FAIL=0 SKIP=0 (24/24 cells); 2-pass          |
|      | round-trip -- PASS=6 FAIL=0 SKIP=0, pass1 counts == pass2 counts    |
|      | for every corpus (including the large proj_d1Iso corpus at          |
|      | 1/643/15151 dag/node/edge counts); quirk-landscape -- PASS=6 FAIL=0 |
|      | SKIP=0, all six corpora at the E1=PRESENT/E2=ABSENT/E3=PRESENT      |
|      | baseline.                                                            |
| G5   | ccs_python/ccs_js/ccs_scb_rs (the sibling SCB repos this step's     |
|      | fixes now correctly REACH) confirmed `git status --short` clean --  |
|      | READ, never written. bet3/bot/dag not present in this environment   |
|      | at all -- no possibility of cross-repo interference.                |
| G6   | `make venv && make smoke && make test` (steps 1-4's suite): 38      |
|      | passed, 8 skipped -- byte-identical to step.4's own baseline, this  |
|      | step's changes are 100% confined to dag/ as C-5-B required.         |
+------+---------------------------------------------------------------------+
```

Bonus, not a named gate but worth recording: the committed `srcloc/`/
`incloc/` bootstrap sources under `dag/cppcc/` and
`dag/dagCommands/cppcc/` came back **byte-identical** after this
step's from-scratch rebuild (`git status` on those paths: clean). The
fork's self-hosting bootstrap state was not just present but
genuinely CONSISTENT -- cppcc regenerating its own committed
generated sources from `dagModel.sgr`/`dagCommands.sgr` reproduces
exactly what was cloned in six days ago, untouched since.

## Findings for later steps

1. **A research pass that reads code cannot find every instance of a
   bug class that only manifests when the code actually RUNS.** The
   skeleton's ground-truth research was thorough and correct about
   the 12 files it found (all 12 needed exactly the fix predicted) --
   but it necessarily missed the 4 more instances that only surface
   mid-execution, because grepping for "VERSION_HOME" or a specific
   filename pattern cannot find a bug hiding in a Cargo.toml path
   dependency or a duplicated-not-shared path-resolution block in a
   test script nobody thought to search for by name. Lesson for any
   future "fix this bug class across a fork" step: budget for the
   plan being INCOMPLETE, not just for it being wrong -- the fix is
   to actually run the thing, not to research harder.
2. **Independent duplication of logic is a bug multiplier.**
   `check_scb_quirks.py` re-implementing `loader.py`'s path
   resolution instead of importing it meant ONE conceptual bug had to
   be fixed TWICE, in two unrelated-looking files, with no shared
   root a single edit could close. Worth naming even though fixing
   the duplication itself (having check_scb_quirks.py import
   loader.py's resolved `_V3` instead of recomputing it) is out of
   this step's scope (C-5-C -- targeted fixes only, not fork
   refactoring).
3. **A "build if missing" guard only self-heals paths the loop
   actually visits.** `dag/cppcc/bin/dagModel` self-healed on a
   second pass because SOME subproject's guard (dagModelPy's)
   happened to build it as a side effect. `dag/dagCommands/cppcc/bin/
   dagCommands` never would have, no matter how many passes -- no
   subproject in the fixed loop order touches that particular
   directory. The difference between "will self-heal on retry" and
   "will fail forever" isn't visible from reading the failure message
   alone; it took tracing which build step actually produces the
   missing artifact to tell them apart.
4. **A shared, idempotent, workspace-scoped output directory
   (`v3_claude/dags/`) is a real, deliberate design choice, not an
   accident to route around.** Worth remembering for U3.9a and any
   future work that touches `bot_init`: it is correct for this repo's
   regression run and any other project's (present or future) to
   read/write the SAME location, describing the SAME real workspace.

## Numbers

```
+--------------------------------------+----------------+
| files fixed (planned)                | 12             |
| files fixed (found live, unplanned)  | 4              |
| missing build step found + resolved  | 1 (dagCommands |
|                                       | cppcc frontend)|
| full cascade attempts to green       | 3              |
| regression matrix cells              | 24/24 PASS     |
| regression corpora                   | 6/6 PASS (both |
|                                       | npass+quirks)  |
| BUILD commit edbe4ed                 | 16 files, +151 |
|                                       | -96            |
+--------------------------------------+----------------+
```

U3.9 (this slice) done -> U3.9a (first `execution`-kind instance
authored + round-tripped) split out as new, separate, still-open work
-- real content-authoring on TOP of a substrate this step only proved
still works. Ledger updated in this RETRO commit per the §0 execution
contract; the P3 §5 table's U3.9 row text and the new U3.9a row both
land in the SAME commit (§12's re-planning rule).

End of retro.
