Commit Graph

7 Commits

Author SHA1 Message Date
Drew T 1576570271 fix(phase-30 S1e): the distinct-code "regression" was a STALE DIGEST — alias lever ungated
The S38 checkpoint gated the phase's best lever ("do NOT scale the alias lever") on
distinct-code falling 89.3 -> 89.2. It never fell.

PROOF (each commit's metric recomputed from its OWN committed tree, 0 unresolved):
  commit:1426 TRUE     : instr 12394533  distinct 5022306  (77895 uniq)
  commit:1426 COMMITTED: instr 12402412  distinct 5029324  (78025 uniq)   <- stale
  HEAD TRUE == COMMITTED: instr 12405402  distinct 5025082  (77952 uniq)
  => true delta 843->HEAD: instr +10869, distinct +2776 ins / +57 uniq. ALL ROSE.
The 843 digest was generated from a working tree still holding work REVERTED before the
commit landed (+7,879 ins / +130 uniq overstated) and never regenerated, so the next
HONEST digest read as a fall. => THE ALIAS LEVER IS UNGATED (scale it, §61 small batches).

Both recorded leads were wrong (R14): progress.py:423's SIG regex feeds fn-count ONLY
(neither weighted metric sees a C identifier — both derive matched = sig - corpus.stubs),
and "the harvest reverted functions to INCLUDE_ASM" died on one grep (483 removed, 0 added).
The 3-grep proof: identical sigs + unchanged tools/ + zero +INCLUDE_ASM => HEAD's stub set
is a strict subset => both numerators are FORBIDDEN to fall.

THREE INSTRUMENT DEFECTS, all one class (a bare except around a fail-CLOSED oracle):
- progress.py stub_addrs wrapped corpus.stubs in `except Exception: return set()`. An empty
  stub set means "could not answer", not "no stubs", so matched = sig - stubs credited EVERY
  function. Byte-witnessed: instr 100.00% / distinct 100.00% in a tree with no asm/. Now
  propagates.
- cast_call_sites.tu_for + reconcile_tu.tu_for had the identical swallow, falling back to the
  default <ov>.c instead of the jr/-O0 split TU — silently reinstating the exact bug
  cast_call_sites' own docstring says it exists to fix. A wrong-TU reconcile fails the gate,
  and this phase's base rate is ~24k PLUMBING vs 4,917 DIFF, so it presents as a codegen wall.
  Now propagate CorpusError; ValueError fallback for curated names preserved; derived-TU path
  re-verified (a _jr_ split stub resolves correctly, both tools agree).

NEW GATE (R34 — the byte-gate is a null oracle for DOCUMENTS; check-all stays 140/140 over a
stale digest forever): tools/audit_digest.py + `make audit-digest`, wired into tools-health
after report. Recomputes the three headline metrics from the current tree and fails if the
committed digest disagrees. Compares INTEGERS, not percentages — the +7,879-instruction
staleness printed as "94.4%" on both sides. Negative-control-proven against the stale 843
digest (fails, exit 1) and green on HEAD.

Verified: make report exit 0 (dedup-check 1910 validated / 0 failed, C1 coverage
241216/241216); audit-digest OK; cookbook-index OK (398 sections); metrics unchanged by the
fix (94.40% / 89.18%). No src/ or config/ edits — no bytes touched, nothing banked.

cookbook §140 · decision-log 2026-08-04 · SETUP.md inventory (R21) · R14/R32/R34/R35.
2026-08-04 21:48:57 -06:00
Drew T 599a33c056 feat(phase-29): wave22 — 5 exemplars banked from an 18-target Ultracode wave (R22 140/140)
THE WAVE: 18 h_seq family exemplars (~255k templated instructions), one agent each, drafting from
cached Ghidra-C + the target .s with canonical callee/data decls resolved from the real TU scope.
Result 12 MATCH / 6 NEAR / 0 FAIL (2.59M subagent tokens). No agent touched the tree — the
draft-only constraint held (verified: git status clean across src/config/tools/include).

BANKED 5: func_80148E54, func_80171B4C, func_8014A738, func_8012A328, func_80163534.
R22 clean-fleet 140 passed / 0 failed of 140.

A BUG I INTRODUCED EARLIER TODAY, FOUND BY WORKING THE 12->5 GAP. My block-scope descent in
reconcile_tu fed ordinary STATEMENTS to cdecl.parse; some parse without raising into a declarator
with an EMPTY base type and the statement's symbol as its name. That fake row overwrote the genuine
plan entry for the same symbol, so the span rewrite landed on a statement instead of the declaration
— and my own R32 completion assertion still PASSED, because the conformed text appeared somewhere.
Byte-witnessed on D_80126B5C: planned twice ("draft 's32'" and "draft ''"), output unchanged, gate
PLUMBING. Now block-scope rows are accepted only from a real `extern` with a non-empty base type.

TWO BANKS CAME FROM TODAY'S OWN FINDINGS:
- func_8012E014's single 0-arg call site took --cast-zero-arg-calls (built this morning for
  func_801789AC's 138 sites).
- §99 HELD A THIRD TIME: K&R conversion dissolved func_80163534's s32->u16 narrowing across 1,072
  declarations, leaving only a caller-neutral pointer change on the last param.

STILL UNBANKED (measured blockers, not guesses): func_8013B6A0 + func_8013B598 CC1-FAIL in the _o0
split; func_80133298 + func_80135260 + func_8012E014 genuine DIFF (match_one MATCH did not hold
whole-binary = TU-context); func_80138C60 parse-order (an extern referencing a body-local typedef
declared after it); func_80177DA8 prototype-vs-K&R mismatch.
2026-07-28 00:16:08 -06:00
Drew T 5f1fc5b5eb feat(phase-29): caller pair banked (57,822 templ ins) via K&R defs — §92's remedy corrected (§99)
func_80175AB8 + func_80175DA8 both banked. R22 clean-fleet 140 passed / 0 failed of 140.

§92 SAID these need "the §17a-1 caller pair, NOT a bare conform" — the diagnosis was right (conforming
a narrow param changes argument promotion at every call site, measured PLUMBING -> DIFF) but the
remedy was the expensive one. The actual fix touches NO declaration: convert the DEFINITION to K&R,
where a narrow param PROMOTES to int (C89 6.3.2.2) and is therefore already compatible with the
fleet's existing `s32` prototype, while still emitting narrow-param codegen. §43 applied to the def
side. T0 draft-only, ZERO blast radius, versus a 524-site fleet conform.

THREE reconcile_tu BUGS SURFACED, ONE OF THEM MINE:
(a) BLIND TO BLOCK SCOPE. split_statements is depth-0 BY DESIGN, and §8d deliberately demotes data
    externs into the function body — so the tool saw one statement and no declarations, printing
    "reconciled: 0 draft(s), 0 data symbol(s); coverage defects: 0" for a draft cc1 rejected with
    `conflicting types for D_8011F7BC`. A silent skip (R32). Fixed: descend one level.
(b) MY BUG, introduced by (a): descending into ANY `{` also enters struct/union/enum definitions, so
    MEMBERS parse as declarations and get conformed — `u32 code;` became the TU's
    `typedef void (*code)(unsigned short*);` INSIDE the struct, and `p->code` became
    `p->(*(u32 *)&code)`. Caught by DIFFING THE TOOL'S OUTPUT AGAINST ITS INPUT before trusting it;
    the byte-gate would have said PLUMBING and explained nothing. Guard: function bodies only.
(c) LATENT since the tool was written: _cast_sub matched bare identifiers and rewrote MEMBER ACCESSES
    as globals. Unreachable until (a) existed. Guard: (?<![.\w])(?<!->).

cookbook §99.
2026-07-27 21:27:24 -06:00
Drew T 8c36ede849 feat(phase-29): func_80176218 banked (45,126 templ ins) + reconcile_tu span/R32 fix
THE DRAFT was failing in a CHAIN, one "next conflict" per gate cycle. Applied §95's own diagnostic
law instead — splice once, dump EVERY cc1 error — and the whole set named the cause immediately:
three errors on TWO axes (one data decl, two callee decls), not three problems.

THE DATA ERROR WAS reconcile_tu AGAIN, ONE SHAPE DOWN (§96). split_statements returns comment-
STRIPPED text WITH SPANS; the rewrite re-found each planned statement by comparing that text to a raw
LINE, so `extern u8  D_80078E78;   /* cur base ($s5) */` never matched. The decl was left unconformed
WHILE THE USE-CAST PASS STILL FIRED -> a draft whose uses are cast for the TU's storage against the
draft's own declaration -> cc1 reports `conflicting types` AT THE VERY DECL THE TOOL JUST CLAIMED TO
FIX, exit 0, "reconciled: 3 symbols".

FIX: rewrite by SPAN (the primitive existed — its docstring says spans are preserved *because drafts
get rewritten*). Plus the R32 assertion the old code was missing: it had a dropped_check counter
incremented in two places and NEVER COMPARED — "a loud failure nobody counts is exactly as invisible
as a silent one" in miniature. Now declarators-in vs -out AND a per-symbol check that each planned
tu.declaration() actually landed, both as `!!` notes so --strict exits non-zero.
MEASURED: 3 -> 4 data symbols reconciled on the same draft; trailing comments preserved (H5).

THE TWO CALLEE CONFLICTS were the other axis (reconcile_tu skips kind=='func' by construction):
cast_call_sites (§20) conformed func_80177AD4 (TU `void (int, unsigned int)`) and func_80178298
(TU `(u32*, u8*, short, short)`) and cast each call site to the draft's intended widths.

GATE: verified 1 / failed 0, d19c9580 BYTE-IDENTICAL. Write set is one overlay-local TU = T1 per the
§63/§85 blast-radius taxonomy, so the per-binary gate is sufficient; the ×137 sweep is the T2 case
and takes a full R22.
2026-07-27 16:55:02 -06:00
Drew T db620d4b8d fix(phase-29): reconcile_tu dropped the sibling declarators of a multi-symbol extern line
THE DEFECT (on the banking path — gate_stage runs reconcile_tu): its rewrite replaced the draft's
declaration LINE with the TU's declaration of the ONE conflicting symbol. A statement can declare
several: 'extern u16 D_80078EB2, D_8011F82A, D_8011F82C, D_80078EB4, D_8011F8C4;' where only EB4
conflicts became 'extern s16 D_80078EB4;' — four symbols silently gone.

WHY IT HID: the draft does not fail at the declaration. It fails later with 'D_8011F82A undeclared'
at a USE, several conflicts down a peeling chain, nowhere near the cause. I peeled four separate
'next conflicts' out of func_80176218 before dumping ALL cc1 errors in ONE build and seeing three
undeclared symbols that the tool itself had removed.

FIX: group the plan by STATEMENT rather than by symbol; re-emit EVERY declarator (TU's version for
the conflicting ones, the draft's own for the rest); note multi-declarator statements; and when a
statement cannot be re-parsed, say so loudly instead of emitting only the planned symbols.
VERIFIED: all 5 declarators survive, and the same draft now reconciles 3 symbols instead of 2 —
the dropped ones had been hiding a further conflict.

cookbook §95. The law (R32 again): a transform that REPLACES a syntactic unit must account for
everything that unit contained — the STATEMENT, not the line, is the unit of a C declaration.
Diagnostic: when a draft fails in a chain, stop peeling one error per gate cycle; splice once and
dump every cc1 error, because the shape of the whole set names the cause.
2026-07-27 16:33:52 -06:00
Drew T 4aae5e7589 fix(phase-26a): A3d — retire the fleet-majority oracle: it was WRONG for the TU 16% of the time, on both banking paths
R33 applied to the worst finding in the audit: this oracle was not fixed, it was RETIRED.

    reconcile_decls asks "what does the FLEET call this symbol?"
    C asks           "what does THIS TRANSLATION UNIT declare?"

The engine is loosely typed -- the same address is legitimately declared with incompatible types in
different overlays -- so a single fleet-wide answer is WRONG FOR SOME TU BY CONSTRUCTION. And it is
worse than a silent skip: it writes an ACTIVELY WRONG declaration into the draft, which then
collides with the very TU it was meant to conform to.

MEASURED across ov_SC01_077's 12 TUs, against what cpp says each TU really declares:

    the fleet oracle AGREES with the TU ................ 2883
    the fleet oracle CONFLICTS with it (cc1 REJECTS)  ..  548    <- 16%
    the TU declares it, the oracle has NO answer ......   357

and it was LIVE ON BOTH BANKING PATHS:
  * gate_stage      -- rewrote 60 of 196 drafts in the current batch
  * jtbl_family_bank -- EVERY SIBLING of the ×134 family sweep, the project's economic engine.
    A poisoned decl means that sibling silently does not bank, and the loss is invisible: the sweep
    simply reports a smaller number. The irony is exact -- that function's own docstring already
    knew the conflicting symbols are PER-OVERLAY, which is precisely why a FLEET oracle could never
    have been right.

reconcile_tu.py (written in Phase 26 but NEVER WIRED) now supersedes it, rebuilt on cdecl:
  * ask cpp what the TU declares (macro-injected DEFINE_func_* externs included -- a raw scan
    cannot see them, §8c / §51g LAW 7);
  * ask cc1 whether the draft's decl can coexist (cdecl.compatible, validated against the real
    gcc-2.7.2 front end on 1,485 live pairs -- NOT the C standard, NOT modern gcc; §51g LAW 9);
  * NOT declared -> leave the draft alone (its extern types are load-bearing: %lo-folding, access
    width, alignment); compatible -> nothing; CONFLICTING -> the TU wins + cast at every USE so the
    draft's intended access survives byte-for-byte;
  * derives WHICH TU from corpus.stubs() rather than a hand-passed --src-file (§51g LAW 10).
  * handles the fn-ptr kind NATIVELY -- which is why it supersedes rather than patches: teaching
    reconcile_decls' parser to see `extern void (*D_x[])(void);` would have ARMED its fn-ptr-blind
    data_access_subs to rewrite a call-through `D_x[i]()` into `((u8 *)D_x)[i]()`. Fixing the regex
    would have detonated a dormant bug.

AND THE NULL RESULT, AGAIN, REPORTED AS SUCH (P9/R14): on the 196 never-banked historical drafts the
new oracle banks EXACTLY AS MANY AS THE OLD ONE -- zero. That tail fails on CODEGEN, not on decl
plumbing. The two disagree on 45 of 196 drafts and the outcome does not move. This is a CORRECTNESS
fix (548 wrong declarations removed from two live pipelines, protecting all FUTURE drafts and every
future family sweep), not a banking win, and it is not being sold as one. Three nulls in one session.

reconcile_decls.py is kept as EVIDENCE, marked RETIRED, with no live caller.

  R22 clean-fleet: make clean + extract-all + check-all -> 136 passed, 0 failed of 136
  src/ untouched (0 changes)   reconcile_tu: 0 coverage defects over 196 drafts
  NOTE: the family-sweep path gets its real exercise at Task 8 -- watch the per-sibling bank rate.
2026-07-14 12:53:14 -06:00
Drew T 5b1de7acaa feat(phase-26): tools/reconcile_tu.py — ask "what can THIS TU see", not "what does the fleet call it"
WRITTEN + VALIDATED, DELIBERATELY NOT WIRED IN (inert; nothing imports it). Wiring + byte-gating is the
first item of the integration fix pass, AFTER the tooling-integrity audit Drew gated it behind.

The successor to reconcile_decls.py for the templating/banking path. Two things are wrong with that tool,
and the second is structural, not a typo (Phase-26 scanner audit):

1. BLIND TO FUNCTION POINTERS. DATA_DECL_LINE_RE wants `extern <type-words> D_x[];`, so the `(` in
       extern void (*D_801DA75C)(void);        <- fn-ptr scalar
       extern void (*D_801812A4[])(void *);    <- fn-ptr array (a dispatch table)
   breaks its type run; the line never matches; the tool SILENTLY SKIPS exactly the symbols that are
   failing and reports success. Blocking func_8017A4AC (536 ins x134 = 287 KB) today.

2. ITS ORACLE ASKS THE WRONG QUESTION. It elects a canonical decl by FLEET MAJORITY. But 34.4% of fleet
   symbols carry >=2 mutually incompatible spellings, so a single fleet-wide answer is PROVABLY WRONG FOR
   SOME TU BY CONSTRUCTION — and it is worse than a skip: it returns an ACTIVELY WRONG decl (measured:
   3,717 symbols) that then collides with the very macro it was meant to conform to.

The only question that matters is what gcc compares the draft against: WHAT CAN THIS TU SEE. So reconcile_tu
reconstructs the TU's visible file-scope decl environment from BOTH §8c sources — col-0 decls AND the externs
INJECTED BY engine_core.h MACRO INVOCATIONS (a DEFINE_func_*() expands at file scope, so its leading externs
are genuine file-scope decls of the invoking TU, invisible to any col-0 scan: 544 visible syms from 1801
macros) — then conforms the draft's decl to it and CASTS AT EVERY USE (gcc folds a compile-time cast of a
known symbol, so the emitted bytes are unchanged; the whole-binary byte-gate remains the sole arbiter).

Carries a COVERAGE ASSERTION (R32): every line that LOOKS like an extern of a D_ symbol must parse, or it is
reported LOUDLY (--strict exits non-zero). A silent skip is a defect, not a no-op.

Validated read-only on func_8017A4AC: resolves D_801DA75C (fn-ptr -> `extern s32` + call-site cast),
D_80126B58 (struct), D_801DA734 (ptr).
2026-07-14 02:10:08 -06:00