mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-10-02 07:59:52 -04:00
e6d98dd1d4
- PhaseEnd_Phase35.md written; CURRENT_PHASE.md archived to phase-ends/logs/Phase35.md (R19); DIGEST §0/§2 appended (step 3b) - T0–T8 + T5b across S94–S96 (S94 died at 91 % without a checkpoint; S95 recovered it from the transcript; S96 finished) - close: R22 clean fleet 218/218; tools-health OK (S1 10,180/10,180, 0 violations; dedup-check 3173/0; the guard 0) - rules: R96–R99 ratified at gate 1; candidates (a)–(g) proposed for Phase 36 gate 1 - v2.0.0 -> v2.1.0
978 lines
112 KiB
Markdown
978 lines
112 KiB
Markdown
# CURRENT_PHASE — Phase 35: Gen3 opens — the dedup phase, "one source per unique function" (v2.0.0 → v2.1.0)
|
||
|
||
> **Gate 1 approved 2026-09-08 (Drew, plan mode, Max, Fable 5.1; session S94).** Rules **R96–R99 ratified at gate 1** (the Phase-34
|
||
> candidates (a)–(d); binding; full text in `phase-ends/DIGEST.md` §3). The approved plan is reproduced VERBATIM at the end of this file
|
||
> (§"Approved plan") — its `~/.claude/plans/` copy is not part of the repo. **Gen3 order (Drew, S94): dedup → pins → structs → names,
|
||
> one phase each, planned one at a time; this phase is dedup only.** Gen3's charter: `docs/gen3-handoff.md` + `docs/gen3-standards.md`.
|
||
> **Baseline HEAD at open: `48170fd7f`** (Drew's Phase-34 CLOSE commit = v2.0.0; tree clean; `origin/main == main`).
|
||
> **Build inputs change in this phase (every shared body moves): R22 is owed after every batch that touches `src/`** — the clean fleet run
|
||
> `make clean && make extract-all JOBS=16 && make check-all JOBS=16` → `check-all: 218 passed, 0 failed of 218`, read by exit code (R97).
|
||
> **The 2026-09-02 "leave the dedup backlog" decision is REVERSED at this gate** on evidence read from sotn-decomp's tree (X2): sotn shares
|
||
> stage code once as plain C in `src/st/<name>.h`, instantiated per stage by a `.c` stub that `#include`s it; it does not write duplicates.
|
||
|
||
## Milestone (gate 2 — what Drew confirms, each with its literal output)
|
||
1. `git grep -c '^#define DEFINE_func_' -- src` empty; no `DEFINE_func_X()` site; `src/shared/engine_core.h` absent; every registered body a
|
||
plain-C header under `src/shared/<space>/`.
|
||
2. `tools/share_census.py --check` exit 0: same-vram duplicate copies 0 (or each ledgered with a reason in `config/dedup_exceptions.tsv`); the
|
||
five twin overlays built from one source directory each; cross-vram classes published as a deferred count.
|
||
3. `make tools-health` OK with the S1 invariant strict and the macro-form guard; every consumer updated, frozen or retired per its dictionary row.
|
||
4. `make clean && make extract-all && make check-all` → `218 passed, 0 failed of 218` (R22).
|
||
5. README/wiki/kit regenerated (R75), decision log (R31), SETUP rows (R21), `PhaseEnd_Phase35.md` + DIGEST written; v2.1.0.
|
||
|
||
## Effort / model (R7/R26/R27 — every transition is PROMPTED, never assumed)
|
||
- **Max** for T0's probe, T1, T2's design, T3's probe pair, T4's and T5's tool, T9 (Tier 1 — prompt Drew before starting the close);
|
||
**xHigh** for the mechanical runs (T3's other pairs, T4's batches, T5's batches, T6, T7, T8); **Low** for T0's bookkeeping.
|
||
Max is session-only — ask Drew to re-apply `/effort max` at each session start. **No Ultracode anywhere** (nothing is agent-breadth; the
|
||
parallelism is machine parallelism: L0 at JOBS=16 ≈ 13 min fleet-wide, the clean fleet run 3–5 min).
|
||
- **Drew-only (R6):** every `git push`; the gate-2 confirmation; the close commit + tag. Claude commits per task (R42 for banks: the moment a
|
||
batch is green, before the next command that can touch `src/`).
|
||
|
||
## Tasks (plan order; one commit each; ☐ → ☑ with the verify line quoted in the log)
|
||
- ☑ **T0** — Open + ground (S94): this file; DIGEST §0/§3 (R96–R99); `.gitignore` `.run/P35/`; the R22 baseline from clean
|
||
`check-all: 218 passed, 0 failed of 218` in **157 s wall**; `make tools-health` **OK** (exit 0, 474 s — after two red runs fixed at
|
||
their cause: the kit's record copies regenerated by `make kit-corpus`, and R96–R99 dispositioned in `config/kit_coverage_map.tsv`);
|
||
the probe on ov_SC06_033: **34/34 objects byte-identical** in the include form, the binary `BYTE-IDENTICAL` both ways, build 1.12 s →
|
||
0.52 s wall (CPU 12.7 s → 5.7 s), warnings 801 → 663 (all 137 macro-redefinition warnings gone), `.d` 11.6 KB → 182 KB
|
||
(`.run/P35/probe/probe_ledger.txt`).
|
||
- ☑ **T1** (S94) — `tools/share_census.py` + `config/dedup_exceptions.tsv`: `selftest: 7/7 verdicts correct`; `362,389 sig instances ·
|
||
362,389 classified · 0 UNACCOUNTED · 10,180 h_exact classes`; the four controls as measured (see the log); SETUP + dictionary rows.
|
||
- ☑ **T2** (S94) — the health chain learns the header + twin forms while the macro form still builds (16 tools; the source-dir
|
||
oracle; `share_census.header_defs` the one reader). Negative control on the unchanged tree: the chain's substantive rungs
|
||
green (audit-binaries OK · dedup-check 2220/0 · cdecl · report + `progress.py --check` fresh · doc_links · wiki_render · kit_lint ·
|
||
cookbook-index · gccmap_cites · ghidra_roster), one red rung — the kit's verbatim copies of the edited tools — fixed by
|
||
`make kit-corpus` (17 copies) with the rungs after it re-run individually by exit code. The +7 count correction published.
|
||
- ☑ **T3** (S94) — twin binaries → one source directory: the five pairs collapsed (probe SC01_005/006 with the race test; SC03_118/119 +
|
||
SC02_000/003 equal carves; SC04_018/019 + SC03_014/015 with the primary's carve, interleave + pads regenerated, R60 checks), each
|
||
twin and primary BYTE-IDENTICAL, 166 source files deleted; the census: same-vram backlog 4,667 → 1,099 classes; the clean fleet
|
||
run after the last pair (see the log).
|
||
- ☑ **T4** (S94) — `tools/macro_to_header.py`: 246,347 sites in 3,818 TUs → 2,215 per-function headers under `src/shared/<space>/`
|
||
(213/213 binaries and 4,121/4,121 objects byte-identical), the legacy headers converted (clearTbl40 = the parameterized control),
|
||
the whale moved, 7 alias bodies bound, the registry text-edited, `engine_core.h` deleted; `--verify OK`; R22 `check-all: 218
|
||
passed, 0 failed of 218` in 145 s.
|
||
- ☑ **T5** (S94–S96) — `tools/share_body.py` (+ `--repair-registry`, `--reexemplar`, `share_body_cycle.sh`): bucket 0 183 classes → 135 shared / 48 ledgered; bucket `new` 915 → 914 shared / 3 ledgered; S1 10,180/10,180 satisfied, 0 violations; registry 2,220 → 3,135 groups; same-vram backlog 1,099 → 51 classes (= the ledger); R22 218/218. Recovery history (S94 died mid-task): `tools/share_body.py`
|
||
built + probed (commit `0bccef901`); bucket 0 (`--bucket extend`, the registered-incomplete classes: 183 at the start) run twice —
|
||
the first pass committed (`571374b97`: 788 members added to the registry, 141/141 + 146/146 binaries green per binary), the second
|
||
run finished after the session died and its output (170 sites → include in 38 TUs of 8 overlays, 55 TU-CONFLICT ledger rows) is
|
||
UNCOMMITTED on disk. Residue: 55 classes / 325 (class, binary) pairs stay private, 317 of them still LISTED as registry members
|
||
(the first pass extended wholesale); three tool defects found by S95 (the cause extractor, the cross-batch edit loss, the ledger
|
||
reason). NEXT inside T5: commit the run-2 bank → fix the tool → repair the registry → replay the suspect rejections → decide the
|
||
E_func_80168B70 exemplar → bucket `new` (915 classes / 3,567 private sites) in bands, one batch per run, R22 after each.
|
||
Plan text (unchanged): bucket 0 `--extend`, then the same-vram buckets largest reach first (no trivial exception); cross-vram
|
||
classes untouched; `share_census --check` same-vram unregistered = 0 or ledgered.
|
||
- ☑ **T6** (S96) — consumers: **14** macro-era tools FROZEN (`tools/frozen.py`, main()-time refusal; the plan's 7 + 7 the guard census
|
||
found), **4** retired to `tools/sunset/` (dedup_propagate, dedup_extend → share_body.py; macro_draft; test_reconcile_ledger), 12 live
|
||
tools' dead macro branches dropped, the `ast` macro-form guard in `tool_census --check` (0 LIVE hits; the pre-T4 tree flags 15 —
|
||
the negative control), `progress.py`'s empty-shared fold corrected (+459 denominator, REAL −10,211 → EMPTY), kit corpus, SETUP;
|
||
`tools-health: OK`.
|
||
- ☑ **T7** (S96) — S1 strict (`--strict-macros --strict-text`) + `--selftest` in `make tools-health`; C2c/C2d in `dedup_integrate --check`;
|
||
`progress.py` fields (105,007 bodies written once; 160 / 51 duplicate copies, all ledgered) + the dated corrections (+7, +459, REAL
|
||
−10,211) + the README sentence; the gate negative-controlled in place (exit 1 naming the class; C2d naming the member); the second
|
||
oracle's finding published: 380 texts deferred inside cross-address classes, 38 texts / 2,030 sites byte-variant — PENDING Drew.
|
||
- ☑ **T5b** (added S96, Claude's delegated decision; done S96 — 38 groups / 2,030 sites, 138/138 green, R22 218/218) — the text tier: registry `tier: h_text` (text hash; per-member `h_exact` in the
|
||
verbose member form), `dedup_integrate` C1 per member; `share_census` text classes at one address (same text, ≥2 distinct TUs, outside
|
||
the deferred/ledgered sets) → the S1 text half a violation, not PENDING; `share_body --bucket text` (header named by the text hash) over
|
||
the 38 classes / 2,030 sites, gated per binary, one batch per run + R22; the two counts (deferred 380/1,668; text-tier shares) in the
|
||
digest + README sentence. Effort: high (Drew's default).
|
||
- ☑ **T8** (S96; doc_links strict 0 broken, wiki 32/32, cookbook §453 + index, decision log, accelerators, DIGEST §4, the memory) — the record: wiki (4 pages), how-to ch.10, README:117, the charter rows as dated snapshots, the cookbook section, decision log
|
||
(R31), accelerators, DIGEST §4, sunset rows, the memory rewritten; doc_links/wiki_render/cookbook_index/kit_coverage green.
|
||
- ☑ **T9** (S96, xHigh by Drew's choice) — close (Tier 1): R22 → 218/218; tools-health OK; the metrics table; the reviewer sequence; PhaseEnd + DIGEST + log archived; v2.1.0.
|
||
- Rules check (P6): after T3 and after T7.
|
||
|
||
## Decisions (owner's words, in order)
|
||
- S94 gate 1 (AskUserQuestion): twin binaries → "One source directory per payload"; cross-address classes → "Census + defer to the names
|
||
phase"; the 62 macro-era tools → "Freeze with a loud refusal"; the four rule candidates → "Ratify all four as R96–R99".
|
||
- S94 (plan): no "trivial" exception — a duplicated 3-instruction accessor is still one function (this project already shares 5-instruction
|
||
bodies).
|
||
- S94 (Drew, mid-planning): "we will need to update our tooling to be aware that we aren't duplicating funcs across overlays anymore and
|
||
that there is only one source for a unique func" → the S1 invariant + the consumer tasks (T2, T6, T7).
|
||
- S95 (Drew, 2026-09-08 evening — the recovery session): "this is a recovery session. last session context filled up and didn't write a
|
||
checkpoint … read last session since the last checkpoint and write an updated checkpoint"; "dont run anything like gates/builds, your
|
||
job is only to capture the info from the session and create an updated checkpoint." → S95 ran no gate, build, census or tool; it read
|
||
the S94 transcript (`~/.claude/projects/-home-musashi-bfm-decomp/e902c34e-e4b3-41a6-b1e4-7d2ef019a371.jsonl`), the two T5 commits,
|
||
`.run/P35/share/` and the dirty tree, and wrote the log entries + the 🛑 block below.
|
||
- S96 (Drew, 2026-09-08, after the E_func_80168B70 question): "I am not sure the best answer to this question. I have toggled Max effort.
|
||
now decide the best answer to the question. and then pause so I can switch back to high effort" → **Claude decided at Max: RE-EXEMPLAR
|
||
the header from the majority text** (the tool's own exemplar rule; T4 inherited the 7 late overlays' minority text because the registry
|
||
group had been created for them; the majority's cast-at-call form compiles under BOTH declaration environments, so 141/141 is expected
|
||
with no TU edit, and 7 private copies would beat 134 if not). The offered alternative "fix the 7 TUs' declarations" was mis-stated —
|
||
the header passes one argument, so the TU-side repair would be the 134 units + every other caller of func_80146C3C = Phase 37's
|
||
canonical-declaration work; rejected. **Policy for the other 48 rejected classes:** their headers already carry the majority text and
|
||
their causes are genuine declaration conflicts in the late overlays' units → they stay ledgered TU-CONFLICT with the real diagnostic
|
||
until the types phase (R95); ≈190 private copies of ≈3,900 collapsible, inside the plan's 2–5 % budget (exact count at T5's close).
|
||
- S96 (Drew, after the T7 report): "Max effort set for this decision call. you review the information and decide what is best, make the
|
||
decision, and then pause" → **Claude decided at Max: T5b NOW for the 38 byte-variant same-address texts (a text tier), DEFER the 380
|
||
inside the cross-address classes.** The normalizer masks only the function's own name, so the 38 are the same C; their bytes differ per
|
||
overlay because the ORIGINAL builds compiled that source under per-overlay declaration environments (our TUs carry that environment) —
|
||
the types phase cannot collapse them and the names phase cannot remove them; the include-at-site form already fits (body in the header,
|
||
declarations in the TU, bytes proven per binary) and only the registry's single-hash group blocks it. The 380 are identical bytes at
|
||
thousands of addresses (empty bodies, tiny accessors): one header per address would be undone by the names phase's parameterized form —
|
||
gate 1's deferral stands; both counts are published. A plan change (P5d) under Drew's delegation: **T5b inserted before T8.**
|
||
|
||
## Log (append-only; one entry per step, with the literal verify line)
|
||
- **S94 2026-09-08 — session start.** Load order read; Phase 34 closed at `48170fd7f`; no CURRENT_PHASE.md → new phase. Drew: order
|
||
dedup → pins → structs → names; plan mode at Max. Exploration (two Explore agents, one Plan agent, ~680k agent tokens) + this
|
||
session's own measurements; sotn's sharing model verified from its tree (`src/st/e_red_door.h` + the ~90-byte per-stage stubs).
|
||
- **S94 — T0 in progress.** DIGEST §0 (the opening paragraph) + §3 (R96–R99 in full) written; `.gitignore` `.run/P35/` allowlist added;
|
||
the R22 baseline from clean launched in the background (`.run/P35/baseline/r22_open.log`, `/usr/bin/time` wall clock recorded);
|
||
the harness task list built (10 tasks, R28).
|
||
- **S94 — T0 baseline.** `.run/P35/baseline/r22_open.log`: `extract-all: 217 extracted, 0 failed of 217 (+ main, serial)` ·
|
||
`check-all: 218 passed, 0 failed of 218` · `wall=157.09 s user=2231.42 s sys=542.99 s` · `exit=0`. The fleet's build-time baseline
|
||
for the phase (the 8.8 MB header is preprocessed by every overlay TU today).
|
||
- **S94 — T0 tools-health, run 1 RED (464 s):** `tool_census --check: FAIL` — 2 GAPs, both "corpus copy differs from its source"
|
||
(`decomp-architect/corpus/record/phase-ends/DIGEST.md`, `…/PhaseEnd_Phase34.md`). Cause: the gate-1 DIGEST edit (and a pre-existing
|
||
drift of the P34 record copy behind Drew's close commit); the instrument was right (R40). Fix: `make kit-corpus` (360 copies + 28
|
||
pointers materialised) → `tool_census --check: OK` (`.run/P35/baseline/kit_corpus_open.log`, `tool_census_open.log`).
|
||
- **S94 — T0 probe** (`.run/P35/probe/probe_convert.py`, in-tree then restored by `git checkout -- src/ov_SC06_033`): `engine_core.h: 5147
|
||
define lines, 3516 distinct, 1631 defined twice` · `ov_SC06_033: 34 TUs, 1813 sites, 1813 distinct shared bodies` · `applied: 1813
|
||
headers under src/shared/ov, prelude written, 34 TUs rewritten` · A rebuild (macro form) `wall=1.12 s user=12.72 s`, 801 warnings
|
||
(137 "redefined") · B build (include form) `wall=0.52 s user=5.70 s`, 663 warnings (5 "redefined" — none of them `DEFINE_func_`) ·
|
||
`sha1 1e91d46e… == config/check.ov_SC06_033.sha (BYTE-IDENTICAL)` both ways · **`obj A/B: 34/34 byte-identical, 0 differing`** ·
|
||
`.d` bytes 11,632 → 182,252 (1,813 header dependencies; harmless) · restore verified (`git status` shows only T0's files). The L0
|
||
object oracle is proven on a whole overlay; the plan's D6/L0 stands.
|
||
- **S94 — T0 tools-health, run 2 RED (460 s):** `kit_coverage: FAIL (4 gap(s))` — `rule R96…R99 is neither cited by a registry provenance
|
||
line nor dispositioned in config/kit_coverage_map.tsv` (R92, the same class P34 hit at its close). Fix: four map rows — R96/R98/R99 →
|
||
DK-81 (its facts 4/1/2 ARE those rules), R97 → G29 (provenance R49 + R53). `kit_coverage: OK`; the rungs after it (xsig 8 OK,
|
||
work_evidence selftest OK, split_indicator SELF-TEST PASS + 218 OK) individually green.
|
||
- **S94 — T0 tools-health, run 3 GREEN:** `tools-health: OK — sigs fresh; corpus(+resident) + cdecl + binaries + report(lint+dedup) +
|
||
cookbook-index all green.` `exit=0`, `wall=474.23 s` (`.run/P35/baseline/tools_health_open3.log`). `docs/story-timeline.md/.svg`
|
||
regenerated by the chain (74 rows; the v2.0.0 tag row). **T0 ☑.**
|
||
- **S94 — T1 `tools/share_census.py` + `config/dedup_exceptions.tsv`.** Built as designed (one pass per TU with comments, dead
|
||
`#if 0`/`NON_MATCHING` halves and macro-continuation text masked; forms macro / macro-param / include / param-include / def / stub /
|
||
asm-verbatim; addresses from each binary's symbol stack, never names; the fleet, the source dirs, the contracts (`<alias>_CHECK_SHA`,
|
||
main = `check.us.sha`), the address spaces (`_VRAM_BASE`) and the twin sets derived from the Makefile and the contracts; classes
|
||
through `dedup_integrate.group_members`). Five scanner defects found by its own self-test and coverage line, each fixed at the cause:
|
||
string contents were blanked before include paths and asm labels were read; K&R definitions (a blank tail after the last `;`,
|
||
and the one-line `void f(a, b) void *a; s16 b; {` form); a second definition head on the same line after an `extern …;`; the
|
||
alias regex spanning a previous macro site's parentheses; `asm(` as well as `__asm__(`; implicit-return-type heads with no type
|
||
line; `static inline` helpers (no address) excluded from the unresolved list. **Result: `--selftest` → `selftest: 7/7 verdicts
|
||
correct`; the census → `218 binaries · 362,389 sig instances · 362,389 classified · 0 UNACCOUNTED · 10,180 h_exact classes with >=2
|
||
instances`; verdicts A 1,712 · B 282 · C 6,107 · D 1,861 · M 218 (instances 214,478 / 45,226 / 14,839 / 5,796 / 462); flags ALIAS 44 ·
|
||
E 3,826 · F 54 · PINS 687 · TWIN-PENDING 3,748 · TYPEDEF 96; SAME-VRAM UNREGISTERED 4,667 classes · 35,976 instances · 11,767 private
|
||
copies · 31,309 collapsible · 1,710,469 ins (twin-pending 3,568 classes / 7,136 instances); CROSS-ADDRESS/SPACE deferred 3,801 classes ·
|
||
30,347 instances · 12,938 private copies; macro sites 255,947; duplicate-text classes 6,845 (23,269 sites); 43 s uncached, 41 s cached
|
||
(the cache is not the cost — profile at T7); exit 0.** Controls (R39): `func_80144B9C` = B/141/{'include': 141}/missing 7 (the registry
|
||
lists 134 — never extended to the 7 later overlays; T5 bucket 0), `clearTbl40` = A+E/2, the three setters = B/282(E), B/145(E), B/141
|
||
(the same 2-instruction bodies also sit at other vrams), twin symmetry SC01_005/006 = 653/653. **Instrument findings for the record:**
|
||
the B class is 282 registered-but-incomplete groups (204 never extended, 74 with a private-copy site — demacroized escapes —, 2 with a
|
||
member the sigs do not show); a 2-instruction empty-body class (`jr ra; nop`) has 11,852 instances across every space (E,F → deferred
|
||
with the cross-address set; the names phase decides how an empty function is written). Outputs `.run/P35/census/{share_census.json,
|
||
share_census.txt, coverage_notes.txt}` (tracked), `classes.jsonl` + `cache/` (ignored). SETUP + dictionary rows (R21/R87). **T1 ☑.**
|
||
- **S94 — T1 slip, named (R97/R66):** commit `1dbffee87` says "kit corpus regenerated" while `make kit-corpus` had exited 2 and
|
||
`tool_census --check` had refused the new row (`phase=P35` — the column is the KIT LADDER phase, P1–P10, not the project phase);
|
||
the chain had `&&` on an echo, not on the checks. Fixed in `72a77e7d3` (row → P10, the readability tools' rung; corpus 361 copies;
|
||
`tool_census --check: OK`; `kit_coverage: OK`), chained on the checks' own exit codes; `docs/tool-index.md` (generated) committed after.
|
||
- **S94 — T2 the health chain learns the header + twin forms (both forms accepted).** ONE source-dir oracle: `corpus.src_dirs()` /
|
||
`src_dir()` / `twin_of()` from `<alias>_SRC_DIR` / `<alias>_TWIN_OF` in the Makefile and `config/*.mk`; `corpus.src_files` and
|
||
`o0_subseg` go through it; `compile_only.src_dirs` delegates to it; `progress.set_binary` takes its dir from it (the table's `src`
|
||
is only a cross-check); `dedup_integrate._src_paths` uses `corpus.src_files`. The include form: `share_census.header_defs()` is the
|
||
one reader (`[(name, empty)]`, masked — a macro-only header defines nothing); `progress.classify` counts an include site as the
|
||
function's definition (REAL/EMPTY by the header body) with the `#define SHARED_FN` / `#undef` state for the parameterized form, and
|
||
the dedup fold keeps included members in the shared count (`(members − real − stubs) ∪ (included ∩ members)` → the README's
|
||
255,632 unchanged); `overlay_src_split` gains the `include` item kind (anchor by the header's defined name, `#undef SHARED_FN`
|
||
travels with the item; `split_header` no longer swallows a leading body include; `macro_externs`/`macro_proto` read the header's
|
||
parts; a macro invocation with no table entry is a loud refusal, R43) and `jr_isolate_all` admits the kind; `dedup_integrate`
|
||
C2a′ requires a plain-C source to DEFINE `func`; `audit_binaries` accepts `engine_prelude.h`, resolves the main TU through the
|
||
oracle, and gains CHECK 3b (twin citizenship: primary onboarded and not a twin, same src dir, no `src/<twin>/`, equal contracts,
|
||
equal carves); `cdecl.audit_differential` reports "not applicable" when the macro header is gone instead of crashing;
|
||
`lint_symbol_refs` / `family_remap` / `fix_arity_callers` glob `src/shared/**/*.h`; `blocker_probe.macro_scope` and
|
||
`demacroize.macro_bodies` tolerate the header's absence; `shared_lock` docstring; `harvest_verify` comment.
|
||
**Verify:** `py_compile` 16 tools OK; `share_census --selftest` 7/7; `audit-binaries: OK`; `dedup-check: 2220 validated, 0 failed`;
|
||
the split tool on `src/ov_SC06_033/ov_SC06_033_o0b.c` (a real include-form TU) → the whale's include is an item.
|
||
**A count correction surfaced (R14):** `FLEET matchable 363,214 → 363,221` — the 7 overlays whose `_o0b.c` includes the whale
|
||
header but were never registry members (`ov_MAIN_012`, `ov_SC02_037`, `ov_SC03_107`, `ov_SC07_006/007/010/011`) were counted by
|
||
NEITHER classify (include lines skipped) NOR the registry fold; the source now decides (ov_MAIN_012: matchable 2,401 → 2,402, the
|
||
known row). `docs/progress.json` + the README block regenerated by `make report BINARY=main` (`progress.py --check: … fresh`);
|
||
the generated timeline follows the COMMITTED digest, so it is regenerated after the commit that carries the new digest.
|
||
- **S94 — T2 close.** The chain's only red rung was the kit's verbatim copies of the 16 edited tools (`tool_census --check` 17 gaps) —
|
||
`make kit-corpus` belongs in EVERY commit that edits a tool; the rungs after it re-run individually by exit code; the chain also
|
||
regenerates the timeline's commits-per-day cell (it moves with every commit — committed with each task). Commits `6cb056c93`
|
||
(the tools + the +7 numbers), `4ec752e68` (the timeline), `24e8ff72d` (the close). **T2 ☑.**
|
||
- **S94 — T3 probe pair SC01_005/006 (Max).** The mechanism: `config/overlays.mk` `ov_SC01_006_TWIN_OF := ov_SC01_005` +
|
||
`ov_SC01_006_SRC_DIR := src/ov_SC01_005`; the Makefile's twin block (static pattern rules `build/src/<twin>/<twin>%.o ←
|
||
src/<primary>/<primary>%.c` + the bare-name rule, the same recipe; `TWIN_O0_OBJS` keep `-O0`; the twin's own `OBJS`/`C_DEPS`);
|
||
`config/splat.ov_SC01_006.yaml` `create_c_files: False` (src_path stays the twin's name → the .ld's 120 object paths under
|
||
`build/src/ov_SC01_006/`); `git rm -r src/ov_SC01_006` (30 files). **Verify:** `make extract BINARY=ov_SC01_006` exit 0, no
|
||
`src/ov_SC01_006/` recreated; `make check BINARY=ov_SC01_006 -j16` → `sha1 56760dbe… == config/check.ov_SC01_006.sha (BYTE-IDENTICAL)`
|
||
(the CC lines read `(twin of ov_SC01_005: src/ov_SC01_005/…c)`); the primary still BYTE-IDENTICAL; **the race test** — both objects
|
||
trees deleted, `make check` for 005 and 006 run CONCURRENTLY → both BYTE-IDENTICAL; consumers: `progress.py --binary ov_SC01_006`
|
||
2503/2503 (of which dedup-shared 1838, unchanged), `dedup-check --binary ov_SC01_006` 1838 validated / 0 failed, `audit-binaries: OK`
|
||
(CHECK 3b passes: primary onboarded, same dir, no src/<twin>, equal contracts, equal carves), `compile_only --list ov_SC01_006`
|
||
→ 30 TUs from `src/ov_SC01_005/` (2 at -O0). One instrument slip caught by the census's R32 gate: my Makefile variable
|
||
`TWIN_SRC_DIR` matched the `_SRC_DIR` oracle regex as a binary named `TWIN` → renamed `TWIN_SRCDIR`; after it the census shows
|
||
**TWIN-COVERED 575 classes** for the pair (twin-pending 3,748 → 3,173), twin symmetry 653/653 unchanged. The census's `copies` now
|
||
counts DISTINCT private sites (a twin's instance resolves to its primary's TU — one source), `collapsible` = copies − 1.
|
||
Commit `9b0816e74`.
|
||
- **S94 — T3 pairs 2–3 (equal carves, mechanical):** `ov_SC03_119` TWIN_OF `ov_SC03_118` (31 files removed; both `77ee78dd…`
|
||
BYTE-IDENTICAL), `ov_SC02_003` TWIN_OF `ov_SC02_000` (37 files; both `5ece4bca…`); `audit-binaries: OK`; dedup-check per twin
|
||
0 failed. Commit `e79cfcc06`.
|
||
- **S94 — T3 pairs 4–5 (carve alignment, R60):** the twin's yaml := the primary's with the alias and `FILE_nnn` substituted (+ the
|
||
twin note + `create_c_files: False`), its `_JTBL_INTERLEAVE` := the primary's substituted — and, found by the first build's
|
||
failure (`missing .end at end of assembly` on `ov_SC04_019_jr_8017AE2C.o`): the per-object **`JTBL_PADS` lines are keyed by
|
||
OBJECT PATH** (`build/src/<twin>/…`), which my alias-normalized block diff (lines starting with the alias) never compared, so the
|
||
twin still carried its retired carve's pads (4 differing lines for SC04, 2 for SC03; the three committed pairs re-checked: 0, 0, 0).
|
||
Regenerated from the primaries by substitution → 0 differing. `pads_audit.py` built `src/<b>/<tu>.c` by hand and raised
|
||
IndexError on a twin — now reads the source through the oracle (`corpus.src_dir` + `twin_of`). **Verify:** `ov_SC04_019` +
|
||
`ov_SC04_018` BYTE-IDENTICAL (`fe9b413f…`), `ov_SC03_015` + `ov_SC03_014` (`d84b01a2…`); `interleave_check` ALIGNED (n=51, n=46);
|
||
`pads_audit` ok on every carve object; dedup-check 0 failed; `audit-binaries: OK` (CHECK 3b incl. equal carves). Commits
|
||
`2648aa5a9`, `6ebb3dac3`.
|
||
- **S94 — T3 census after the five collapses:** TWIN-COVERED 3,748 classes (3,580 non-A, 7,186 instances); **same-vram unregistered
|
||
4,667 → 1,099 classes · 4,755 private sites · 3,658 collapsible · 28,840 instances** (twin-pending 0); `S1: … 10,180 classes, 9,081
|
||
satisfied (3,580 twin-covered, 0 excepted, 3,801 deferred cross-address), 1,099 VIOLATION(S)` — the backlog T5 shares. The controls
|
||
unchanged (twin symmetry 653/653). `make kit-corpus` for `pads_audit` + `share_census`; `tool_census --check: OK`.
|
||
- **S94 — Rules check (P6, after T3 = four tasks): re-read complete (CLAUDE.md's eight mandatory behaviours; PROJECT_CONTEXT's 23
|
||
P/G/H/X rules). Continuing with T4.**
|
||
- **S94 — T3 close, R22:** `make clean && make extract-all JOBS=16 && make check-all JOBS=16` → `extract-all: 217 extracted, 0 failed of
|
||
217 (+ main, serial)` · **`check-all: 218 passed, 0 failed of 218`** · `wall=308.78 s` (the census and the kit corpus ran beside it) ·
|
||
`exit=0` (`.run/P35/baseline/r22_t3.log`). Twins: 5 pairs / 10 aliases / 166 source files deleted; every twin builds from
|
||
its primary's directory. **T3 ☑.**
|
||
- **S94 — T4 `tools/macro_to_header.py` (Max) + the fleet conversion.** `--plan` on the post-twin tree: `engine_core.h: 5147 define lines,
|
||
3516 distinct, 1631 twice (4 divergent asserted); 8 directives — the prelude is complete` · `sites 246,347 in 148 binaries · TUs
|
||
including engine_core.h 3,818 · macros: shared 1,959 · single-site→header 257 · single-site→inline 0 · dead 1,300` · naming
|
||
`{'suffixed': 86, 'plain': 1873}`. **`--apply` over all 213 non-twin binaries** (`.run/P35/convert/batch2.sh`: snapshot objects → one
|
||
apply → per binary `make check` + every object compared): **213/213 BYTE-IDENTICAL, `objects byte-identical: 4121/4121`**, apply
|
||
ledger `{"sites":246347,"tus":3818,"headers_written":2215,"dead":1300}`, 224 s wall (commit `c53a9e1a9`, 6,036 files). **`--finalize`:**
|
||
the three legacy sites (`SETTER`/`RETCONST` ×3 in SC01_005 → `ov/func_8012AD64.h` etc.; `CLEAR_TBL40` ×2 in `src/800_c.c` → the
|
||
`#define SHARED_FN` / `#include "shared/main/func_80037004.h"` / `#undef` form — the cross-address control), the whale moved to
|
||
`ov/func_80144B9C.h` (guard removed) with every `-O0` includer rewritten, **7 alias-form bodies given their own binding** (`s32
|
||
aF80131CA8(int a0) __asm__("func_80131CA8");` derived from the head; the K&R head gets the unprototyped form), the registry's 2,224
|
||
`source:`/`func:` lines text-edited by id, the 3 non-shared headers that included a macro header rewritten (`src/800_shared.h`,
|
||
two per-overlay `_shared.h`), `engine_core.h` + `ov_setters.h` + `clearTbl40.h` deleted; `--verify: OK — 0 macro sites, no macro
|
||
header, the prelude present`. **Gates:** main `143dbb89…`, ov_SC01_005, ov_SC01_084, ov_SC02_005, ov_SC06_033, ov_SC02_027,
|
||
ov_SC02_011, ov_SC02_026 and the 7 late whale includers all BYTE-IDENTICAL; `dedup-check: 2220 validated, 0 failed`;
|
||
`audit-binaries: OK`; the census `362,389 classified · 0 UNACCOUNTED · macro sites 0`, verdicts A 1,712 · B 282 · C 6,640 · D 1,545 ·
|
||
M 1, same-vram backlog 1,099 classes (unchanged — T4 shares nothing new); **R22: `check-all: 218 passed, 0 failed of 218`, wall
|
||
145.10 s** (157 s at the open; user CPU 2,230 s vs 2,231 s — the per-overlay cpp saving the probe measured is small against extract,
|
||
assemble and link fleet-wide). **Instrument findings, each caught by a gate and fixed at the cause:** (1) the first apply of "main"
|
||
swapped the include line in all 3,818 TUs (main's dir is `src/`, a prefix test matched the fleet) — restored with `git checkout --
|
||
src`, the test made exact; (2) the per-binary driver paid the 218-file sig load per binary (36 binaries > 10 min) → the batch form
|
||
pays it once (224 s for 213); (3) five mid-file `match_one-only` includes with trailing comments and (4) seven whale includers in
|
||
`_o0c.c`/`_o0d.c` (a `*_o0b.c` glob) were missed — the census's coverage line (7 unaccounted) and `--verify` found them; (5) `verify`
|
||
listed 3 of N offenders (a `[:3]` slice) — it now lists every include LINE and ignores prose; (6) `.run/P35/convert/` was not
|
||
allowlisted — added. `docs/SETUP.md` P35 T3–T4 section + the dictionary row (P10, PROJECT-ONLY). **T4 ☑.**
|
||
- **S94 — T5 `tools/share_body.py` (Max) + the R37 probe (commit `0bccef901`, 18:12 local).** Built as designed (411 lines): `census(jobs)` =
|
||
`share_census.classify(…, keep_instances=True)` (the census now exports per-instance `line`/`end`/`nlines` and the class's `groups`; a
|
||
census with unaccounted or multi-form instances is a refusal, R32); `candidates()` → `extend` = verdict-B classes (registered, some
|
||
instance still a private `def`), `new` = unregistered same-vram classes with ≥1 private def — both skip A, E/F flags, TWIN-COVERED and
|
||
EXCEPTED classes, sorted by (−instances, −nins); `choose_exemplar` = majority normalized text → pin-free → fewest lines (printed);
|
||
`Batch.add_class` (B: the registry's header must define `func` or the class is refused; the extension list = instances not yet
|
||
listed; new: header `func_<VRAM>[__h8].h` via `macro_to_header.Oracles.header_rel`, banner + `bind_alias_header`; every private site →
|
||
`include_line(tu, hdr)` at `[line, end]`; every instance's binary is "touched", twins with their primary); `gate()` = `make check
|
||
BINARY=<b> -j16` by exit code + every object compared with the pre-batch snapshot (`.run/P35/share/check_<b>.log`); a red binary is
|
||
BISECTED (restore its TUs, re-apply classes one at a time, the culprit REJECTED and ledgered, the survivors kept); `registry_append`
|
||
(shorthand, by text — never `yaml.safe_dump`), `dedup_extend.add_members_surgical` for extensions; `batch_<label>.json` per batch
|
||
(`classes`, `refused`, `results` per binary, `registered`, `extended`, `rejected`, `exemplars`). `--plan` on the post-T4 tree: `census
|
||
28 s · extend (registered-incomplete) 183 classes · new (unregistered same-vram) 916 classes / 3,569 private sites · new by band {'32+':
|
||
446, '16-31': 255, '8-15': 204, '1-7': 11}; differing-text classes 27; with pins 139; alias-form 6` (183 + 916 = the census's 1,099
|
||
same-vram backlog; the plan's "206 never-extended" counted the B census before the twins). First probe run: `KeyError: 'line'` (the
|
||
census's per-instance records lacked the line range) → fixed in `share_census.classify`. **The probe (R37):** class
|
||
`c1c085b28d16c2417f2d9ce46553d16fe45b4508` = `func_801681FC` (2 instances, ov_SC04_008 + ov_SC05_009, 22 ins): `[new1] 1 classes ·
|
||
2 sites in 2 TUs · 1 new headers · gating 2 binaries` → `gated 2/2 binaries green · registered 1 groups · extended 0 members · rejected
|
||
classes 0`; header `src/shared/ov/func_801681FC__c1c085b2.h` (suffixed: the vram hosts >1 class fleet-wide), group `S_func_801681FC`,
|
||
ledger rows 0. `bind_alias_header` factored out of `macro_to_header` (shared by both tools). SETUP §"P35 T3–T5" + the dictionary row
|
||
(`tools/share_body.py P10 ADAPT LIVE`); `make kit-corpus` (363 copies) + `tool_census --check: OK`. 18 files, +1,198/−81.
|
||
- **S94 — T5 bucket 0, FIRST PASS (commit `571374b97`, 18:30 local; `.run/P35/share/run_extend.log`, 617 s, exit 0).** `share_body --apply
|
||
--bucket extend --batch 120`: `census 24 s · extend 183 classes · new 915 classes / 3,567 private sites` · `[extend1] 120 classes · 774
|
||
sites in 190 TUs · 0 new headers · gating 141 binaries` → `gated 141/141 binaries green · registered 0 groups · extended 754 members ·
|
||
rejected classes 88` · `[extend2] 63 classes · 486 sites in 406 TUs · gating 146 binaries` → `gated 146/146 binaries green · extended 34
|
||
members · rejected classes 37`; 482 REJECTED lines; the registry +788 members (13,062 → 13,850 member entries in 102 `binaries:` lines);
|
||
539 src files changed; 690 files in the commit. **Diagnosis before committing (the replay method: apply ONE class's edit from the census's
|
||
instance record, look at the text/compile):** (1) the failure "detail" was the FIRST line containing `error`/`conflicting` — the
|
||
fleet-wide benign `warning: conflicting types for built-in function 'memcpy'` labelled 233 of the rejections; (2) the bisect re-applied
|
||
the kept classes SEQUENTIALLY with the census's ORIGINAL line numbers — every earlier class's edit shifted the later sites, so the later
|
||
classes landed on the wrong lines (duplicate definitions → `{standard input}: Error: symbol 'func_80168B70' is already defined`, 123
|
||
lines; `parse error before 'extern'` at a header's line 3, 14 lines; `parse error before '}'`) — **125 of the 183 classes were rejected
|
||
on the tool's own artifacts**; (3) no ledger row was written for a rejected EXTEND class; (4) `add_members_surgical` extended every
|
||
group with ALL planned members, rejected or not. **Fixes in the same commit** (`tools/share_body.py`, 60 lines): the detail from
|
||
error lines only (`warning:`/`note:` lines excluded); the bisect's `apply_selected()` restores the TU and applies the selected classes
|
||
in ONE bottom-up pass from the original text; rejected extend classes ledgered `TU-CONFLICT` with the rejecting binaries; a group
|
||
extended only with members whose sharing survived the gate. **What the commit therefore holds:** the sites the (artifact-prone) first
|
||
bisect kept, every binary gated green in its final state (per-binary `make check` + object A/B — NO clean fleet run), and a registry
|
||
that lists 788 new members regardless of whether their site shares (the wholesale extension ran BEFORE fix 4 existed).
|
||
- **S94 — T5 bucket 0, SECOND RUN (`.run/P35/share/run_extend2.log`; started 18:30:35 local, 511 s, exit 0 — finished at ~18:39 AFTER the
|
||
session's context filled: the session hit 91% context at 18:32, Drew asked for a hook and then for a checkpoint, both answers were
|
||
"Prompt is too long", the run's completion notification arrived to a dead session; Drew let the script finish before exiting).**
|
||
`census 37 s · extend (registered-incomplete) 150 classes` (33 classes completed by the first pass) `· new 915 classes / 3,567 private
|
||
sites` · `[extend1] 120 classes · 503 sites in 182 TUs · 0 new headers · gating 141 binaries` → `gated 141/141 binaries green · registered
|
||
0 groups · extended 0 members · rejected classes 48` · `[extend2] 30 classes · 53 sites in 23 TUs · gating 141 binaries` → `141/141 green ·
|
||
extended 0 members · rejected classes 7` · `share_body: done — 150 classes in 2 batch(es); see the ledger`. 303 REJECTED lines, 129 of
|
||
them one class (`93d5fccdcc` = `E_func_80168B70`, rejected in 134 binaries — so 136 of 141 binaries went through the bisect in extend1;
|
||
9 in extend2). "extended 0 members" is CORRECT for this run: the first pass had already listed every planned member. **Left on disk,
|
||
uncommitted (`git status`): 38 `src/` files in 8 overlays (ov_MAIN_012 5 sites, ov_SC01_077 19, ov_SC02_037 6, ov_SC03_107 5, ov_SC07_006
|
||
49, ov_SC07_007 29, ov_SC07_010 28, ov_SC07_011 29 = 170 private definitions → `#include "../shared/ov/func_X[__h8].h"`; every added line
|
||
is such an include, verified by S95), `config/dedup_exceptions.tsv` +55 rows (all `TU-CONFLICT`, "share_body extend1/extend2: extend of
|
||
E_func_X rejected in [binaries]"), `.run/P35/share/batch_extend1.json` + `batch_extend2.json` (overwritten with this run's records), 139
|
||
of the 146 `check_*.log` (each ends `BYTE-IDENTICAL` — the final gate of each binary), untracked `run_extend2.log` and
|
||
`.run/P35/baseline/kit_corpus_t5a.log`.**
|
||
- **S95 — RECOVERY (2026-09-08 evening; Fable 5.1 at Max; no gate, build, census or tool run — Drew's instruction).** Reconstructed the
|
||
above from the S94 transcript (dumped to text with a 60-line script: user text / assistant text / tool_use / tool_result per record),
|
||
the two commits, the batch records and the tree. **Findings, each read from an artifact:**
|
||
1. **The dirty tree is run 2's net output and is bankable as-is (R42/R66):** 170 include lines, 0 other added lines; every one of the 8
|
||
overlays' final `check_<b>.log` reads `[ OK ] … (BYTE-IDENTICAL)`; the run's exit was 0. It has NOT had the clean fleet run — **R22 is
|
||
owed for all of T5 so far** (the first pass too: its gates were per-binary incremental builds).
|
||
2. **The registry runs ahead of the source (the first pass's wholesale extension):** the 55 ledger rows name 325 (class, binary)
|
||
rejections; **317 of those binaries are LISTED as members of the class's group** in `config/dedup.us.yaml` (computed by S95 from the
|
||
ledger notes × the registry's `binaries:` lines). `E_func_80168B70` alone: 7 members before the first pass (the 7 late overlays), 141
|
||
after, 134 of them private copies. `share_census --check` will count these classes as EXCEPTED (the ledger), so S1 stays green — but
|
||
T7's planned C2d ("every member's site includes the source") fails on all 317 until the members are removed or the classes crack.
|
||
3. **The cause extractor still misses gcc-2.7.2's error lines** (`tools/share_body.py:204-206` requires `\berror\b|Error\b|undefined
|
||
reference|already defined|multiple definition|parse error|\[FAIL\]`): cc1 prints errors as `file:line: message` with NO "error" token
|
||
(`too many arguments to function`, `conflicting types for`, `redeclaration of`, …), so 254 of the 303 rejection lines read only `make:
|
||
*** [Makefile:1033: build/src/<b>/<tu>.o] Error 33` (33 = cc1's fatal exit status). **The real cause of the dominant class**, read
|
||
from the dead session's own replay residue (`/tmp/claude-1000/-home-musashi-bfm-decomp/e902c34e-…/scratchpad/cc1.err`, the single edit
|
||
of `func_80168B70` in ov_SC01_074 through cpp → cc1): **`src/ov_SC01_074/../shared/ov/func_80168B70.h:13: too many arguments to
|
||
function 'func_80146C3C'`**. The header's text is the 7 late overlays' spelling (`func_80146C3C((u8 *)a0)` against THEIR `extern void
|
||
func_80146C3C(u8 *a0)`); the 134 main overlays declare `extern void func_80146C3C(void)` and their private copies call `((void
|
||
(*)(s32))func_80146C3C)(a0)` — same bytes (h_exact `93d5fccdcc…`), incompatible source environments. The header carries the MINORITY
|
||
spelling because the group was created for the 7. (The first pass's `symbol 'func_80168B70' is already defined` for this class WAS
|
||
the bisect artifact; the second run's rejection is real.) First-pass lines already showed the same family in ov_MAIN_012:
|
||
`func_80168BDC.h:5: conflicting types for 'ApplyMatrixSV'`, `func_80168070.h:7: conflicting types for 'ApplyMatrixSV'`.
|
||
4. **A cross-batch edit-loss defect (R42 class — a gate destroying banks):** `restore()` (`share_body.py:221-223`) is `git checkout --
|
||
<tus>`, and `Batch.apply_edits` uses the census's line numbers from the START of the run. Within one run, batch N+1's edits land on
|
||
TUs batch N already changed (uncommitted) → stale lines → the initial gate fails → the bisect `git checkout`s those TUs, WIPING batch
|
||
N's kept includes, then re-applies only batch N+1's classes (which pass) → the binary ends green with FEWER shares than reported.
|
||
Evidence: run 2's extend2 bisected ov_SC07_006/007/011 (007 and 011 "3 classes kept, 0 rejected" — a batch that fails whole yet
|
||
passes class by class is the stale-line signature) and their `_jr_801457A4.c` lost extend1's six kept sites `func_80149374,
|
||
func_801493D0, func_80149450, func_801494CC, func_80149544, func_8014964C` (present in no diff); ov_MAIN_012 / ov_SC02_037 /
|
||
ov_SC03_107 lost `func_80146128, func_80146FC4, func_80153800, func_801539F8` the same way; ov_SC01_077 ~11 sites. S95's estimate:
|
||
≈50–63 kept sites lost in run 2 (the same happened inside the first pass). They stay private and reappear as verdict-B classes in the
|
||
next census (NOT ledgered → retried automatically). Fix: snapshot each touched TU's text in memory before `apply_edits` and restore
|
||
from the snapshot; re-derive the edit positions per batch (re-census, ~30 s) or run ONE batch per invocation and commit between.
|
||
5. **The residue's causes, as recorded (to be confirmed by replay — the ledger says TU-CONFLICT for all 55):** (a) prototype/type conflicts
|
||
between a header's preamble or calls and the TU's declarations — the late overlays ov_MAIN_012, ov_SC02_037, ov_SC03_107, ov_SC07_006/
|
||
007/010/011, ov_SC01_077 (+ ov_SC01_000, ov_SC06_018 for 2 classes), and the 134-binary `E_func_80168B70` case; (b) **suspected census
|
||
definition-range defects** — `parse error before 'if'` at `<b>_jr_8015C32C.c:3388-3390` (`d743c21bd9` = `E_func_8016163C`) and
|
||
`before 'not'` at `:3438` (`3576d059c2` = `E_func_80161774`) in exactly the five twin-pair primaries (SC01_005, SC02_000, SC03_014,
|
||
SC03_118, SC04_018 — one shared source text), `<b>_jr_801380E0.c:1050 before 'if'` (`9bda7673e9` = `E_func_80138C30`, SC03_014/118);
|
||
(c) **symbol-name conflicts at link** — `undefined reference to func_80161A90` (`3576d059c2`, the five primaries' `_after.c`),
|
||
`func_8012A68C` (`6f62feba35` = `E_func_8012A328`, SC04_018), `func_80161B18` (`7604e5daf1` = `E_func_80161A90`, SC04_018),
|
||
`func_8012A598` (`bdb48359cd` = `E_func_8012A568`, SC03_014/118): the header names a callee those binaries' symbol stacks do not carry
|
||
under that name; (d) **GATE-REJECT (bytes differ)** — `[FAIL] build/ov_SC07_00x` for `7529ad8f17` = `E_func_801376E8` (an alias-form
|
||
header, `aF801376E8`) in SC07_006/007/010/011; (e) `c9866fcb86` = `E_func_8012F49C` in SC07_006, cc1 error, cause uncaptured. The
|
||
reason column should say which (TU-CONFLICT / RANGE-DEFECT / SYMBOL-NAME / GATE-REJECT), not one word for all.
|
||
6. **T6 recon the session had done (read-only, reproduced by S95):** 30 live-tree tools carry a non-docstring string constant naming
|
||
`DEFINE_func_` or `engine_core.h` (the plan's negative control said "exactly the 22 parsers" — the measured set is 30 and includes the
|
||
three Phase-35 tools, which are LEGITIMATE mentions the guard must whitelist): aprop_autodraft 2 · audit_binaries 1 · auto_driver 1 ·
|
||
blocker_probe 3 · build_engine_types 1 · bulk_harvest 1 (STILL-NEEDED) · canon_sig_reconcile 1 · cdecl 7 · conform_decls 1 ·
|
||
dedup_extend 1 · dedup_integrate 1 · dedup_propagate 3 · demacroize 2 · export_pairs 1 (STILL-NEEDED) · family_cousins 1 ·
|
||
family_remap 1 · family_sweep 6 · fix_arity_callers 2 · gccmap_cites 1 · gen_harvest_targets 3 · jr_isolate_all 1 · macro_draft 1
|
||
(STILL-NEEDED) · macro_to_header 19 · normalize_self_decls 2 · overlay_src_split 3 · p16_improve 4 · recover_giant 1 ·
|
||
recover_integration 2 · share_census 4 · sig_unify 1. The 7 freeze tools' entry points: `family_sweep` main:878/1060,
|
||
`gen_harvest_targets` 154/294, `p16_improve` 68/136, `recover_giant` 71/81, `restore_dropped_decls` 73/129, `normalize_self_decls`
|
||
193/210, `o0_subsplit` 64/197; dictionary status today: all LIVE except restore_dropped_decls + macro_draft STILL-NEEDED.
|
||
Nothing else changed on disk in S95 except this file.
|
||
- **S96 (2026-09-08 evening, same terminal as S95; Drew: `/effort high` — testing Fable 5.1's default; the plan's Max for T5's tool work is
|
||
waived by that choice) — T5 step 1: run 2 BANKED** as the checkpoint's §2 step 1 specifies: 37 `src/` files (170 shared includes in
|
||
8 overlays), `config/dedup_exceptions.tsv` (+55 rows), the batch records, 139 gate logs, `run_extend2.log`, `kit_corpus_t5a.log` —
|
||
181 files. No gate re-run (each binary's final `check_<b>.log` is the tool's success line, R66); R22 still owed for T5.
|
||
- **S96 — T5 step 2: `tools/share_body.py` fixed** (the three S95 defects): `failure_cause()` = the first `file:line: message` (or
|
||
`file:(.text+0x..): message`) diagnostic that is not a warning/note/banner, then the assembler/linker phrases, then the make line;
|
||
`reason_for()` → SYMBOL-NAME / GATE-REJECT / PARSE-ERROR / TU-CONFLICT (the two new codes documented in the ledger's header); the
|
||
bisect restores from `tu_snapshot()` (the pre-edit text of every touched TU, in memory) — `git checkout` is gone; `--batches` (default
|
||
1) stops after one batch with a "COMMIT, then re-run" line; the per-class cause is kept (`causes` in the batch JSON, in every ledger
|
||
note); the docstring carries the run-on-a-committed-tree rule. **Controls (R39):** on the dead session's real red `cc1.err` (64 warning
|
||
lines) the cause is `func_80168B70.h:13: too many arguments to function 'func_80146C3C'` → TU-CONFLICT; six synthetic logs (linker,
|
||
assembler, `[FAIL]`, parse error, conflicting types, a warning-only log) each give the expected code and never a warning line.
|
||
`make kit-corpus` + `tool_census --check: OK`.
|
||
- **S96 — T5 step 3: the registry repaired** — `share_body.py --repair-registry` (new: every listed member whose site is still a private
|
||
definition, derived from the census and matched on (binary, vram) — the first cut matched the binary NAME alone and removed 9,269
|
||
members from 214 groups because h_exact is position-independent and a listed binary can hold the same bytes privately at ANOTHER
|
||
address; caught by `dedup-check: 14 failed` + a known-true control (ov_SC01_004's include of func_80146E90), reverted, fixed):
|
||
`repair-registry: 378 listed-but-private members removed from 79 groups; groups left with <2 members: 0; verbose groups … 0` —
|
||
317 of them the ledger's rejected pairs (325 minus 8 never listed), 61 the sites the S94 bisects wiped plus older escapes;
|
||
`E_func_80168B70` back to its 7 late-overlay members. `dedup-check: 2221 validated, 0 failed | C1 coverage 256120/256120`;
|
||
census `362,389 classified · 0 UNACCOUNTED`, verdicts A 1,812 · B 183 · C 6,639 · D 1,545 · M 1 (A 1,712 → 1,812 = the 100 classes
|
||
bucket 0 completed), same-vram backlog **1,099 → 999 classes · 3,883 private copies · 2,887 collapsible**; the whale control
|
||
`A/141/missing 0`. `.run/P35/share/repair_registry.json` is the record. kit corpus + `tool_census --check: OK`.
|
||
- **S96 — T5 step 4: the suspect rejections replayed; a FOURTH tool defect found and fixed; the extend bucket re-run (`run_extend3.log`).**
|
||
The census's definition ranges for the three parse-error sites are CORRECT (e.g. `ov_SC01_005_jr_8015C32C.c:3389-3426` covers all of
|
||
`func_8016163C`) and each single edit compiles clean (cc1 rc 0) — so the failures were not the class's edit. Cause: **a twin's instance
|
||
resolves to its PRIMARY's TU, so a twin pair queued the SAME (tu, line) edit twice; the second replacement swallowed the NEXT function**
|
||
→ `parse error before 'if'` (the orphaned body) or `undefined reference to <the following function>` at link — in exactly the five twin
|
||
primaries; 7 classes (`d743c21bd9`, `3576d059c2`, `6f62feba35`, `7604e5daf1`, `9bda7673e9`, `bdb48359cd` + one) were the tool's artifact.
|
||
Fix: `Batch.add_class` dedupes edits by (tu, line). `E_func_801376E8` (`7529ad8f17`, SC07_006/007/010/011) IS a real GATE-REJECT:
|
||
the SC07 private copies read `((void (*)(s32))func_80139BE0)(obj)` where the header calls `func_80139BE0(obj)`, and those TUs declare
|
||
the callee `s32 (s32)` — the header text allocates registers differently there (replayed: `[FAIL]`, the one object differs inside
|
||
`func_801376E8`). The ledger's 54 S94 rows were dropped for re-judging (7 artifacts, the rest without a captured cause);
|
||
`E_func_80168B70`'s row kept with its real cause (step 5 pending). **Run 3** (`--bucket extend --batch 400 --batches 1`, 165 s):
|
||
`extend (registered-incomplete) 83 classes` · `[extend1] 83 classes · 235 sites in 55 TUs · gating 141 binaries` → `gated 141/141
|
||
binaries green · registered 0 groups · extended 87 members · rejected classes 48`; 157 REJECTED lines (303 before), every cause now a
|
||
real diagnostic (`conflicting types for 'ApplyMatrixSV'` ×6 classes, `too few arguments to function 'func_8014A51C'`, `conflicting
|
||
types for 'D_80126CC4'`, `… 'D_800A651C'`, `… 'func_801497A8'`, `too few arguments to function 'func_80146C3C'` — the late overlays'
|
||
declaration environments vs the headers' text); 78 more sites shared in 24 TUs; 49 ledger rows. A fifth extractor wart (a chained
|
||
`a.h:65: b.h:15: warning: …` line picked for 3 classes) fixed in this commit — those 3 rows carry a warning as cause and are
|
||
re-judged with the step-5 run. `dedup-check` 0 failed; kit corpus + `tool_census --check: OK`. Effort: high (Drew's choice).
|
||
- **S96 — T5 step 5 DECIDED (Max, delegated by Drew): re-exemplar `E_func_80168B70` from the majority text; the 48 late-overlay conflicts
|
||
stay ledgered for the types phase** (the decision and its reasoning are in §Decisions). NEXT: `share_body.py --reexemplar <h_exact>` —
|
||
header ← the majority private text (`choose_exemplar` over the private copies, the current header's text counted once per includer),
|
||
`bind_alias_header` if needed, gate every current includer's binary FIRST (roll the header back on any red), then the class as a normal
|
||
extend batch (`--only`), the registry extended with the members that passed; its ledger row removed just before the run. Effort: high.
|
||
- **S96 — T5 step 5 DONE: `share_body.py --reexemplar 93d5fccdcc`** (`.run/P35/share/run_reex_93d5fccd.log`, 84 s): `header
|
||
src/shared/ov/func_80168B70.h ← src/ov_SC01_000/ov_SC01_000_jr_8015C32C.c:5792 (majority text; texts 1); 134 private copies, 7 includers
|
||
to re-gate first` → `7/7 includer binaries green under the majority text` → `[reex_93d5fccd] 1 classes · 129 sites in 129 TUs · gating
|
||
141 binaries` → **`gated 141/141 binaries green · registered 0 groups · extended 134 members · rejected classes 0`**; the previous
|
||
ledger row dropped (48 rows remain, all late-overlay declaration conflicts for the types phase). The old header text is
|
||
`.run/P35/share/reexemplar_93d5fccd.old.h`. `dedup-check` 0 failed; SETUP paragraph extended (`--batches`, the snapshot restore, the
|
||
cause rule, `--repair-registry`, `--reexemplar`; R21); kit corpus + `tool_census --check: OK`. **Bucket 0 is CLOSED:** 183 classes at
|
||
the start → 135 fully shared, 48 ledgered TU-CONFLICT with real diagnostics.
|
||
- **S96 — R22 after bucket 0** (`.run/P35/baseline/r22_t5_bucket0.log`): `make clean && make extract-all JOBS=16 && make check-all JOBS=16`
|
||
→ `extract-all: 217 extracted, 0 failed of 217 (+ main, serial)` · **`check-all: 218 passed, 0 failed of 218`** · `wall=84.02 s
|
||
user=1334.73 s sys=185.72 s` · `exit=0` — every T5 bank so far (five commits) proven from clean; fleet CPU 2,230 s (T4) → 1,335 s.
|
||
- **S96 — T5 bucket `new`, batch 1** (`.run/P35/share/run_new1.log`, 99 s; `--apply --bucket new --batch 120 --batches 1`): `census 29 s ·
|
||
extend (registered-incomplete) 5 classes · new (unregistered same-vram) 915 classes / 3,567 private sites` · `[new1] 120 classes · 1482
|
||
sites in 535 TUs · 120 new headers · gating 165 binaries` → **`gated 165/165 binaries green · registered 120 groups · extended 0 members ·
|
||
rejected classes 1`** ([new1] ov_SC07_010: class 2e822a8f62 REJECTED — src/ov_SC07_010/../shared/ov/func_80140D68.h:41: 'D_800B9A02' undeclared (first use this function) — an exemplar body relying on a
|
||
file-scope extern outside its definition; ledgered TU-CONFLICT, 49 rows). 795 classes of the bucket remain; the 5 unexcepted B classes
|
||
get an `--bucket extend` run at the end.
|
||
- **S96 — T5 bucket `new`, batch 2** (`run_new2.log`; record copied to `batch_new2.json`): `120 classes · 360 sites in
|
||
69 TUs · 120 new headers · gating 9 binaries` → **`gated 9/9 binaries green · registered 120 groups ·
|
||
extended 0 members · rejected classes 0`**.
|
||
|
||
- **S96 — T5 bucket `new`, batch 3** (`run_new3.log`, 107.16 s; `batch_new3.json`): `120 classes · 358 sites in
|
||
59 TUs · 120 new headers · gating 12 binaries` → **`gated 12/12 binaries green · registered
|
||
120 groups · extended 0 members · rejected classes 0`**.
|
||
|
||
- **S96 — T5 bucket `new`, batch 4** (`run_new4.log`, 109.92 s; `batch_new4.json`): `120 classes · 360 sites in
|
||
69 TUs · 120 new headers · gating 15 binaries` → **`gated 15/15 binaries green · registered
|
||
120 groups · extended 0 members · rejected classes 0`**.
|
||
|
||
- **S96 — R22 after bucket `new` batch 4** (`.run/P35/baseline/r22_t5_new4.log`): `check-all: 218 passed, 0 failed of 218` · `wall=221.86 s` · `exit=0`.
|
||
|
||
- **S96 — T5 bucket `new`, batch 5** (`run_new5.log`, 79.54 s; `batch_new5.json`): `120 classes · 329 sites in
|
||
75 TUs · 120 new headers · gating 15 binaries` → **`gated 15/15 binaries green · registered
|
||
120 groups · extended 0 members · rejected classes 1`**; rejected: [new5] ov_SC03_126: class 60be554c70 REJECTED — src/ov_SC03_126/../shared/ov/func_8017EF6C__60be554c.h:4: 'D_80126942' undeclared (first use this func.
|
||
|
||
- **S96 — T5 bucket `new`, batch 6** (`run_new6.log`, 28.66 s; `batch_new6.json`): `120 classes · 240 sites in
|
||
40 TUs · 120 new headers · gating 14 binaries` → **`gated 14/14 binaries green · registered
|
||
119 groups · extended 0 members · rejected classes 1`**; rejected: [new6] ov_SC04_006: class 250070c4bf REJECTED — src/ov_SC04_006/../shared/ov/func_8017D0FC__250070c4.h:4: 'D_80126954' undeclared (first use this func.
|
||
|
||
- **S96 — R22 after bucket `new` batch 6** (`.run/P35/baseline/r22_t5_new6.log`): `check-all: 218 passed, 0 failed of 218` · `wall=84.30 s` · `exit=0`.
|
||
|
||
- **S96 — T5 bucket `new`, batch 7** (`run_new7.log`, 32.90 s; `batch_new7.json`): `120 classes · 240 sites in
|
||
63 TUs · 120 new headers · gating 34 binaries` → **`gated 34/34 binaries green · registered
|
||
120 groups · extended 0 members · rejected classes 0`**.
|
||
|
||
- **S96 — T5 bucket `new`, batch 8** (`run_new8.log`, 26.95 s; `batch_new8.json`): `75 classes · 150 sites in
|
||
30 TUs · 75 new headers · gating 4 binaries` → **`gated 4/4 binaries green · registered
|
||
75 groups · extended 0 members · rejected classes 0`**.
|
||
|
||
- **S96 — R22 after bucket `new` batch 8** (`.run/P35/baseline/r22_t5_new8.log`): `check-all: 218 passed, 0 failed of 218` · `wall=88.98 s` · `exit=0`.
|
||
|
||
- **S96 — T5 CLOSE.** Bucket `new` done in 8 batches (`share_body_cycle.sh` 3–5 and 6–8 unattended; batches 1–2 by hand): 3,519 sites in
|
||
980 TUs → 914 groups registered, 3 classes rejected (new1, new5, new6 — each a body relying on a declaration outside its definition,
|
||
ledgered TU-CONFLICT); R22 green after batches 4, 6 and 8 (`r22_t5_new{4,6,8}.log`, `check-all: 218 passed, 0 failed of 218`). The
|
||
tail run (`run_extend_tail.log`): `new (unregistered same-vram) 0 classes / 0 private sites`; the 5 leftover B classes had NO private
|
||
copy — every one of their 141 instances already includes the header while the registry listed 16 (or 2): the eight VERBOSE `members:`
|
||
groups, which `dedup_extend.add_members_surgical` skipped silently (no `binaries:` line) — the inverse defect, the registry BEHIND the
|
||
source. Fixes: `add_members_surgical` refuses a verbose group loudly (R43); `--repair-registry` gained the second half (an
|
||
including-but-unlisted member is added; a verbose group is converted to shorthand): `639 including-but-unlisted members added to 5
|
||
groups (5 converted to shorthand: D1_ov_dup_8012AD64, E_func_80128EA8, E_func_8012A568, E_func_80132EC4, E_func_80138C30)`; 3 verbose
|
||
groups remain (I0_clearTbl40 + the two SC01_005 setters, cross-address controls). **Verify (T5's milestone items, literal):**
|
||
`share_census --check` → **`S1: one source per unique function — 10,180 classes, 10,180 satisfied (3,580 twin-covered, 51 excepted,
|
||
3,801 deferred cross-address), 0 VIOLATION(S) — OK`**, exit 0 (`.run/P35/census/check_t5.txt`); verdicts A 2,760 · B 149 · C 5,751 ·
|
||
D 1,518 · M 2; **same-vram unregistered 51 classes · 160 copies · 109 collapsible — exactly the ledger (50 TU-CONFLICT + 1
|
||
GATE-REJECT, every row with its diagnostic)**; `dedup-check: 3135 validated, 0 failed | C1 coverage 260543/260543`;
|
||
`audit-binaries: OK`; kit corpus + `tool_census --check: OK`; the last R22 (`4ba4b2080`) stands — no build input changed after it
|
||
(the tail run shared 0 sites; the registry is not a build input). **Before → after (T4 close → T5 close):** registry groups 2,220 →
|
||
3,135; shared headers 2,215 → 3,137; same-vram backlog 1,099 classes / 4,755 private copies / 3,658 collapsible → 51 / 160 / 109
|
||
(all ledgered); verdict A 1,712 → 2,760; fleet clean-run wall 145 s → 84 s (CPU 2,230 s → 1,335 s); 23 commits in T5. **T5 ☑.**
|
||
|
||
- **S96 — T6 part 1: freeze + retire.** `tools/frozen.py` (the one refusal, printed from main() — never at import: cdecl imports
|
||
gen_harvest_targets, 18 tools import family_remap). **FROZEN (14, status FROZEN + successor in the dictionary):** the plan's 7
|
||
(family_sweep, gen_harvest_targets, p16_improve, recover_giant, restore_dropped_decls, normalize_self_decls, o0_subsplit) + 7 the
|
||
guard census found reading or writing the macro form (inject_capped_externs, aprop_autodraft, canon_sig_reconcile, conform_decls,
|
||
blocker_probe, demacroize, and family_remap's command line — it writes a `#define DEFINE_func_` head; its library stays). Deviation
|
||
from the plan's "7": the S95 ast census measured 30 hits, not 22; twin_sweep/recover_integration (which exec frozen CLIs) stay LIVE
|
||
and would print the refusal; family_cousins stays LIVE (`make atlas` runs it) and loses its dead branch in part 2. **RETIRED (4, git mv
|
||
to tools/sunset/, README rows):** dedup_propagate + dedup_extend → share_body.py (add_members_surgical and the library surface
|
||
onboarded_overlays / load_sig / registered_addrs / sym moved verbatim; validate_targets imports share_body; inject_capped_externs
|
||
frozen), macro_draft (product: the headers), test_reconcile_ledger (retired with its subject). Six lane callers repointed to
|
||
`share_body.py --apply --bucket new --batches 1` (gate_stage, gate_lane, grinder's message, lora_grind, auto_driver, bulk_harvest);
|
||
audit_binaries' hint reworded. `tool_census` accepts FROZEN. Verify: every touched file compiles; `family_sweep.py --source x` →
|
||
`FROZEN since Phase 35 … Successor: tools/share_body.py` rc 1; `import share_body, family_remap, gen_harvest_targets,
|
||
validate_targets` OK; no live tool names a retired file; `tool_census --check: OK` after `make kit-corpus` (the first chain checked
|
||
BEFORE regenerating and a `;` let a partial commit through — R97 again; amended into one commit).
|
||
|
||
- **S96 — T6 part 2: the dead macro branches dropped + the guard.** Twelve live tools lost their macro-form code: audit_binaries
|
||
(`SHARED_INCLUDES` = the prelude only), auto_driver + bulk_harvest (`git add src/shared`), build_engine_types (the generated comment),
|
||
cdecl (`MACRO_STMT` = INCLUDE_ASM only; the fifteen-incumbent differential reduced to its honest statement — the macro header is gone —
|
||
and its dead body deleted, the gen_harvest_targets import with it), dedup_integrate (`PARAM_FUNC_RE` = SHARED_FN), export_pairs (mines
|
||
the shared headers under src/shared/ instead of macro bodies), family_cousins (`seed_body_ref` finds the shared header), fix_arity_callers
|
||
(the shared-header glob only), jr_isolate_all (the prelude include maps to engine_types.h + common.h), recover_integration (message +
|
||
snapshot list), sig_unify (help text). **The guard** (`tool_census.py --check`, `macro_form_guard`): every LIVE `.py` parsed with `ast`;
|
||
a non-docstring string constant carrying `DEFINE_func_` or `engine_core.h` is a gap; FROZEN, retired and six whitelisted detectors
|
||
(share_census, macro_to_header, share_body, overlay_src_split, gccmap_cites, tool_census) excluded; `--guard-root DIR` runs it against
|
||
another tree. **Verify:** `macro-form guard: 0 LIVE tools reference the retired form (217 scanned, 14 frozen, 6 whitelisted detectors, 38
|
||
retired)` · `tool_census --check: OK`. **Negative control (R39):** a worktree of the pre-T4 commit `0e38b51a3` → `15 LIVE tools reference
|
||
the retired form (197 scanned, 14 frozen, 4 whitelisted)` — exactly the twelve fixed here + the three retired, none frozen or whitelisted.
|
||
**`make tools-health` went red twice, both instrument findings fixed at the cause:** (1) `progress.py`'s R32 bucket assertion —
|
||
`func_801EF790`/`func_801EF85C` (md_SC03_073/074/075/078, empty-bodied shared headers) in BOTH `real` and `empty`: the include rule
|
||
classifies an empty header body as EMPTY while `report()`'s fold `real ∪= shared` re-added every included member; under the macro
|
||
form an empty shared body had been invisible to the classifier and counted as REAL through that fold. Fix: the fold excludes `empty`.
|
||
**Two published-number corrections follow (R14/R75; stated here, carried to T7/T8):** the fleet denominator **363,221 → 363,680
|
||
(+459)** = ov_SC03_015 +219 (its single-site macros, the plan's expected correction) + ov_SC03_118 +3 + 2 in each of ~118 overlays
|
||
(the never-extended members of the five under-listed groups — macro sites invisible until T4 made them includes); and **FLEET REAL
|
||
substantive 360,744 → 350,533 (−10,211)** with EMPTY up by the same — empty-bodied shared functions were REAL under the macro form's
|
||
fold and are EMPTY now, honestly; the instruction-weighted metrics (13,492,113 / 5,820,205 / 45,150) are unchanged. (2) `timeline.py`'s
|
||
self-check: its last row follows the COMMITTED digest (T2's lesson) — this commit carries the corrected `docs/progress.json` + README
|
||
block + `progress.fleet.md`; the timeline is regenerated in the next commit and the chain re-run for T6's verify line.
|
||
|
||
- **S96 — T6 CLOSE.** `make tools-health` red three times, each an instrument finding fixed at its cause, green on the fourth: (1) the
|
||
`progress.py` fold (above); (2) `doc_links`: `docs/wiki/The-dedup-engine.md` linked the retired `tools/dedup_propagate.py` — repointed to
|
||
`share_body.py` with a note (T8 rewrites the page); (3) `kit_lint` LEAK: my dictionary row for `share_body_cycle.sh` carried rule ids
|
||
(`R42`, `R22`) — project-specific text the kit's manifest must not inherit — reworded. **Verify (T6's line):** `tools-health: OK — sigs
|
||
fresh; corpus(+resident) + cdecl + binaries + report(lint+dedup) + cookbook-index all green.` (`.run/P35/baseline/tools_health_t6d.log`,
|
||
484 s, exit 0) with `macro-form guard: 0 LIVE tools reference the retired form (217 scanned, 14 frozen, 6 whitelisted detectors, 38
|
||
retired)`; the timeline regenerated by the chain against the committed digest (74 rows, self-check OK). **T6 ☑.**
|
||
|
||
- **S96 — T7: the invariants wired + every published number regenerated.** Makefile `tools-health`: after `audit-text-sources`, before
|
||
`report BINARY=main`, two rungs — `share_census.py --selftest` (7/7) and `share_census.py --check --strict-macros --strict-text --quiet`
|
||
(exit code, R97) — so `progress.py` reads a fresh census json. `dedup_integrate --check` gained **C2c** (one source under `src/shared/`
|
||
defining exactly one function) and **C2d** (every member's site is an include of THIS group's source — from the census's per-instance
|
||
forms, one cached scan; a `def` site = the registry ahead of the source). `progress.py`: `counts.unique_function_bodies` (REAL + EMPTY −
|
||
shared instances + their one header each = **105,007** bodies written once in C), `duplicate_source_copies` / `_classes` (**160 / 51**,
|
||
from the census json, all ledgered), a dated `corrections` list (T2 +7; T6 +459; T6 REAL −10,211) and the README sentence "One source
|
||
per unique function (Phase 35): …". **The second oracle (R34) disagreed with the byte oracle and the disagreement is measured:** of
|
||
4,312 same-address definition texts duplicated across TUs, 3,495 were a twin and its primary (one TU — the oracle now counts distinct
|
||
TUs), 28 are ledgered, **380 texts (1,668 sites) sit inside the E/F-deferred cross-address classes** (an empty body's hash spans every
|
||
address, so gate 1's deferral took its same-address pairs along — published as the names phase's inheritance), and **38 texts (2,030
|
||
sites) are the same C at the same address compiling to DIFFERENT bytes per binary** (singleton h_exact classes — e.g. `func_8012AAAC`,
|
||
138 copies, 133 byte patterns, h_norm differs in 5 ways): one source the byte tier cannot register — published as PENDING, a decision
|
||
for Drew (an `h_text` tier now, or the names phase); 0 texts a shared byte class holds across two TUs — the first oracle has no hole.
|
||
**Negative control (R39), in place on the probe class `S_func_801681FC` (2 members):** ov_SC05_009's include reverted to the 19-line
|
||
private copy → `S1 … 10,179 satisfied … 1 VIOLATION(S) — FAIL` naming `B - h=c1c085b28d … ['ov_SC04_008', 'ov_SC05_009']`, exit 1;
|
||
`[FAIL] S_func_801681FC: ov_SC05_009:0x801681fc … its site is a def in … (C2d)`, `dedup-check: 3134 validated, 1 failed`; restored →
|
||
`0 VIOLATION(S) — OK`, exit 0. Instrument slips on the way, each caught by the run: a missing `os` import (C2d's first run), the
|
||
`--check` path not keeping per-instance records, twin pairs counted as two TUs, "classed" mis-defined as any classed site (fixed to
|
||
"one class across two TUs"). **Verify:** `make tools-health` → `tools-health: OK — …` (`.run/P35/baseline/tools_health_t7.log`, 551 s,
|
||
exit 0) with `S1: … 10,180 satisfied … 0 VIOLATION(S) — OK`, `dedup-check: 3135 validated, 0 failed | C1 coverage 260543/260543`,
|
||
`macro-form guard: 0 LIVE …`, `progress.py --check: … fresh`, `timeline --check: fresh (74 rows)`. **T7 ☑.**
|
||
|
||
- **S96 — the second oracle's finding DECIDED (Max, delegated by Drew): T5b (a text tier) for the 38 byte-variant same-address texts;
|
||
the 380 inside the deferred classes stay deferred** — the reasoning is in §Decisions; T5b's design in its task row. NEXT: T5b at high.
|
||
|
||
- **S96 — T5b: the text tier.** `config/dedup.us.yaml` gains `tier: h_text` (the group hash = the normalized text; verbose members each with
|
||
their own `h_exact`); `dedup_integrate` C1 checks an h_text member against ITS recorded byte hash (TIERS += h_text); `share_census`
|
||
indexes h_text groups by site (a byte class whose sites they list is registered by TEXT — no `extra`), exports `text_classes()` (same
|
||
normalized text, same address, ≥2 distinct TUs, outside the deferred/ledgered sets) and makes the S1 text half a VIOLATION (was PENDING);
|
||
`share_body --bucket text` shares them (header `func_<VRAM>__t<hash8>.h`, banner `h_text`, the body from any site — identical by
|
||
construction — declarations stay in each TU; registration in the verbose form with per-member h_exact). **Run (`run_text1.log`, 1,489 s):**
|
||
`text tier — 38 same-address text classes / 2,030 private sites` · `[text1] 38 classes · 1957 sites in 1540 TUs · 38 new headers · gating
|
||
138 binaries` → **`gated 138/138 binaries green · registered 38 groups · extended 0 members · rejected classes 0`** (e.g. `T_func_8012AAAC`:
|
||
138 members, 133 byte patterns, one header). **Verify:** `dedup-check: 3173 validated, 0 failed | C1 coverage 262573/262573`; the strict
|
||
census `S1 … 10,180 satisfied … 0 VIOLATION(S) — OK` (twin-covered 3,580 → 3,507: the twin pairs' sites are text-tier includes now) with
|
||
the second oracle `0 same-address definition texts duplicated` and `381 … inside the deferred cross-address classes (1,668 sites)`;
|
||
**R22 `check-all: 218 passed, 0 failed of 218`, 90 s** (`r22_t5b.log`); `progress.py --check … fresh` — the digest now says 103,015
|
||
bodies written once (3,173 headers instantiated 262,573 times), 160 duplicate copies in 51 ledgered classes, 38 text-tier functions
|
||
(2,030 sites), 1,668 same-address copies of 381 tiny bodies deferred to the names phase (the README sentence carries all of it). The
|
||
census json is rewritten after the strict-text keys are set (progress.py read empty counts before). SETUP §T7/T5b (R21). **Verify
|
||
(the chain):** `tools-health: OK — …` (`.run/P35/baseline/tools_health_t5b.log`, 439 s, exit 0) with `S1 … 10,180 satisfied … 0
|
||
VIOLATION(S) — OK`, `dedup-check: 3173 validated, 0 failed | C1 coverage 262573/262573`, `macro-form guard: 0 LIVE …`; the timeline
|
||
regenerated (74 rows). **T5b ☑.**
|
||
|
||
- **S96 — T8: the record.** `docs/wiki/The-dedup-engine.md` rewritten for the shared-source model (the shape, the three tiers incl.
|
||
`h_text`, the tool and its four first-day defects, the S1 gate with the second oracle, the numbers as a dated derivation, the honesty
|
||
rules); `Repository-layout.md` (`src/shared/`), `Where-the-project-goes-next.md` (item 3 done; the macro row 5,147 → 0),
|
||
`Verification-and-progress.md` (the S1 rungs in the reviewer table); how-to ch.10 (dedup in both eras; the old "leave the backlog" rule
|
||
scoped to matching); README prose ("shared engine code lives as one plain-C header per function"); `gen3-standards.md` (§4 row
|
||
struck through with the Phase-35 measurement, the DoD's "0 macro bodies" marked met); `gen3-handoff.md` (§2.2 and §3 rows as dated
|
||
snapshots); cookbook **§453** (the include-at-site share, the exemplar rule, the gate, the four defects, S1, the second oracle, the two
|
||
corrections) + a retirement banner on §14; `docs/decision-log.md` P35 (R31: the reversal with the sotn evidence, the session death and
|
||
recovery, the six decisions, the measurements, four hindsights); `docs/accelerators.md` P35 (five); DIGEST §4 (the Phase-35 doc map);
|
||
the memory `dedup-backlog-leave-it` rewritten as REVERSED with its index line. **Verify:** `doc_links: 56 documents, 455 relative links
|
||
checked, 0 pending, 0 broken` (strict) · `wiki_render --selftest: 12 cases, 0 failed; reachability: 32 pages, 0 unlisted` · the cookbook
|
||
index regenerated (`1170 sections, 14 symptom buckets`) and `cookbook-index OK` · `kit_coverage: OK` · kit corpus + `tool_census
|
||
--check: OK`. **T8 ☑.** (`tools/sunset/README.md` rows were written at T6.)
|
||
|
||
- **S96 — T9, the close (run in S96 at xHigh by Drew's choice; the plan said Max in a fresh session).** R22 from clean:
|
||
`extract-all: 217 extracted, 0 failed of 217 (+ main, serial)` · **`check-all: 218 passed, 0 failed of 218`** · `wall=87.62 s` · exit 0
|
||
(`.run/P35/baseline/r22_t9.log`). `make tools-health` run 1 (467 s) was green through S1 / dedup-check / the guard and red only at
|
||
`tool_census --check` — the kit's record copies of `DIGEST.md` and the new `PhaseEnd_Phase35.md` (both written while the chain ran);
|
||
`make kit-corpus` regenerated them and the chain was re-run (its line is appended below). The headless Ghidra MCP stopped via the
|
||
sentinel: `Save succeeded for processed file: /SLUS_007.26` (R23; unused all phase). `PhaseEnd_Phase35.md` written (the metrics table,
|
||
the milestone items with literal output, the reviewer sequence, seven rule candidates, the recap); DIGEST §0 refreshed + §2's P35
|
||
synopsis appended (step 3b). **The chain, run 2:** `tools-health: OK — sigs fresh; corpus(+resident) + cdecl + binaries +
|
||
report(lint+dedup) + cookbook-index all green.` (`.run/P35/baseline/tools_health_t9.log`, 431 s, exit 0, 0 gaps) with `S1 … 10,180
|
||
satisfied … 0 VIOLATION(S) — OK`, `dedup-check: 3173 validated, 0 failed`, `macro-form guard: 0 LIVE …`. The log archived to
|
||
`phase-ends/logs/Phase35.md` (R19); `make kit-corpus` + `tool_census --check` after the PhaseEnd's last edit; everything left
|
||
uncommitted for Drew's close commit (R6). **T9 ☑ — Phase 35 milestone met; v2.1.0.**
|
||
|
||
## Approved plan (verbatim, gate 1 — 2026-09-08)
|
||
|
||
# Phase 35 — Gen3 opens: the dedup phase, "one source per unique function" (v2.0.0 → v2.1.0)
|
||
|
||
> Plan mode, Max, S94, 2026-09-08. Gen3 order set by Drew this session: **dedup → pins → structs → names**, one phase each,
|
||
> planned one at a time; this plan is Phase 35 only. Gate 1 also ratifies **R96–R99** (Drew: "ratify all four").
|
||
|
||
## Context
|
||
|
||
Gen2 closed at v2.0.0 (218 binaries byte-identical, the repository public). Gen3's charter is readability on a byte-exact
|
||
floor. The first readability defect is that the same function exists many times in the tree, in two forms:
|
||
|
||
1. **Shared bodies as macros.** The dedup engine instantiates one body in every location overlay as a `DEFINE_func_XXXXXXXX()`
|
||
macro from `src/shared/engine_core.h` (8.8 MB, 227,730 lines, backslash-continued, `#include`d whole by 3,981 TUs; measured
|
||
cost 0.35 s + 94 MB RSS of cpp per TU ≈ 19 CPU-minutes per fleet build). `docs/gen3-standards.md` §2 rule 3 requires shared
|
||
engine functions to live as C, not macros.
|
||
2. **Literal duplicate copies.** The July-2026 family sweeps banked proven bodies as a private copy per overlay and registered no
|
||
group. On 2026-09-02 that backlog was deferred on the belief "sotn writes duplicate funcs explicitly" (memory
|
||
`dedup-backlog-leave-it`); the decision log caveated that the claim rested on one cookbook parenthetical — which is about a
|
||
cross-jump barrier idiom (`docs/matching-cookbook.md:253`), not about sharing.
|
||
|
||
**Verified this session, as data (X2), from sotn-decomp's tree:** sotn does NOT duplicate. Shared stage code lives once as plain C
|
||
in `src/st/<name>.h` (full definitions, `static` tables, `#ifdef STAGE_IS_…` where one stage differs), and each stage overlay
|
||
carries a ~90-byte `.c` stub (`#include "nz0.h"` + `#include "../e_red_door.h"`) that instantiates it at that stage's link
|
||
position; per-stage parameters are `static` data in the stub before the include. "Written once, instantiated per overlay by an
|
||
include at the site" is the community shape — structurally what our macros do, minus the macro form. The 2026-09-02 decision is
|
||
reversed on this evidence (recorded in the decision log at task 8).
|
||
|
||
**Drew's requirement (this session):** after the phase the tooling knows that functions are no longer duplicated across overlays
|
||
and that there is exactly one source for a unique function — an invariant asserted by a gate (R36), not remembered.
|
||
|
||
**Decisions at gate 1 (Drew, AskUserQuestion):** twin binaries share one source directory · cross-address classes are censused
|
||
and deferred to the names phase · macro-era tools are frozen with a loud refusal (the direct predecessors retire) · R96–R99 ratified.
|
||
Added by the plan: **no "trivial" exception** — a duplicated 3-instruction accessor is still one function (this project already
|
||
shares 5-instruction bodies; 341 tiny classes / 42,744 instances are in scope).
|
||
|
||
## Measured shape (this session; every number carries its rule; task 1 makes them an instrument)
|
||
|
||
- **Registry** (`config/dedup.us.yaml` via `dedup_integrate.group_members`): 2,220 groups / 255,708 members, all `h_exact`; 2,212
|
||
shorthand (one vram + name for every member), 8 verbose; median 141 members; spans 2,207 ov / 12 md / 1 main; one group with
|
||
differing member names (main's `clearTbl40` pair). Sources: `engine_core.h` 2,215 · `ov_setters.h` 3 (name-parameterized
|
||
SETTER/RETCONST, the SC01_005≡006 pair) · `clearTbl40.h` 1 · `func_80144B9C.h` 1 (a 319-line ordinary-C header included by
|
||
141 `<ov>_o0b.c` — already the target form; "a header-share is as valid as a macro-share", cookbook §38).
|
||
- **The header:** 5,147 `#define DEFINE_func_` lines = **3,516 distinct macros**; 1,631 defined twice (copy 1 inside the `#ifndef
|
||
ENGINE_SHB` block ending at line 99,164, copy 2 after; 1,627 identical, 4 differ in one extern's `(void)` vs `()`; cpp keeps the
|
||
LAST); **2,215 shared (all registered) · 218 single-site (217 of them in ov_SC03_015, 1 in ov_SC03_118) · 1,083 dead.** 4,600
|
||
carry `extern` preambles, 451 asm-label aliases, 357 register pins (Phase 36's; kept verbatim), 0 `#if`, 0 `__LINE__`/`__FILE__`;
|
||
exactly 8 non-`DEFINE_` directives (the prelude is provably complete). All macros zero-argument. Invocation sites: 255,947 lines
|
||
in 3,153 files across 153 binaries; the site is always one line `DEFINE_func_X() /* dedup: … */`; max 539 sites in one TU (p50 31).
|
||
The charter's "5,147 macro bodies" counted define lines (R14/R41 correction, published at task 8).
|
||
- **Why include-at-site is byte-neutral and a separate object is not:** a macro expands AT THE SITE to `[externs + definition]`,
|
||
so its `extern`s become file-scope declarations at that line and later functions rely on them (cookbook §112; decision-log
|
||
:441-457). An include reproduces the text at the same point. **Probe P1 (run): three real bodies compiled both ways through
|
||
cpp → cc1 → maspsx → as give byte-identical `.o` files (1,632 B, identical relocations); cc1 adds only a `.file` directive that
|
||
`as` consumes.** A separate `.c` object would delete ~4,000 declaration lines from ~3,153 TUs and cannot place interleaved
|
||
functions (shared and local bodies interleave in address order, `src/ov_SC06_033/ov_SC06_033.c:1-31`). `src/shared/` is not
|
||
pruned from main's source find (Makefile:841-847) → shared bodies are `.h`. `-MMD` tracking (:849-855) recompiles on header edits.
|
||
- **Backlog, from the 218 fleet sig files (362,389 instances; 255,937 macro sites · 105,262 inline definitions · 1,190 alias-form
|
||
unaccounted):** h_exact classes with ≥2 instances fleet-wide: **10,180 (280,801 instances)**; registered 1,994 hashes / 2,220
|
||
groups; **206 registered classes have 3,996 instances not listed as members** (never extended); unregistered classes with ≥2
|
||
inline copies: **7,956 classes / 20,038 copies / 12,082 collapsible copies / 478,773 ins** — 4,259 same-vram (9,698 copies),
|
||
**3,697 cross-vram (10,340 copies; the member's own name differs per site → deferred)**, 54 cross-space. Under the old propagate
|
||
rule alone: 11,289 copies / 1,932 addresses / 5,083 bodies. Source text across copies: **91% identical, 8.3% (403 bodies) differ**
|
||
(`uniquify_type` suffixes, extern scoping, 9 with pins) → one chosen text re-gated per member; type-carrying bodies via
|
||
`tools/lift_types.py` first.
|
||
- **Twin binaries:** five overlay pairs have IDENTICAL payloads (equal `config/check.*.sha`): SC04_018/019, SC03_118/119,
|
||
SC03_014/015, SC02_000/003, SC01_005/006 (10 of 141 aliases; 136 distinct payloads); their piles ≈ 7,900 of the 11,289
|
||
same-vram copies (~70%). Each twin's yaml differs from its primary's only in `target_path` (+ a carve delta: SC04_019 has one extra
|
||
`_jr_80181804` split, SC03_015 two extra); `overlays.mk` blocks differ only by alias; the linker script names objects by alias.
|
||
- **Header-name collisions:** 6,346 of 13,355 overlay-slot vrams host >1 h_exact class fleet-wide (max 134) → a shared header
|
||
cannot be keyed by address alone; of today's 2,220 groups, 1,961 get a clean name and 259 need a stable suffix.
|
||
- **Consumers:** 55 live tools reference the architecture; 22 text-parse the macro form; the rest ask cpp (form-independent) or only
|
||
read the registry. Two would CRASH `make tools-health` on the header's deletion: `cdecl.audit_differential` (`tools/cdecl.py:1292-1323`
|
||
opens `engine_core.h` unconditionally; `make audit-cdecl` is in the chain) and `overlay_src_split` (:667 degrades to a silently
|
||
EMPTY macro table). 51 tools build `src/<binary>` paths themselves; only `compile_only.py` reads the Makefile's `<alias>_SRC_DIR`;
|
||
`corpus.py:312` maps link objects to `src/{binary}/{subseg}.c`. `progress.py` undercounts ov_SC03_015 by 219 (single-site macro
|
||
sites invisible to `classify()`) — a real undercount the phase corrects and publishes.
|
||
|
||
## Target architecture (decided)
|
||
|
||
```
|
||
src/shared/
|
||
engine_prelude.h # engine_types.h + ENGINE_SHB — every overlay/module TU includes this at line 2 (was engine_core.h)
|
||
engine_types.h # unchanged (tools/build_engine_types.py owns it)
|
||
ov/ func_80128EA8.h · func_80150000__2905b55f.h # the overlay slot 0x80128158
|
||
slot_800CAE08/ … slot_801EF468/ # the 11 module slot bases, derived from <b>_VRAM_BASE (R33)
|
||
main/ func_80037004__a0744d60.h # the clearTbl40 pair (the one intra-binary, name-parameterized share)
|
||
```
|
||
- **One plain-C header per shared body**, keyed by the h_exact class: directory = address space, filename = `func_<CANON_VRAM>` +
|
||
`__<h8>` iff (space, vram) hosts >1 class fleet-wide or the class spans >1 vram/space — both predicates on immutable ROM data, so
|
||
names are stable under later additions (R48: never a bare name). Flat per space (address-named files list in assembly order).
|
||
- **Body text** = the live macro's LAST definition, continuations stripped, re-indented; every `extern`, alias, comment and pin
|
||
verbatim (asserted to round-trip to the same token stream). **Pure fragment** (no includes of its own; the prelude comes from
|
||
the TU, sotn's contract). **No include guard** (a second include of a fixed-name header is the loud duplicate-definition error
|
||
we want; the parameterized form legitimately includes twice). Banner: h_exact, canonical vram, "one source — instantiated by
|
||
#include at each member's site; members: config/dedup.us.yaml" (no member count stored — derived, R51).
|
||
- **Two site forms, only two:** `#include "../shared/ov/func_80128EA8.h"` for same-vram classes; for the cross-vram control
|
||
(clearTbl40 only in this phase) `#define SHARED_FN func_80037334` / `#include …` / `#undef SHARED_FN`, the header defining
|
||
`void SHARED_FN(void) {…}`. More than two parameters → refuse (R43). The parameterized form is what the names phase will
|
||
apply to the deferred cross-vram classes — clearTbl40 is its worked example and permanent negative control.
|
||
- **Single-site bodies** with no fleet-wide twin → inlined at their site as plain C; **dead macros** dropped (listed by name in the
|
||
run log — counted, never silent); `engine_core.h`, `ov_setters.h`, `clearTbl40.h` deleted; `func_80144B9C.h` moved to `ov/`,
|
||
its guard removed (one shape). End state: `src/shared/` = the prelude, `engine_types.h`, the per-function tree. Nothing else.
|
||
- **Twin binaries:** `config/overlays.mk` gets `ov_B_TWIN_OF := ov_A` and `ov_B_SRC_DIR := src/ov_A`; the Makefile maps a twin's
|
||
objects to its OWN `build/src/ov_B/` from the primary's sources (`-O0` object lists and `.d` paths twin-aware; `check-all`'s
|
||
per-binary parallelism never races); the twin's yaml = the primary's carve with its own `target_path`/`sha1`; its `check.sha`
|
||
still proves it; `src/ov_B/` deleted. **One oracle "binary → source dir"** (Makefile-derived, R33) replaces the hardcoded
|
||
`src/<binary>` shapes in the Gen3-live tools. The registry keeps twins as members (their site resolves through the oracle to
|
||
the primary's TU); no per-function groups for twins.
|
||
- **Registry v2:** same file, same `group_members` oracle, shorthand kept; `source:` = the header path; `func:` = the defined
|
||
token (`SHARED_FN` for the parameterized form, as `CLEAR_TBL40` is today); optional `form:`; **text-edited only, never
|
||
`yaml.safe_dump`** (the H5 precedent that decimalized every vram and deleted 47 comment lines while every gate stayed green,
|
||
`tools/dedup_extend.py:78-89`). `dedup_integrate --check` gains C2c (the source is under `src/shared/`, defines exactly one
|
||
function, and `func` is the token it uses — via `cdecl`) and C2d (every member's site file includes the source and no inline
|
||
definition of that member survives in the binary).
|
||
- **The S1 invariant** (permanent, in `make tools-health`): every same-(vram, h_exact) class with ≥2 instances is a registry group
|
||
with the include at every member, or twin-covered, or a ledgered exception (`config/dedup_exceptions.tsv`: class hash · nins ·
|
||
instances · reason code {JTBL-CARVE, O0-LOCAL, TU-CONFLICT, GATE-REJECT, PINNED, CROSS-VRAM-DEFERRED, CROSS-SPACE} · evidence);
|
||
no `DEFINE_func_` token in `src/`; and a second, sig-blind oracle: no name-blind normalized definition text appears in >1 file
|
||
under `src/` outside `src/shared/` (R34 — it cannot fail the way the sig join fails).
|
||
|
||
## The three tools
|
||
|
||
**`tools/share_census.py`** (permanent; T1) — inputs: the 218 sigs enumerated from `dup_report.BINARIES` (refuse on a missing
|
||
sig; never a glob — `.run/` holds 223 sig files incl. two non-fleet ones), the registry, `src/` through the source-dir oracle.
|
||
Source-form index: every sig instance resolves to exactly one of def / include / param-include / stub / asm-verbatim / linked /
|
||
blob — 0 or ≥2 forms is a coverage DEFECT (R32; strictly stronger than `progress.py`'s `unplaced`, which is vacuous where
|
||
`asm/<bin>/nonmatchings/` does not exist). Verdicts per class: A registered-complete · B registered-but-site-missing · C
|
||
unregistered-identical-text · D unregistered-differing-text · flags E cross-vram · F cross-space; `--json`, a human table,
|
||
`--check` (S1 + the text oracle, exit code), `--selftest` (an in-memory fixture of 2 binaries × 7 classes covering every
|
||
verdict, R39). Negative controls on the real tree: `func_80144B9C` = A/141; clearTbl40 = A+E; the 3 ov_setters = A;
|
||
`ov_SC01_005` = 657 unregistered items (S75 said 557 at 174 binaries; printed with its denominator, R41).
|
||
|
||
**`tools/macro_to_header.py`** (ONE-OFF; T3) — `--plan` / `--dry-run [--diff]` / `--apply --binaries …` / `--verify`
|
||
(idempotence: a second apply changes 0 files). `macro_index()` keeps the LAST definition (reusing `macro_draft.extract`'s
|
||
dedent/continuation strip, `tools/macro_draft.py:31-43`; the 4 divergent names cross-checked against `blocker_probe.py:83-90`'s
|
||
independent list); `directive_audit()` asserts the 5,147 + 8 directive inventory before touching anything; `site_index()` refuses
|
||
any site not of the one known shape; `header_path()` per the naming rule; `emit_header()` asserts token-stream round-trip;
|
||
`rewrite_tu()` (line 2 → the prelude; each site → its include, relative to the TU's own directory); `inline_single_site()`;
|
||
`drop_dead()`; registry `source:`/`func:` rewritten surgically. Asserts no emitted header carries a stray trailing `\`.
|
||
|
||
**`tools/share_body.py`** (permanent; T5 — the successor of `dedup_propagate` + `dedup_extend`) — `--plan [--bucket] [--limit]`,
|
||
`--apply --plan-file`, `--extend --binaries` (the never-extended 206 classes). Per class: exemplar = the registry's source if
|
||
registered; else the majority name-blind text; tie → pin-free; tie → shortest (the rule printed with every share). Guards reused
|
||
(R33): `family_hseq.has_mid_jr` → JTBL-CARVE; the `-O0` split guard (`dedup_propagate.py:676-687`, extended to `_o0b/_o0c`) →
|
||
O0-LOCAL; the inline-type guard (:750) → `lift_types` first. Sites replaced at the same position with the function's own
|
||
preamble (the `overlay_src_split` Item model). Then **L0 per TU** (build that one object before/after the batch's edits in that TU;
|
||
byte-equal, the Probe-P1 oracle; the exact per-TU recipe asked of the Makefile via `parallel_gate.generated_paths`' `--eval`
|
||
trick, never re-derived) → on failure bisect within the TU, drop the member, ledger the compiler's message class → **L1**
|
||
`make check BINARY=` per touched binary → register (shorthand) → **commit (R42)** → **L2** clean fleet per batch. "shared" is
|
||
printed only from the gate's success line (R66). Library surface: `onboarded_overlays`, `load_sig`, `sym`, `find_site`,
|
||
`registered_addrs` moved verbatim so the three importers of `dedup_propagate` change one line.
|
||
|
||
## Tasks (in order; one commit each; effort per `docs/effort-map.md`; every verify line read by exit code, R53/R97)
|
||
|
||
**T0 — Open + ground (Low, then Max for the probe).** `CURRENT_PHASE.md` with this plan verbatim; DIGEST §3 gets R96–R99 in full;
|
||
`.gitignore` allowlists `.run/P35/`; harness task list (R28). The R22 baseline from clean: `make clean && make extract-all JOBS=16 &&
|
||
make check-all JOBS=16` → `check-all: 218 passed, 0 failed of 218`, wall time recorded; `make tools-health` OK. **The probe (R37):**
|
||
in a scratch worktree, convert ov_SC06_033's 34 TUs by hand-driven script and run the L0 object A/B on all 34 (+ `make check
|
||
BINARY=ov_SC06_033`), measuring the `.d` growth and the per-TU cpp saving. Verify: `34/34 objects byte-identical`; `[ OK ] … (BYTE-IDENTICAL)`.
|
||
|
||
**T1 — `tools/share_census.py` + `config/dedup_exceptions.tsv` (Max; new instrument).** Built BEFORE anything moves so every claim has
|
||
a before/after. SETUP + dictionary rows (R21/R87). Verify: `--selftest` → `7/7 verdicts correct`; `--json` on the unchanged tree prints
|
||
the class counts above with their denominators, and the four negative controls; coverage line `classified == instances`.
|
||
|
||
**T2 — Teach the health chain the new forms while the old tree is still green (Max design, xHigh apply).** The source-dir oracle
|
||
(`corpus.src_dir(binary)` from the Makefile's `<alias>_SRC_DIR` — one function; `compile_only.src_dirs` folded in); `corpus.py` (definitions
|
||
may live under `src/shared/**.h`; :312 through the oracle); `cdecl.py` (`audit_differential` includes `src/shared/**/*.h` when
|
||
`engine_core.h` is absent; `MACRO_STMT` :109); `overlay_src_split.py` (+ `jr_isolate_all`: an include-site anchor kind; header-resident
|
||
bodies; :667 becomes a loud refusal); `progress.py` (`classify()` recognizes an include of a `src/shared/` path as the definition the
|
||
registry names for that site; the fold at :773 becomes `shared = real ∩ dedup_members` — derived from source, R33); `dedup_integrate.py`
|
||
(C2c/C2d; the oracle for `_src_paths`); `audit_binaries.py` (the prelude include; twin citizenship: equal `check.sha`, `SRC_DIR` = the
|
||
primary's, primary not a twin, carve equal — R36); `lint_symbol_refs.py` (:89 recursive glob); `shared_lock.py`; `fix_arity_callers.py`
|
||
(:41); `family_remap._unit_from_macro` → read the header; `blocker_probe.py`; `demacroize.py` (`macro_bodies()` reads headers);
|
||
`export_pairs.py`; `harvest_verify.py:167`. Both forms accepted during the transition. Verify: `make tools-health` OK on the UNCHANGED
|
||
tree (the negative control) and `share_census --selftest` still 7/7.
|
||
|
||
**T3 — Twin binaries → one source directory (Max for the mechanism on the probe pair; xHigh for the other four).** Probe SC01_005/006
|
||
(carves already equal): the `TWIN_OF` declaration, the Makefile mapping, the twin's yaml = the primary's carve with its own
|
||
`target_path`; delete `src/ov_SC01_006/`; `make extract BINARY=ov_SC01_006 && make check BINARY=ov_SC01_006` → `56760dbe…`;
|
||
`ov_setters.h`'s three groups become twin-covered (the header goes at T4). Then SC03_118/119 and SC02_000/003 (carves equal), then
|
||
SC04_018/019 and SC03_014/015 (adopt the primary's carve for the twin; R60: `interleave_check` + `pads_audit` on the twin). One commit
|
||
per pair; the clean fleet run after the last. Verify per pair: the twin's `make check` sha line; after all: `share_census` twin-covered
|
||
copies ≈ 7,900, same-vram backlog copies fall by that count, ov_SC03_015's 217 single-site macros become dead.
|
||
|
||
**T4 — The conversion: `tools/macro_to_header.py` applied fleet-wide; `engine_core.h` deleted (Max for the tool; xHigh to run).**
|
||
Order: `--plan`, `--dry-run` reviewed; `--apply --binaries ov_SC06_033` + L0 + `make check`, commit; the remaining binaries in ~8
|
||
batches of ~27, each L0-gated (`obj A/B: N/N byte-identical`); then delete `engine_core.h` / `ov_setters.h` / `clearTbl40.h`
|
||
(clearTbl40 rewritten as the parameterized control), move `func_80144B9C.h`, rewrite the 141 `<ov>_o0b.c` includes; the registry's
|
||
`source:`/`func:` rewritten surgically; L2. Verify, in order: `--verify` → `idempotent: 0 files would change`; L0 fleet `N/N`;
|
||
`check-all: 218 passed, 0 failed of 218`; `git grep -c '^#define DEFINE_func_' -- src` and `git grep -cP '^\s*DEFINE_func_' -- src`
|
||
empty; `tools/audit_text_sources.py` OK (every new include resolves in-repo); the fleet build wall time vs T0's baseline (the 8.8 MB
|
||
header no longer preprocessed per TU); `share_census`: 2,215 registered classes intact, the 1,083 dead listed, the fleet function
|
||
count +219 (ov_SC03_015's correction — published at T8 as a corrected undercount, never buried).
|
||
|
||
**T5 — `tools/share_body.py` + the same-vram backlog (Max for the tool; xHigh for the runs; batched largest reach first).** Bucket 0:
|
||
`--extend` the 206 never-extended classes (3,996 instances). Then the same-vram buckets: 32+ members same-text (1,899 classes / 4,234
|
||
copies / 249,554 ins) → 32+ differing-text (168 / 760 / 55,820) → 16–31 (1,033 / 2,231) → 8–15 (986 / 2,106) → the tiny classes
|
||
(no trivial exception). Per batch: `share_body --apply` → `banked N/M classes, K copies collapsed; J gate rejects` → `check-all: 218
|
||
passed` → commit. Differing-text bodies: one chosen text per the rule; type-carrying bodies: `lift_types` first, retry; what still
|
||
fails is ledgered with its message class (budget ~2–5% of sites). **Cross-vram classes are not touched** (listed by T1; published at
|
||
T8 as CROSS-VRAM-DEFERRED with their count and copies). Verify at task end: `share_census --check` → same-vram unregistered copies 0
|
||
(or = the ledger's count, each with a reason).
|
||
|
||
**T6 — Consumers, second half: freeze / retire / the guard (xHigh).** Drop macro-form support from the T2 set; FREEZE the 7
|
||
matching-era parsers (`family_sweep`, `gen_harvest_targets`, `p16_improve`, `recover_giant`, `restore_dropped_decls`,
|
||
`normalize_self_decls`, `o0_subsplit`) with one shared refusal in `main()` — never at import (`cdecl.audit_differential` imports
|
||
`gen_harvest_targets`; an import-time exit would take the health chain down) — dictionary status `FROZEN`, successor named; RETIRE
|
||
the 3 direct predecessors to `tools/sunset/` (`dedup_propagate`, `dedup_extend` → `share_body.py`; `macro_draft` → product: the
|
||
headers) after repointing their 9 callers (`gate_stage.py:522`, `bulk_harvest.py:267`, `auto_driver.py:140`, `lora_grind.py:230`,
|
||
`gate_lane.py:86`, `grinder.py:351`, the 3 importers); the guard in `tool_census.py --check`: every LIVE, non-FROZEN tool parsed
|
||
with `ast` — no non-docstring string constant contains `DEFINE_func_` or `engine_core.h` (prose mentions in comments/docstrings are
|
||
historical record and survive; 4,570 `DEFINE_func_` mentions in `src/` comments are deliberately not rewritten). Negative control
|
||
(R39): on the pre-T4 tree the guard flags exactly the 22 parsers and none of the comment-only tools; flipping one row to FROZEN
|
||
un-flags it. `make kit-corpus`, SETUP rows (R21). Verify: `tool_census --check` → `macro-form guard: 0 LIVE tools reference the
|
||
retired form (7 frozen, 3 retired)`; `make tools-health` OK.
|
||
|
||
**T7 — Wire the invariants + regenerate every published number (xHigh).** In `make tools-health` after `report BINARY=main`:
|
||
`share_census.py --check` (strict) + `--selftest`; `dedup_integrate --check` C2c/C2d. `progress.py`: two generated fields
|
||
(`unique_function_bodies`, `duplicate_source_copies`) in `docs/progress.json` and the README block (:1146-1166) sourced from the
|
||
census; `progress.py --readme --check`, `timeline.py`, `make audit-digest` (the +219 correction stated with its cause). Negative-
|
||
control the new gate: in a worktree revert one member's site to an inline copy → `share_census --check` exits 1 naming that class.
|
||
Verify: `make tools-health` OK; `progress.py --readme --check` fresh.
|
||
|
||
**T8 — The record (xHigh).** Wiki: `The-dedup-engine.md` rewritten as the shared-source model (headers, twins, the census, the
|
||
sotn fact as read), `Repository-layout.md`, `Where-the-project-goes-next.md`, `Verification-and-progress.md`; how-to ch.10 (the
|
||
Gen3 outcome; history kept); README:117 prose; `gen3-standards.md` §4 row + DoD line and `gen3-handoff.md` §2.2/§3 as dated
|
||
snapshots with the derivation; a cookbook section replacing §14/§38's macro narrative; `docs/decision-log.md` P35 (R31: the
|
||
2026-09-02 reversal with the sotn evidence, the 5,147-vs-3,516 miscount, the twin discovery, the +219 undercount);
|
||
`docs/accelerators.md`; DIGEST §4; `tools/sunset/README.md` rows; the memory `dedup-backlog-leave-it` rewritten; `doc_links --strict`,
|
||
`wiki_render --selftest`, `cookbook_index --check`, `kit_coverage`. Verify: doc_links 0 broken / 0 pending; the checks green.
|
||
|
||
**T9 — Close (Max, Tier 1).** R22 clean fleet run → 218/218; `make tools-health` OK (S1 strict + the guard); the metrics table
|
||
before/after (macro lines 5,147 → 0; `engine_core.h` 8.8 MB → deleted; same-vram duplicate copies → 0 + ledger N; twins 5 pairs /
|
||
10 aliases; cross-vram classes deferred N / copies M; registry groups 2,220 → ~6,500; fleet build wall time; the +219); the
|
||
reviewer sequence documented; `PhaseEnd_Phase35.md` + DIGEST §0/§2/§3 + the log archived (R19), left for Drew's close commit; v2.1.0.
|
||
Rules check (P6) after T3 and T7.
|
||
|
||
**Effort:** Max for T0's probe, T1, T2's design, T3's probe pair, T4's and T5's tool, T9; xHigh elsewhere; **no Ultracode** (the
|
||
parallelism is machine parallelism: L0 at JOBS=16 ≈ 13 min fleet-wide; the clean fleet run 3–5 min). **Sessions (R41, summed from the
|
||
task sizes above): T0–T1 ≈ 1 · T2 ≈ 1 · T3 ≈ 1 · T4 ≈ 1 · T5 ≈ 1–2 · T6–T7 ≈ 1 · T8–T9 ≈ 1 → ≈ 7–8 sessions;** the tail is the
|
||
differing-text and type-carrying bodies and the consumer edits, not the gates.
|
||
|
||
## Which gate proves which step
|
||
| step | gate | the line that banks it |
|
||
|---|---|---|
|
||
| one TU converted/shared | L0 object A/B (Probe P1) | `obj A/B: N/N byte-identical` |
|
||
| one binary | `make check BINARY=<b>` | `[ OK ] build/<b>/<b> … (BYTE-IDENTICAL)` |
|
||
| a batch / the phase | `make clean && make extract-all JOBS=16 && make check-all JOBS=16` (R22) | `check-all: 218 passed, 0 failed of 218` |
|
||
| the registry is honest | `tools/dedup_integrate.py --check` | `dedup-check: N validated, 0 failed` |
|
||
| the invariant holds | `tools/share_census.py --check` | `S1: one source per unique function — …, 0 unregistered, N excepted` |
|
||
| no live tool assumes the macro form | `tools/tool_census.py --check` | `macro-form guard: 0 LIVE tools reference the retired form` |
|
||
| everything | `make tools-health` | `tools-health: OK — …` |
|
||
|
||
## Milestone (gate 2 — what Drew confirms, each with its literal output)
|
||
1. `git grep -c '^#define DEFINE_func_' -- src` empty; no `DEFINE_func_X()` site; `src/shared/engine_core.h` absent; every registered
|
||
body a plain-C header under `src/shared/<space>/`.
|
||
2. `tools/share_census.py --check` exit 0: same-vram duplicate copies 0 (or each ledgered with a reason); the five twins built from one
|
||
source directory each; cross-vram classes published as a deferred count.
|
||
3. `make tools-health` OK with S1 strict and the macro-form guard; every consumer updated, frozen or retired per its dictionary row.
|
||
4. `make clean && make extract-all && make check-all` → `218 passed, 0 failed of 218` (R22).
|
||
5. README/wiki/kit regenerated (R75), decision log (R31), SETUP rows (R21), PhaseEnd + DIGEST written.
|
||
|
||
## Verification (the reviewer's sequence from a fresh clone, documented at T9)
|
||
```
|
||
make bootstrap
|
||
make clean && make extract-all JOBS=16 && make check-all JOBS=16 # 218 passed, 0 failed of 218
|
||
make tools-health # tools-health: OK — …
|
||
tools/share_census.py --selftest && tools/share_census.py --check # S1 … 0 unregistered, N excepted
|
||
tools/tool_census.py --check # macro-form guard: 0 LIVE …
|
||
tools/progress.py --readme --check # docs/progress.json + README block are fresh
|
||
git grep -c '^#define DEFINE_func_' -- src # (no output)
|
||
```
|
||
|
||
## Risks — settled by probe, or carried with its probe
|
||
Settled: include-vs-macro byte identity (P1, `.o`-level); the 4 divergent twins (last wins, uniform: copy 1 < 99,164 < copy 2);
|
||
`__LINE__`/`__FILE__` absent and no `-g`; the prelude's completeness (the 5,147 + 8 directive inventory); header-name collisions
|
||
(the stable suffix rule); `ld_interleave` never reads C. Carried: `.d`/file-count growth (measured at T0; fallback a `vram>>12` fan-out,
|
||
mechanical since paths are generated); a backlog site's TU rejecting the exemplar text (L0 localizes; ledgered); the registry text
|
||
edits (never `yaml.safe_dump`); the health-chain crash on deletion (T2 lands before T4); pins spread by a share (the exemplar rule
|
||
prefers pin-free; PINNED is a ledger class, never a spread).
|
||
|
||
## Rules at gate 1
|
||
**R96** (a) a scratch prune is an instrument change — re-run every tool that writes under it before calling the prune done.
|
||
**R97** (b) "green" is read from a check's EXIT CODE, never its last line; every chain sets `pipefail`.
|
||
**R98** (c) a step that hands a file to a third party is proven through that party's own toolchain on the file itself before the
|
||
owner's browser session, and the proving tool writes the paste.
|
||
**R99** (d) what a public tree carries is decided before the flip; a retired document gets its Archive-index row and is deleted in the
|
||
same commit — history keeps it.
|
||
Candidate for the PhaseEnd: "a shared body has exactly one source; a duplicate copy is a defect the health chain asserts, and a
|
||
count of them is published with its rule" (this phase's invariant).
|
||
**Candidates from S95 (the recovery):** (b) "every commit that advances a task — an intra-task bank included — carries its log line and
|
||
the 🛑 headline; a checkpoint older than the last commit is a dead session's checkpoint" (S94 refreshed the block at every TASK close but
|
||
made two T5 bank commits with no log line, then filled its context during a background wait; the successor rebuilt 35 minutes of state
|
||
from the transcript). (c) "a tool that restores files never uses `git checkout` on a tree it did not commit — it restores from its own
|
||
snapshot" (share_body's bisect wiped the previous batch's uncommitted shares; R42 for tools). (d) "a failure-cause extractor is
|
||
negative-controlled against the compiler's real message forms, not against the word error" (gcc 2.7.2 prints errors without it;
|
||
254 of 303 rejection lines read `Error 33`).
|
||
|
||
## 🛑 SESSION CHECKPOINT — S96 close (2026-09-08): Phase 35 COMPLETE — T0–T9 ☑ (+ T5b); PhaseEnd_Phase35.md written, this log archived; NEXT = Drew's close commit, then Phase 36 (pins) in a fresh session at Max, plan mode
|
||
|
||
### 0. How to use this block
|
||
A fresh session (S97) reads CLAUDE.md's load order, replays THIS block verbatim, asks Drew for `/effort max` (T9 is Tier 1: the PhaseEnd
|
||
synthesis), and executes §2. Every task's literal verify lines are in the log above (append-only, one entry per step); the numbers below
|
||
are copied from those entries — re-derive any you publish (R75). The tree is CLEAN at **HEAD `ce837b1e4`** ("T8 — the record"); Drew
|
||
pushes (R6). The context guard (`~/.claude/ctx_guard.sh`, PostToolUse, user settings) fired at 90 % and this block was written at once.
|
||
|
||
### 1. Where things stand
|
||
- **Done, committed, gated:** T0 open/probe · T1 `share_census` · T2 the health chain learns the include + twin forms · T3 five twin pairs
|
||
→ one source dir · T4 every macro body a header, `engine_core.h` deleted · T5 `share_body` (bucket 0: 183 classes → 135 shared / 48
|
||
ledgered; bucket new: 915 → 914 shared / 3 ledgered; the E_func_80168B70 re-exemplar) · T5b the `h_text` tier (38 classes / 2,030
|
||
sites) · T6 14 tools FROZEN, 4 retired, the macro-form guard · T7 S1 strict + C2c/C2d in tools-health, the digest fields and
|
||
corrections · T8 the record. Sessions: S94 (T0–T4 and the start of T5; died at 91 % without a checkpoint), S95 (the recovery, no runs),
|
||
S96 (T5 → T8). Commits since the open `48170fd7f`: `git log --oneline 48170fd7f..HEAD` (≈50).
|
||
- **The last gates on this tree:** R22 `check-all: 218 passed, 0 failed of 218` from clean at `ac8c29edb` (`.run/P35/baseline/r22_t5b.log`,
|
||
90 s; T6–T8 after it changed tools and docs only — T9 re-runs it anyway); `make tools-health: OK` at `ac8c29edb`
|
||
(`tools_health_t5b.log`, 439 s) with `S1: one source per unique function — 10,180 classes, 10,180 satisfied (3,507 twin-covered, 51
|
||
excepted, 3,801 deferred cross-address), 0 VIOLATION(S) — OK`, `dedup-check: 3173 validated, 0 failed | C1 coverage 262573/262573`,
|
||
`macro-form guard: 0 LIVE tools reference the retired form (217 scanned, 14 frozen, 6 whitelisted detectors, 38 retired)`; after T8,
|
||
`doc_links` strict 0 broken, `wiki_render` 32/32, cookbook index 1,170 sections OK, `kit_coverage: OK`, `tool_census --check: OK`.
|
||
- **Open by design (recorded, not owed):** 51 ledgered classes / 160 copies in `config/dedup_exceptions.tsv` (50 TU-CONFLICT — the late
|
||
overlays' declaration conflicts, the types phase's; 1 GATE-REJECT `E_func_801376E8`); 3,801 cross-address classes deferred to the names
|
||
phase (E flag 3,826), including 381 same-address tiny-body texts / 1,668 copies inside them (the parameterized `SHARED_FN` form is
|
||
their shape). The Ghidra MCP: launched by the hook, unused all phase — stop it via `tools/ghidra_mcp_stop.sh` before the close commit
|
||
(R23). Disk ≈ 30 GB free.
|
||
|
||
### 2. T9 — the close (Max; one session; leave the deliverables UNCOMMITTED for Drew's milestone-close commit, R6)
|
||
1. `/effort max` confirmed. `make clean && make extract-all JOBS=16 && make check-all JOBS=16` → `check-all: 218 passed, 0 failed of 218`
|
||
by exit code (`.run/P35/baseline/r22_t9.log`); `make tools-health` in the FOREGROUND (≤ 600 s; ~440–550 s) → `tools-health: OK` with the
|
||
S1 line, dedup-check 3173/0, the guard line (`tools_health_t9.log`).
|
||
2. **The metrics table (before → after)** for the PhaseEnd, each cell from its command: macro define lines 5,147 → 0 (`git grep -c
|
||
'^#define DEFINE_func_' -- src` empty; `git grep -cP '^\s*DEFINE_func_' -- src` empty); `src/shared/engine_core.h` 8.8 MB / 227,730 lines
|
||
→ deleted; shared headers 2,215 (T4) → 3,175 (`find src/shared -name 'func_*.h' | wc -l`); registry groups 2,220 → 3,173 (38 `h_text`),
|
||
instances 255,708 → 262,573; same-address backlog 1,099 classes / 4,755 private copies / 3,658 collapsible → 51 / 160 / 109, all
|
||
ledgered; S1 10,180 / 10,180, 0 violations; twins 5 pairs / 10 aliases / 166 files deleted; cross-address deferred 3,801 classes;
|
||
`unique_function_bodies` 103,015; the fleet denominator 363,221 → 363,680 (+459, cause in the T6 part-2 entry) and REAL 360,744 →
|
||
350,533 (empty-bodied shared functions now EMPTY); instruction-weighted metrics unchanged (13,492,113 / 5,820,205 / 45,150); fleet
|
||
clean-run wall 157 s (T0) → 84–90 s; 14 tools FROZEN, 4 retired; `docs/progress.json` `corrections` (3 entries).
|
||
3. **The milestone (gate 2), item by item with literal output:** (1) `git grep -c '^#define DEFINE_func_' -- src` empty, no
|
||
`DEFINE_func_X()` site, `engine_core.h` absent, every registered body a header under `src/shared/<space>/` (`dedup_integrate --check`
|
||
C2c); (2) `tools/share_census.py --check --strict-macros --strict-text` exit 0 — same-vram copies 0 or ledgered, the five twins from one
|
||
directory (`make audit-binaries` CHECK 3b), the cross-vram count published; (3) `make tools-health` OK with S1 strict and the guard;
|
||
every consumer updated/frozen/retired per its dictionary row (`config/tool_dictionary.tsv`); (4) R22 218/218; (5) README/wiki/kit
|
||
regenerated (T7/T8), decision log (T8), SETUP rows (T1–T7), PhaseEnd + DIGEST written (this task); v2.1.0.
|
||
4. **The reviewer sequence** (document it in the PhaseEnd's Build Log): `make bootstrap` · `make clean && make extract-all JOBS=16 && make
|
||
check-all JOBS=16` · `make tools-health` · `tools/share_census.py --selftest && tools/share_census.py --check --strict-macros
|
||
--strict-text` · `tools/tool_census.py --check` · `tools/progress.py --readme --check` · `git grep -c '^#define DEFINE_func_' -- src`.
|
||
5. **Write `phase-ends/PhaseEnd_Phase35.md`** (the format in PROJECT_CONTEXT.md; the P33/P34 files are the exemplars; R25 plain-English
|
||
recap; "what we believed / what failed / what we'd do sooner"; the sessions' cost in agents: none — no subagents this phase; the
|
||
S94 death and the S95 recovery as a Deviation; the plan's "7 frozen" → 14 and "3 retired" → 4; T5b added; the E_func_80168B70 and
|
||
the 38-text decisions delegated to Claude at Max). **Rule candidates for Phase 36 gate 1 (P10), from §"Rules at gate 1" + the log:**
|
||
(a) a shared body has exactly one source; a duplicate copy is a defect the health chain asserts and its count is published with its
|
||
rule (the phase's invariant); (b) every commit that advances a task carries its log line and the 🛑 headline; a checkpoint older than
|
||
HEAD is a dead session's checkpoint; (c) a tool that restores files restores from its own snapshot, never `git checkout` on a tree it
|
||
did not commit; (d) a failure-cause extractor is negative-controlled against the compiler's real message forms, not the word "error";
|
||
(e) build the disagreeing oracle (R34) BEFORE the batch runs and measure its disagreement before believing either side — the text
|
||
oracle found in one run what the byte join could never class; (f) a registry lists a member only after the gate has spoken for it
|
||
(never ahead of the source); (g) a policy taken on a remembered precedent is a belief — read the target project's tree.
|
||
6. **DIGEST:** append the P35 synopsis to §2, the ratified R96–R99 are already in §3 (T0), refresh §0's "where the project stands" (Phase 35
|
||
closed, v2.1.0, Gen3's next = Phase 36 pins per Drew's order dedup → pins → structs → names), §4 already has the P35 map (T8).
|
||
7. `git mv phase-ends/CURRENT_PHASE.md phase-ends/logs/Phase35.md` (R19); `make kit-corpus` so the kit's record copies (the DIGEST, the new
|
||
PhaseEnd, the log) stay equal; leave PhaseEnd + DIGEST + the moved log + the kit corpus UNCOMMITTED with the commit message written in
|
||
the PhaseEnd; stop the Ghidra MCP; final message per P8; HARD STOP — no Phase 36 preview.
|
||
|
||
### 3. Numbers to re-derive, never trust
|
||
Every figure in §2 step 2 has its command there; the S1 line and the registry counts come from `share_census --check` /
|
||
`dedup_integrate --check`; the digest from `tools/progress.py --fleet`; the header count from `find`; the frozen/retired counts from
|
||
`config/tool_dictionary.tsv` (`grep -cP '\tFROZEN\t'`, the sunset README rows).
|
||
|
||
### 4. Gotchas that cost something this phase (the log has each with its verify line)
|
||
1. `share_body.py` runs on a COMMITTED tree, one batch per invocation; its bisect restores from an in-memory snapshot (never `git checkout`).
|
||
2. gcc 2.7.2 error lines carry no "error" token; `Error 33` is cc1's fatal exit — read the `file:line: message` form.
|
||
3. A twin's instance resolves to its primary's TU — dedupe edits by (tu, line); the census counts distinct TUs.
|
||
4. `add_members_surgical` refuses the verbose `members:` form; `--repair-registry` converts and completes; matches on (binary, vram).
|
||
5. `make kit-corpus` belongs in every commit that edits a tool, the DIGEST, a PhaseEnd, the cookbook or the how-to; the kit's lint refuses
|
||
rule ids (`R42`) and project names in authored kit text.
|
||
6. The timeline's last row follows the COMMITTED digest: commit `docs/progress.json` first, regenerate the timeline in the next commit.
|
||
7. `make tools-health` in the foreground (~440–550 s); `share_census` cache is keyed by mtime/size — `--no-cache` after a scanner edit.
|
||
8. A `grep` guard in a `&&` chain exits 1 on zero matches (twice this session); `;` before `git commit` let a partial commit through once (R97).
|