Skip to content

tools: fix test_tubuild expectations that demanded #2066's defect back - #2075

Merged
andrewboudreau merged 1 commit into
mainfrom
tools/tubuild-pilot-expectations
Aug 31, 2026
Merged

tools: fix test_tubuild expectations that demanded #2066's defect back#2075
andrewboudreau merged 1 commit into
mainfrom
tools/tubuild-pilot-expectations

Conversation

@andrewboudreau

Copy link
Copy Markdown
Collaborator

One of these tests asserted the defect #2066 fixed

tools/test_tubuild.py has two failing assertions (52 passed / 2 failed on main at 6c7d35b0c). Task #79 asked whether that is (a) stale expected strings or (b) a real PoleLift regression from the #2066 inline-destructor flip, and said to read the actual output before assuming (a).

Measured, and it is (a) — but with a sting. One of the two is not merely stale. It asserts the anomaly #2066 removed:

# The one documented, non-fixable anomaly (pilot report sec 3): the compiler
# emits D2/D0/D1 in a fixed order that puts D0 before D1, opposite the ROM.
assert "1 ordinal pair(s) NOT in ROM order: [(0, 1)]" in out

An out-of-line destructor makes mwccarm emit D2/D0/D1 with D0 ahead of D1, opposite the cartridge. #2066 (467bde020) moved the destructor into the class body, and the compiler then emits exactly the ROM's two variants in the ROM's order. So this test now demands the defect back: the only way to make it pass by "fixing the code" would be to revert #2066. That is worth more than the numbers below.

Measured, not inferred

I reverted both #2066 files together — the shadow source alone will not compile against the post-#2066 header (object 'PoleLift::~PoleLift()' redefined), which is precisely why this surfaced as a failing assert rather than a quietly wrong number — and ran tubuild.py compile on each side:

sections .text unlicensed D2 ordinal check
467bde020^ 37 8 12 _ZN8PoleLiftD2Ev present 1 ordinal pair(s) NOT in ROM order: [(0, 1)]
467bde020 35 7 11 absent ALL MATCH, ROM order

Section arithmetic: each vague-linkage symbol that stops being emitted costs two sections — the section itself plus its own .rela. That is 43 → 37 at #1555 (three symbols: Platform's vtable and its two out-of-line destructors, anchored by giving Platform a key function) and 37 → 35 here (one). The docstring now carries all three rows so the next drift is diagnosable instead of mysterious.

Stale expectations become regression guards

Bumping 37 to 35 would fix the red and lose the information. Each stale expectation is instead restated as an absence, so reintroducing an out-of-line definition fails loudly right here:

assert "NOT in ROM order" not in out, out
assert "all 7 function(s) in the expected ROM-ascending section order" in out
assert "_ZN8PoleLiftD2Ev" not in out, "an out-of-line ~PoleLift() is back; see #2066"
assert "UNLICENSED function symbols" not in out, out

The last one is an absence rather than "(0)" because tubuild.py compile prints no function block at all when the count is zero. Asserting a count of zero would never have matched.

Promotion is still correctly refused, but for a narrower reason than before: what remains unlicensed is _ZTI8PoleLift/_ZTS8PoleLift, for which no compiler_only_output disposition is admissible while the class carries a coined name the cartridge contradicts.

Negative control

Same discipline as #2074 — a guard nobody has watched fail is a guess. Reverting both files to 467bde020^ fails the new assertion:

FAILED tools/test_tubuild.py:166 -- assert "NOT in ROM order" not in out

The guards have teeth. (A third assertion at line 112 also fails in that mixed state; that is an artifact of old source against today's manifest and I am not claiming it as evidence.)

No path names PoleLift

The new prose names the class and the mangled symbols, never a file path. PoleLift is a coined name the cartridge contradicts —

_ZTV8PoleLift @ 0x02112dbc (ov045)   ROM name 18daObjKm2_Ami_Bou_c   DISAGREES

— and the rename is a prerequisite for promoting this very TU, so include/PoleLift.h and src_tu/actors/PoleLift.cpp are dead references with a scheduled fuse. That is the defect class #2074 landed to remove, and I caught two of them in my own first draft here. The docstring says why, so the next reader does not restore the paths as a helpful clarification.

Gates

Base is 6c7d35b0c = current origin/main, and git merge-base --is-ancestor origin/main HEAD succeeds, so the merge tree is the branch tree — these are merge-tree measurements, not proxies:

pytest tools/test_tubuild.py      54 passed          (was 52 passed / 2 failed)
check_dead_references             no new dead references, no broken links
check_python_names                PASS, 0 unresolvable
working tree                      only tools/test_tubuild.py; tracked config untouched

Single file, tools-only, no src/, no config/, no generated state. check_dead_references still reports 1 baselined reference(s) now resolve — that is the pre-existing stale src/game/actors baseline entry, not a failure and not from this branch; it is filed separately and deliberately kept out of this PR.

Deliberately split out

tools/test_tubuild.py has 10 sites of if not _toolchain(): return — a silent vacuous PASS, the same false-green shape the worktree skill warns about. .github/workflows/tool-tests.yml already documents it (test_tubuild 28 real pass / 15 vacuous / 2 fail, "return guards become skips"). Converting them to real skips needs a decision about taking a pytest import dependency in this file, which is a different question from this one, so it is not bundled here.

Two assertions in tools/test_tubuild.py went stale at #2066 (467bde0),
which moved ~PoleLift() into the class body. One of them was not merely
stale -- it asserted the anomaly #2066 fixed:

    assert "1 ordinal pair(s) NOT in ROM order: [(0, 1)]" in out

An out-of-line destructor makes mwccarm emit D2/D0/D1 with D0 before D1,
opposite the ROM; the pilot report called that non-fixable. Defining the
destructor inline emits exactly the ROM's two variants in ROM order. So
the only way to make this assertion pass by "fixing the code" would be to
revert #2066.

Measured across 467bde0^ -> 467bde0, swapping the shadow source and
the class header together (the pre-#2066 source does not compile against
the post-#2066 header, which is why this surfaced as a failing assert
rather than a quietly wrong number):

  before  37 sections, 8 .text, D2 present, "1 ordinal pair(s) NOT in
          ROM order: [(0, 1)]", 12 unlicensed
  after   35 sections, 7 .text, no D2, "ALL MATCH, ROM order", 11
          unlicensed

Section arithmetic: each vague-linkage symbol that stops being emitted
costs two sections, itself plus its own .rela. That is 43->37 at #1555
(3 symbols) and 37->35 here (1).

Rather than bump the numbers, the stale expectations become regression
guards stated as absences, so reintroducing an out-of-line definition
fails loudly here:

  assert "NOT in ROM order" not in out
  assert "_ZN8PoleLiftD2Ev" not in out
  assert "UNLICENSED function symbols" not in out

The last is an absence because tubuild prints no function block at all
when the count is zero -- asserting "(0)" would never match.

Negative control: reverting both files to 467bde0^ fails the new
assertion at tools/test_tubuild.py:166. The guards have teeth.

No path naming PoleLift appears in the new prose. PoleLift is a coined
name the cartridge contradicts (ROM RTTI: 18daObjKm2_Ami_Bou_c) and the
rename is a prerequisite for promoting this very TU, so such a path is a
dead reference with a scheduled fuse -- see #2074.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WdCK1xgrJdiJzPCh3bAJfQ
@tangos-validator

tangos-validator Bot commented Aug 31, 2026

Copy link
Copy Markdown

✅ PR validation — Passed

noverify: no source/build-data changes in this PR

Each changed src/*.c|*.cpp is compiled and its relocated bytes compared to the binary data on a private build box. Passing requires every changed file to reproduce the ROM byte-for-byte with correct relocation targets — this catches WRONG-DEST relocations and non-reproducing near-misses that ledger-scoped linkcheck skips.

@andrewboudreau
andrewboudreau merged commit eace12b into main Aug 31, 2026
6 checks passed
@andrewboudreau
andrewboudreau deleted the tools/tubuild-pilot-expectations branch August 31, 2026 10:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant