mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-30 23:37:39 -04:00
fix(phase-29): the gate manufactured 3 false CC1-FAIL verdicts — carve-refusal, tree hygiene, R32 (§97)
A 15-draft harvest_verify batch reported CC1-FAIL=4 and `final SHA None`. Three of the four were the HARNESS, not the compiler. Checked the tree FIRST (the MISMATCH is a tree alarm, not a result), reverted to the committed baseline rather than reasoning about a half-applied state, rebuilt -> d19c9580 BYTE-IDENTICAL. No banked result was ever at risk: the byte-gate cannot manufacture a match, but it CAN manufacture a verdict — and verdicts are what the backlog and roadmap are built from. ORDERING PROVED THE CASCADE (R14): items 1-11 are real (9 PLUMBING, 2 DIFF), all before item 12 — jtbl_carve REFUSING func_8013B83C (§59(3) non-contiguous same-subseg table). Items 13-16 are four CC1-FAILs on the SAME ov_SC01_077_o0.o = one refused carve counted four times. THREE DEFECTS FIXED: 1. `_ok` was computed and IGNORED — a refused carve was spliced and built anyway into a guaranteed Error 33, filed as CC1-FAIL. Now a named CARVE-REFUSED class, skipped (one build cheaper). 2. attempt() never restored on failure, so the tree was dirty BETWEEN drafts — and _jtbl_snapshot() snapshots the tree AS IT FINDS IT, so a later carve captured an earlier FAILED draft's splice and its undo faithfully RE-APPLIED it, after the final _write(baseline). That is the entire `final SHA None` mechanism. Invariant restored: the tree is at baseline except while a draft is under test (atomic AND bisect branches). 3. The recovery's own `make extract` rc was unchecked (_sh does not raise — §93's sibling). Now loud. Plus an R32 assertion on the cleanup: at 0 verified a non-empty git status is residue, not a result; it names the files and the recovery command. It fired correctly on its first real run. MEASURED RECOVERY (same drafts, clean tree): func_8013B83C -> CARVE-REFUSED; func_801789AC -> PLUMBING (actionable); func_8017C974 -> DIFF (corroborates its agent's global_alloc spill diagnosis); func_80140958 -> CC1-FAIL (genuinely its own). final SHA None -> d19c9580; tracked diff empty. BLAST RADIUS OF §96, HONESTLY: the reconcile_tu span fix unblocked func_80176218 (banked, swept 133/137) and no other draft in the batch. 7 of the 9 PLUMBING are `conflicting types for <the function itself>` = the DEF-side self-decl axis conform_decls owns — the next lever, now a measured target list rather than a guess. cookbook §97.
This commit is contained in:
@@ -7041,3 +7041,50 @@ different "next conflict".
|
||||
> needed both: 4 data symbols conformed, then 2 callees (`func_80177AD4` `void (int, unsigned int)`,
|
||||
> `func_80178298` `(u32*, u8*, short, short)`) decl-conformed + call-site-cast to the draft's
|
||||
> intended widths.
|
||||
|
||||
## §97 — The gate's own tree hygiene: a refused carve, an unchecked recovery, and a snapshot that captured a dirty tree (Phase 29 SESSION-22)
|
||||
|
||||
A 15-draft `harvest_verify --chunk 1` batch reported `CC1-FAIL=4` and ended `final SHA None`. Three
|
||||
of those four were manufactured by the harness. The chain, in order:
|
||||
|
||||
1. **`_ok` was computed and ignored.** `_jtbl_prep_one` returns `(ok, snapshot)`; the caller tested
|
||||
only `snapshot`. When `jtbl_carve` REFUSES a table (§59(3): a non-contiguous same-subseg carve),
|
||||
`ok` is False and `snapshot` is None — so the draft was spliced and built **without its carve**,
|
||||
which cannot link. The resulting `Error 33` was filed as **CC1-FAIL**, a codegen-flavoured verdict
|
||||
for a pure plumbing wall. Now classified `CARVE-REFUSED` and skipped (also one build cheaper).
|
||||
2. **`attempt()` does not restore on failure.** It writes the candidate render and relies on the
|
||||
NEXT attempt's render to overwrite it — so the tree is dirty *between* drafts.
|
||||
3. **`_jtbl_snapshot()` snapshots the tree as it finds it.** Combined with (2), a later draft's carve
|
||||
captured an EARLIER FAILED DRAFT'S SPLICE, and its undo then faithfully **re-applied** it — after
|
||||
the final `_write(baseline)` had already run. A run that verified nothing therefore ended with a
|
||||
body spliced on disk and unable to rebuild the binary, which reads exactly like a byte regression
|
||||
and is not one.
|
||||
4. **The recovery's own `make extract` was unchecked.** `_sh` does not raise (§93's sibling), so a
|
||||
failed re-extract inside `_jtbl_restore` would report nothing — a recovery that silently did not
|
||||
recover, strictly worse than not attempting one.
|
||||
|
||||
**The invariant, one line:** *the gate's tree is at `baseline` except while a specific draft is under
|
||||
test.* Restore after every failed attempt (atomic branch AND bisect branch), and the snapshot can
|
||||
never capture someone else's failure.
|
||||
|
||||
**Plus an R32 coverage assertion on the cleanup itself:** at 0 verified, a non-empty
|
||||
`git status --porcelain src/<bin> config` is residue, not a result — print the files and the recovery
|
||||
command rather than making the operator go looking.
|
||||
|
||||
**Measured recovery of the false verdicts** (same 4 drafts, same drafts dir, clean tree):
|
||||
|
||||
| draft | contaminated | true |
|
||||
|---|---|---|
|
||||
| `func_8013B83C` | CC1-FAIL | **CARVE-REFUSED** (§59(3) wall) |
|
||||
| `func_801789AC` | CC1-FAIL | **PLUMBING** — `conflicting types for func_801789AC` (actionable) |
|
||||
| `func_8017C974` | CC1-FAIL | **DIFF** — corroborates its agent's `global_alloc` spill diagnosis |
|
||||
| `func_80140958` | CC1-FAIL | CC1-FAIL (genuinely its own, on a different object) |
|
||||
|
||||
`final SHA` went `None` → `d19c9580` BYTE-IDENTICAL; tracked diff empty.
|
||||
|
||||
> **The law (R34/R35 again, pointed at the gate itself):** the whole-binary byte-gate is a perfect
|
||||
> oracle for *did this draft match* and a NULL oracle for *what state did I leave behind*. It cannot
|
||||
> manufacture a match — no banked result in this session was affected — but it can manufacture a
|
||||
> **verdict**, and verdicts are what the backlog and the roadmap are built from. A failure class is
|
||||
> evidence about the compiler only once the harness is proven not to be the cause (§53's carve law,
|
||||
> generalized from "sweep with the right tool" to "gate from a clean tree").
|
||||
|
||||
@@ -5805,3 +5805,54 @@ Each reverted its byte-neutral self-decl edit cleanly ("no dead diff left behind
|
||||
honest. Per **§59** a sweep failure is a per-sibling INTEGRATION signal, not a codegen verdict:
|
||||
the next step is to read ONE sibling's real gate result (COMPILE-fail vs byte-DIFF) before
|
||||
concluding anything about the class.
|
||||
|
||||
## ⚠️ T14 — the 15-draft batch banked 0, and 3 of its 4 "CC1-FAIL" verdicts were the HARNESS (§97)
|
||||
|
||||
Ran the reconcile+cast ladder over all 24 SESSION-21 drafts to measure the §96 fix's **blast radius**
|
||||
(a confirmed mechanism proves nothing about consequence). 15 had live stubs, across 9 TUs — the
|
||||
reconcile was re-run with the TU **derived per draft** (the first pass had forced one `--src-file`,
|
||||
correct only for the 801734BC TU), and `cast_call_sites` was run grouped by TU.
|
||||
|
||||
**Prepared:** 4 drafts reconciled / 10 data symbols; 3 drafts cast-recovered / 8 callees.
|
||||
**Gated: 0 verified / 15 failed, `final SHA None (*** MISMATCH ***)`.**
|
||||
|
||||
### The MISMATCH was a tree-state alarm, not a matching result — checked FIRST
|
||||
4 source files were left modified at 0 verified. Reverted to the committed baseline rather than
|
||||
reasoning about a half-applied state (the SESSION-21 lesson), then rebuilt: **`d19c9580`
|
||||
BYTE-IDENTICAL**. No banked result was ever at risk — every bank this session passed the whole-binary
|
||||
gate AND a clean-tree R22.
|
||||
|
||||
### Ordering proved the counts were a cascade (R14)
|
||||
Items 1–11 are real verdicts (**9 PLUMBING, 2 DIFF**), all recorded BEFORE item 12 — the
|
||||
`jtbl_carve` REFUSAL of `func_8013B83C` (§59(3) non-contiguous same-subseg table). Items 13–16 are
|
||||
four CC1-FAILs **on the same `ov_SC01_077_o0.o`**. That is ONE refused carve counted four times,
|
||||
three of them against drafts never actually diagnosed.
|
||||
|
||||
### Three harness defects, each independently justified (cookbook §97)
|
||||
1. **`_ok` was computed and ignored** — a refused carve was built anyway into a guaranteed `Error 33`
|
||||
and filed as CC1-FAIL, a codegen-flavoured verdict for pure plumbing. Now a named
|
||||
**`CARVE-REFUSED`** class, skipped (and one build cheaper).
|
||||
2. **`attempt()` never restored on failure**, so the tree was dirty BETWEEN drafts — and
|
||||
`_jtbl_snapshot()` snapshots the tree as it finds it, so a later carve captured an EARLIER FAILED
|
||||
DRAFT'S SPLICE and its undo faithfully **re-applied** it, after the final `_write(baseline)`.
|
||||
That is the whole `final SHA None` mechanism. Fixed with the invariant: *the tree is at `baseline`
|
||||
except while a draft is under test* (both the atomic and bisect branches).
|
||||
3. **The recovery's own `make extract` return code was unchecked** (`_sh` does not raise — §93's
|
||||
sibling, and the same class already logged in Task 14 stage 1). Now loud.
|
||||
4. Plus an **R32 assertion on the cleanup**: at 0 verified, a non-empty `git status --porcelain` is
|
||||
residue, not a result — it names the files and the recovery command. **It fired correctly on its
|
||||
first real run.**
|
||||
|
||||
### Measured recovery of the false verdicts (same drafts, clean tree)
|
||||
`func_8013B83C` CC1-FAIL → **CARVE-REFUSED** · `func_801789AC` CC1-FAIL → **PLUMBING**
|
||||
(`conflicting types for func_801789AC` — actionable) · `func_8017C974` CC1-FAIL → **DIFF**
|
||||
(corroborating its agent's `global_alloc` spill-choice diagnosis) · `func_80140958` CC1-FAIL →
|
||||
CC1-FAIL (genuinely its own, on a different object).
|
||||
**`final SHA None` → `d19c9580` BYTE-IDENTICAL; tracked diff empty.** 3 of 4 corrected.
|
||||
|
||||
### The honest blast-radius answer for §96
|
||||
The `reconcile_tu` span fix unblocked **`func_80176218` (measured, banked, swept 133/137)** and did
|
||||
**not** by itself unblock any of the other 14 — their blockers are a different axis. **7 of the 9
|
||||
PLUMBING are `conflicting types for <the function itself>`**, i.e. the DEF-side self-decl axis that
|
||||
`conform_decls` owns (the path that banked `func_80179B74` and `func_8015B950` at 137/137 in
|
||||
SESSION-21). That is the next lever, and it is now a measured target list rather than a guess.
|
||||
|
||||
+44
-1
@@ -272,7 +272,13 @@ def _jtbl_restore(snap):
|
||||
os.remove(rf)
|
||||
except OSError:
|
||||
pass
|
||||
_sh(['make', '--no-print-directory', 'extract', 'BINARY=%s' % a.binary])
|
||||
# R32/§93: CHECK the recovery's own exit status. `_sh` does not raise, so a failed re-extract here
|
||||
# left the generated build inputs describing the abandoned isolation while config/ read clean —
|
||||
# a recovery that silently did not recover, which is strictly worse than not attempting one.
|
||||
if _sh(['make', '--no-print-directory', 'extract', 'BINARY=%s' % a.binary]).returncode:
|
||||
print(' [jtbl] !! RESTORE INCOMPLETE: re-extract FAILED after undoing the carve — the tree '
|
||||
'is NOT back at baseline; stop and `git checkout -- src/ config/` before trusting any '
|
||||
'later verdict in this run')
|
||||
_reload_corpus()
|
||||
|
||||
|
||||
@@ -435,6 +441,22 @@ while i < len(items):
|
||||
_jsnap = None
|
||||
if len(chunk) == 1:
|
||||
_ok, _jsnap = _jtbl_prep_one(chunk[0])
|
||||
if not _ok:
|
||||
# THE CARVE WAS REFUSED — do NOT build this draft (Phase 29 SESSION-22, byte-witnessed).
|
||||
# `_ok` was computed and ignored here, so a table-bearing draft whose carve jtbl_carve
|
||||
# REFUSED (§59(3): a non-contiguous same-subseg table) was spliced and built anyway. It
|
||||
# cannot link without its carve, so the build fails with `Error 33` — and that failure was
|
||||
# then recorded as **CC1-FAIL**, i.e. a codegen-flavoured verdict, for what is purely a
|
||||
# carve-plumbing wall. Worse, the tail of the batch inherited the broken object: one
|
||||
# refused carve produced FOUR CC1-FAILs on the same `ov_SC01_077_o0.o`, three of them
|
||||
# against drafts that were never even diagnosed, and the run ended unable to rebuild the
|
||||
# binary at all (`final SHA None`). Skip it with a NAMED class instead: honest, cheaper by
|
||||
# one build, and it leaves the tree where the next draft can be judged on its own merits.
|
||||
fn = chunk[0]
|
||||
failed.append((fn, 'CARVE-REFUSED'))
|
||||
print(' - %s (%s) [CARVE-REFUSED: jtbl_carve declined the table; §59(3) plumbing, '
|
||||
'NOT a codegen verdict]' % (fn, drafts[fn]['conf']))
|
||||
continue
|
||||
if _jsnap is not None: # a jtbl carve happened -> reconcile the draft vs the CARVED TU
|
||||
_jtbl_reconcile(chunk[0])
|
||||
if attempt(chunk):
|
||||
@@ -443,6 +465,15 @@ while i < len(items):
|
||||
elif len(chunk) == 1:
|
||||
if _jsnap is not None:
|
||||
_jtbl_restore(_jsnap) # stranded-carve + truncated-TU undo (R32/§61)
|
||||
# AND PUT THE SPLICE BACK (Phase 29 SESSION-22). `attempt()` writes the candidate render and
|
||||
# does not restore on failure — it relied on the NEXT attempt's render to overwrite it. That
|
||||
# left the tree dirty BETWEEN drafts, and `_jtbl_snapshot()` snapshots the tree AS IT FINDS
|
||||
# IT: a later draft's carve therefore captured an earlier FAILED draft's splice, and its undo
|
||||
# faithfully RE-APPLIED it — after the final `_write(baseline)` had already run. Net effect: a
|
||||
# run that verified nothing still ended with a spliced body on disk and could not rebuild the
|
||||
# binary (`final SHA None`), which reads exactly like a byte regression and is not one.
|
||||
# The invariant is one line: the tree is at `baseline` except while a draft is under test.
|
||||
_write(baseline)
|
||||
# ATOMIC CHUNK — do NOT bisect (Phase-28 T6). The old code fell into the loop below and
|
||||
# re-ran attempt([fn]) on the SAME single element against the SAME baseline: a byte-identical
|
||||
# DUPLICATE build. classify_fail reads _last_sha/_last_err, which the failed attempt(chunk)
|
||||
@@ -462,6 +493,7 @@ while i < len(items):
|
||||
klass = classify_fail(_last_sha) # DIFF (real codegen) vs PLUMBING (recoverable) vs CC1-FAIL
|
||||
failed.append((fn, klass))
|
||||
print(' - %s (%s) [%s]' % (fn, drafts[fn]['conf'], klass))
|
||||
_write(baseline) # same invariant as the atomic branch above
|
||||
|
||||
# restore the accumulated verified state and confirm the binary is byte-identical
|
||||
_write(baseline)
|
||||
@@ -477,6 +509,17 @@ if _klass:
|
||||
print(' failed by class:', ' '.join('%s=%d' % (k, n) for k, n in sorted(_klass.items())))
|
||||
print('VERIFIED:', ' '.join(verified) or '(none)')
|
||||
print('FAILED :', ' '.join(fn for fn, _ in failed) or '(none)')
|
||||
# R32 — assert the gate LEFT THE TREE where it found it (plus whatever it banked). `_write(baseline)`
|
||||
# above restores the files render() manages; a carve/isolation can touch files it does not. At 0
|
||||
# verified the tracked diff must be EMPTY, and any residue is a failed draft still spliced in — which
|
||||
# the next run would silently gate on top of. Name the files; do not make the operator go looking.
|
||||
_dirty = [l[3:] for l in _sh(['git', 'status', '--porcelain', '--', 'src/%s' % a.binary,
|
||||
'config']).stdout.splitlines()]
|
||||
if _dirty and not verified:
|
||||
print(' !! TREE NOT CLEAN at 0 verified — residue from a failed draft/carve, NOT a result:')
|
||||
for q in _dirty[:12]:
|
||||
print(' %s' % q)
|
||||
print(' recover with: git checkout -- src/%s config' % a.binary)
|
||||
open(a.verified_out, 'w').write('\n'.join(verified) + '\n')
|
||||
# failed_out stays NAMES-only (backward-compatible for existing consumers); the class goes to a sidecar
|
||||
open(a.failed_out, 'w').write('\n'.join(fn for fn, _ in failed) + '\n')
|
||||
|
||||
Reference in New Issue
Block a user