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.