Commit Graph

13 Commits

Author SHA1 Message Date
Drew T bb34eac42f fix(phase-31): S54 — four measured over-refusals in the overlay pre-gate path
Each one refused byte-verified work; each fix is probed, not reasoned:
* built-in redeclaration: a cc1 probe shows two conflicting "memcpy" declarations give
  "warning: conflicting types for built-in function" + exit 0, while the same pair on a
  non-builtin name errors. Every overlay TU in the fleet declares memcpy twice and compiles
  today -> CONFLICTING-EXTERN on a builtin is now WARN.
* driver mismatch: overlays bank via gate_lane -> gate_stage -> harvest_verify, which strips
  every typedef the target TU provides; pregate_check modelled gate_main's hoist/strip instead
  and reported DUPLICATE-TYPEDEF for exactly the duplicates the real gate removes.
  substitute() now takes an optional per-draft transform; pregate passes the overlay one.
* block-scope typedefs: two functions may each declare their own typedef inside their bodies
  (that is how a draft stays self-contained for match_one). _typedefs now honours the brace
  depth map the caller already computed.
* project scalar aliases: include/common.h's "typedef s32 M2C_UNK;" makes "extern s32 D_x" and
  "extern M2C_UNK D_x" the same declaration; _ALIASES now DERIVES those from common.h (R33).

Measured on the 5 leftover slates: 28 drafts, all re-verified MATCH by match_one, went from
"0 kept / phantom FAILs" to main 2 clean, ov_SC04_011 15 clean, ov_SC03_028 1 clean,
ov_SC06_029 4 + 1 named TU edit, ov_SC02_005 2 real TYPEDEF-USED-ABOVE-DEFINITION.
2026-08-17 11:20:32 -06:00
Drew T 85671bc1f7 feat(phase-31): S54 — wave T selector (--one-per-gid, --rank total) + the pre-gate ladder learns to see overlays (§192)
build_wave_atlas: --one-per-gid collapses same-skeleton siblings to one card and defers
them to <out>.siblings.json for the post-bank family_sweep remap (R32 accounting asserted);
--rank total ranks gate groups by DELIVERED mass (card + deferred siblings). Measured on the
wave-T draw: 6,557 drafted ins carrying 12,709 sibling ins behind 69 of 71 gids = 19,266
instructions of potential for 71 agents, vs 9,985 behind 57 under --rank mass. R39 NC: the
flag is byte-inert on a pool whose gids are unique.

gate_main/pregate_check (§192): three defects that made the pre-gate ladder main-only while
reporting "clean" on overlay slates — (1) resolve_conflicts/substitute hardcoded
corpus.stubs('main') -> per-binary _stubs_for(); (2) sym_of returned the keyword `void` for
every `extern void (*D_x[])(...)`, manufacturing 192 phantom CONFLICTING-EXTERNs (NC over
5,526,100 declarations: 189,301 changed verdicts, 0 regressions); (3) `void f()` and
`void f(void)` were normalized together, costing 40 more phantoms — C89's unspecified-
parameter rule is now gate_main.sig_conflict. §192b: the tool refuses when it substituted 0
files, and prints the per-draft [DROP] reasons it used to compute and discard.

Same overlay slate now reports 2 failures, both real (duplicate typedef; memcpy declared two
ways). Cookbook §192/§192b + index regenerated (585 sections).
2026-08-17 11:00:14 -06:00
Drew T 6744fac650 feat(phase-31): gate_main strips a draft's FORWARD typedef instead of renaming it (§184b) 2026-08-17 00:07:11 -06:00
Drew T a6e522a96a feat(phase-31): typedef comparison ignores comments (gate_main + reconcile_slate); func_8001ABBC + func_80037028 banked via byte-neutral TU edits 2026-08-16 22:49:05 -06:00
Drew T 1ebca23c87 feat(phase-31): gate_main hoists TU typedefs above the include block (§181 class 2); +3 recovered drafts banked 2026-08-16 22:20:56 -06:00
Drew T 207559daad feat(phase-31): tools/pregate_check.py — validate a main slate in 0.7s instead of a 5-min rebuild
Wave P drafted at 97% and cost A DOZEN clean rebuilds to bank, and not one of those rebuilds
failed on a matching problem -- every one failed on a TEXTUAL property of the substituted file
that a grep could have reported instantly. This is that grep.

gate_main's resolve_conflicts cannot answer it, and not from carelessness: it inspects the DRAFTS
while the compiler sees the FILE THEY LAND IN -- after typedef stripping and renaming, at each
draft's own insertion offset, interleaved with declarations the file already had. Those
transformations run AFTER the conflict check passes. So substitute() gained write=False and this
tool checks the artifact itself.

Five checks, each earned by a rebuild lost this session (§176h): typedef used above its
definition; type never defined anywhere; duplicate typedef with different bodies; one symbol
declared two incompatible ways; definition contradicting a visible prototype.

CALIBRATED AGAINST THE COMPILER, NOT AGAINST C89 PEDANTRY -- and this mattered. The first version
reported 4 hard FAILUREs on the slate that had just built BYTE-IDENTICAL:
- it ignored SCOPE, but the project deliberately uses block-scope extern blocks, and a declaration
  inside one function cannot conflict with a definition elsewhere. Now brace-depth aware.
- it split `void f()` from `void f(void)`, which gcc-2.7.2 accepts. Normalized.
- it called every def-vs-decl mismatch fatal, but gcc-2.7.2 accepted `void f(void*,s32)` against a
  `void f(s8*,s32)` definition and even `G3P *f(...)` against `G4P *f(...)`. What it REJECTED was
  a void/non-void RETURN split (func_8001ABBC). That split alone is FAIL; the rest are WARN.
Comments are masked via cdecl before any use-site scan (an unmasked scan reported 7 phantom hits).

R39 controls: the slate that banked is FAIL-free (exit 0, 3 informative warnings); four synthetic
defects each reported at FAIL; a clean text reports nothing; a block-scope extern does not
conflict; `short` vs `s16` does not conflict; array-vs-scalar does.
2026-08-15 22:03:42 -06:00
Drew T ad2aa6173f feat(phase-31): wave P — 32 main functions banked, main byte-identical (S52)
Wave P was the first full run of the 6k-ins doctrine: 60 cards / 6,589 ins in 2 gate groups,
59/60 claimed and 58/60 independently re-verified MATCH (6,372 ins), reloc_identity 58/58 AGREE
with ZERO symbol errors -- the second consecutive clean wave on symbol identity.

Banking cost a dozen rebuilds and exposed four more gate_main defects plus three regressions of
my own. The tool fixes, all NC'd:
- resolve_conflicts never read a draft's OWN DEFINITION, so the DEF-side wall (a draft defining
  s32 func_X against a TU prototyping void func_X) reached the compiler. Now definition-aware:
  it caught 13 conflicts up front where the build had been finding them one rebuild at a time.
- DECL and both typedef patterns anchored on end-of-line, so a TRAILING COMMENT hid a declaration
  or typedef entirely -- and agents comment nearly everything they declare. Seventh instance of
  one root cause: a scanner that looks green while reading less than it claims (R32).
- typedef handling is now BODY-AWARE and POSITION-AWARE, in a single pass:
    * identical definition visible ABOVE the insertion point -> strip and reuse;
    * same name, different shape -> rename (private to the draft);
    * definition below the insertion point -> never reuse (it is not in scope there).
  Three wrong strategies preceded this, each costing a rebuild: blanket strip (the file's copy can
  sit BELOW the draft -> implicit-int, then a collision), blanket rename (breaks drafts that share
  an IDENTICAL typedef, because their externs stop agreeing -- my regression, three drafts at
  once), and a rescan loop that found the definition it had just renamed and stripped it as a
  self-duplicate -> 'parse error before *'.

KNOWN LIMIT, recorded not fixed: the conflict check compares spelled type NAMES, so three drafts
each defining their own Slot54 with different layouts all declared func_80032A74(Slot54*) and
looked compatible. Comparing struct LAYOUTS for locally-defined types is the real fix.

13 + 4 verified-correct drafts are parked in two named buckets (competing local type models;
immovable TU declarations that gate_main reverts before every build).
2026-08-15 21:55:38 -06:00
Drew T 110570e3fc feat(phase-31): wave O — 46 main functions banked, main EXE byte-identical (S52)
36 fresh wave-O cracks + 10 recovered wave-J/K/L drafts, verified in ONE clean rebuild:
143dbb89f34491258bbc27810d0a12ec8b43a8dd BYTE-IDENTICAL. main stubs 1881 -> 1835.

Wave O was a 3-arm 49-card wave (6,266 ins): main head-crack, main UNKNOWN, overlay UNKNOWN.
47/49 standalone MATCH, independently re-verified by me (R14) at 47/49 -- exact agreement --
and reloc_identity reported 46 AGREE / 0 MISMATCH, the first wave of the campaign with zero
symbol errors. THE UNKNOWN LEVER DRAFTS LIKE ANY OTHER LANE, which matters strategically: it is
~138k ins fleet-wide (a quarter of everything open) and was routed as "needs its own lane".

FOUR gate_main defects fixed here, each of which had been silently costing prior waves drafts:
- typedef stripping walked drafts in SLATE order while substitution happens at ADDRESS order, so
  the surviving typedef could land BELOW a draft using it -> "syntax error before D_800A651C".
  Verified the two orders genuinely diverge for both destination files in this slate.
- a BUILD failure (sha None) fell through to a silent bisect -- a full clean rebuild per step to
  rediscover what the compiler had already printed and discarded. Now the error lines are shown
  and the offending drafts named for undefined-reference/redefinition/conflicting-types.
  (My first version of that printer TAILED a stderr+stdout concatenation and faithfully showed 25
  lines of make progress chatter instead of the error -- selecting by position, not by content.)
- typesig treated "short" and "s16" as different types (R39 over-refusal). Aliases now normalize;
  11/11 NC cases pass, with signedness, volatile and array-vs-scalar still conflicting correctly.
- conflict detection ignored shared headers: engine_core.h's DEFINE_ macros declare symbols in
  their own bodies, so a draft's file-scope array decl of D_800A651C was illegal. Block-scoping
  the draft's extern fixes it byte-identically.

DECLARATION RECONCILIATION took the slate from 5 dropped to 0, and three of the four conflicts
were load-bearing CODEGEN, not style: the array form of D_80078D88 blocks a sched1 hoist (scalar
users adopt [0] for free); "volatile" on D_800B9A02 is required by one draft and fatal to two
others (plain u16 loses 1 bank, volatile loses 2); D_800A651C needs block scope. Cookbook §176f.
2026-08-15 14:01:19 -06:00
Drew T 02edb37265 fix(phase-31): gate_main conflict check reads the destination TU + is per-file (S52-7)
resolve_conflicts() had two defects, both found by the wave-J/K/L draft recovery:

(a) THE SYMBOL TABLE STARTED EMPTY -- only draft-vs-draft was compared, so a draft contradicting
    a declaration ALREADY IN the .c reached the rebuild and surfaced only as a compile error and
    a bisect. src/800.c carries 'extern void func_8001C9D0(void);' (from banked func_8001C2C4)
    while three wave-J drafts declared it (s32)/(void *). The TU now seeds the table, and the
    drop report names whether the clash is with the TU itself or an earlier draft.
(b) ONE NAMESPACE FOR ALL FILES -- 'seen' was global across the slate, so two drafts landing in
    DIFFERENT .c files could not legally disagree about a symbol. Separate TUs are separate
    namespaces; the table is now keyed per destination file (R39: over-refusal discards good work).

On the 11 recovered drafts the new check named 7 real TU conflicts that the old one missed
entirely. All 7 were repaired by adopting the TU's declaration verbatim and casting at the use
site -- including a NEW variant: when the TU's prototype takes no argument and the call must pass
one, cast through a function pointer, ((void (*)(s32))func_8001C9D0)(a0). All 11 re-verified
MATCH afterwards, so the cast is byte-identical in every case.

R39 NC: a synthetic draft re-contradicting the TU is still dropped; the repaired slate is 11/11.
2026-08-15 09:35:26 -06:00
Drew T 093a05ab36 feat(phase-31): gate_main strips duplicate typedefs on substitution (the wave-L loss)
- each draft compiles STANDALONE so it carries its own 'typedef struct {...} SVECTOR;'. Once
  one such function banks, that typedef lives in src/800.c forever and every later draft
  defining its own collides — a C89 duplicate-typedef error, not a byte miss. harvest_verify
  already handles this; gate_main did not, and wave L lost a verified-correct draft to it.
- strip_dup_typedefs() drops typedefs the destination file (or an earlier body in the same
  batch) already defines, and keeps novel ones.
- R39 NC: drops the duplicate, keeps the novel one, leaves the function body untouched, and is
  a strict no-op when there are no duplicates.
2026-08-15 06:32:30 -06:00
Drew T 219fbd7e04 fix(phase-31): gate_main FALSE PASS — sha() read a stale binary when the build failed
- clean_build() ran 'make build' and then sha()'d build/us/SLUS_007.26 off disk. If the build
  FAILED (compile error), the PREVIOUS successful binary was still there, so sha() returned the
  good hash and the tool reported BYTE-IDENTICAL for a build that never ran.
- that is exactly how it claimed '43 banked' for wave K on a TU that did not compile; the
  clean-fleet R22 caught it ([FAIL] main). A verifier that can pass without building is worse
  than no verifier.
- fix: rm the output before building, and treat a non-zero make return as no-hash/never-pass.
- also: unbuffered print (a 16-min run looked hung with an empty log) and read the compile
  error to name the culprit instead of bisecting at a full clean rebuild per step.
2026-08-15 05:10:11 -06:00
Drew T e05f3e3e83 fix(phase-31): gate_main typesig kept only the prefix — u8 D_x and u8 D_x[] compared EQUAL
- gate_main reported wave K BYTE-IDENTICAL; the clean fleet R22 then failed [FAIL] main.
  Three drafts declared D_80078D98 inconsistently (1 scalar, 2 array) and my conflict checker
  could not see the difference: typesig() split on the symbol and kept only the prefix.
- fixed to retain the declarator suffix ('' vs '[]'); NC'd both directions — the wave-K
  conflict is now caught, wave J's known answer (34/5) is unchanged.
- BOTH failure modes now documented in the tool: v1 too STRICT (compared parameter names,
  discarded 2 good drafts), v2 too COARSE (ignored [], passed a real conflict). R22 caught
  what the tool missed, which is exactly why the clean-rebuild rule exists.
- phase log: recorded the night's methodological lesson — every serious stall was an
  instrument trusted without a control, never the compiler.
2026-08-15 04:32:23 -06:00
Drew T c4dd60363d feat(phase-31): tools/gate_main.py — batch clean-rebuild gate for the main EXE
- main CANNOT be gated incrementally: its extract runs the EXE-only psyq_integrate +
  ld_interleave steps that REWRITE the .ld, so gate_lane/gate_stage's incremental build
  re-runs that on an already-rewritten script and yields a FALSE diff (R22's own rationale).
  That cost a night: 4 byte-correct drafts gated 0/4 and I wrote up a nonexistent linker
  defect before the null-draft control refuted it.
- gate_main substitutes the whole batch -> make extract BINARY=main -> make build -> compare
  SHA. ONE clean build verifies the WHOLE batch (34 banked in one rebuild); bisects on failure
  so a single bad draft can't sink the rest.
- handles both main-specific hazards: (1) in-TU cross-draft decl conflicts, resolved greedily
  on TYPE SIGNATURES ONLY (comparing parameter names wrongly discards good drafts, R39 — my
  own first version did exactly that); (2) stale .s after a revert (extract before resolving).
- NC'd against wave J's known answer: 34 compatible / 5 dropped, matching the hand result.
2026-08-15 04:10:37 -06:00