Promote daPropeller_Heyho_c as a compiler-owned C++ TU - #2096
Conversation
✅ PR validation — PassedCommitted merge passes; 26 contributor credit reassignment(s) noted, not a blocker. Full merge validation
Contributor credit moved (26)
+1 more; the full list is in the JSON report's Byte-verified means the range carries Warnings: contributor attribution changed, not a blocker (26 changed, 0 lost -- src/actors/daPropeller_Heyho_c.cpp: tangosdev -> github-actions[bot]; src/actors/daPropeller_Heyho_c.cpp: tangosdev -> github-actions[bot]; src/actors/daPropeller_Heyho_c.cpp: tangosdev -> github-actions[bot]; +23 more); 27 address range(s) left the byte-verified set while enrolled totals held steady: ov070:0x0211f000-0x0211f048, ov070:0x0211f048-0x0211f0a4, ov070:0x0211f0a4-0x0211f100, ov070:0x0211f100-0x0211f368, ov070:0x0211f368-0x0211f450, +22 more; 5 linkcheck result(s) have unresolved relocations; 1 affected source file(s) could not be fully link-checked; 26 more function(s) now claim a match that nothing compiles; enroll them in a delinks.txt to have the ROM build check them. Per-file link-check detail1 of 1086 changed file(s) do not match the ROM (NO-SYM).
The private worker commits a test merge, builds the stock ROM profile, compares every executable module, measures matched and source-built code, checks contributor lineage, and verifies affected relocations. The mod profile is opt-in and is not part of this merge gate. |
andrewboudreau
left a comment
There was a problem hiding this comment.
CHANGES REQUESTED
One blocking ask, and it is a two-line rename. Everything else here is merge-ready and some of it is the best-evidenced work in this series — I re-derived the central claim from the cartridge myself and it holds.
The rename is ROM-proven — independently verified
I did not take the manifest's word for this. Straight out of the ov070 image:
19daPropeller_Heyho_csits at ov070 offset0x00412c(0x0212312c), length prefix 19, matching the 19-character name exactly, NUL-terminated._ZTV19daPropeller_Heyho_cat0x02123168points through_ZTI19daPropeller_Heyho_cat0x02123120to it.FlyGuyappears in no ROM image, anywhere.- The sibling
24daPropeller_Heyho_Fire_cis in ov002, consistent.
So FlyGuy was a coined decomp name and this PR is right to retire it. Dropping the duplicate _ZTV6FlyGuy that shared 0x02123168 with _ZTV19daPropeller_Heyho_c is a real cleanup on its own — two symbols at one address is exactly the shape that makes later data verification ambiguous.
Also verified independently:
- Converted baseline moves as a SET, not a count. The raw diff looks like it drops seven rows belonging to dScMgBSC_c, dScMgCard_c, dScMgMCarlo_c and dScMgMCarlo2_c. It does not — those are re-sorted, and a set difference confirms it. The only real motion is 8 removed / 7 added, all FlyGuy, with
src/FlyGuy_ChangeState.cppthe single genuine drop.2571 -> 2570is honest. - source-coverage
-25is arithmetic, not loss. 26 files deleted plus one renamed in. Consolidation. - Premerge against current main (
b250e59d6): 8/8 gatespass -> pass. Nothing goes green to red. src_tu/actors/FlyGuy.cpp -> src/actors/daPropeller_Heyho_c.cppis recorded as a rename (R090), so attribution lineage survives.
BLOCKING: this PR disproves FlyGuy, then coins two brand-new symbols carrying it
-data_ov070_02123184 kind:data(any) addr:0x02123184 ambiguous
-data_ov070_021231a0 kind:data(any) addr:0x021231a0 ambiguous
+FlyGuy_VT7 kind:data(any) addr:0x02123184 ambiguous
+FlyGuy_VT14 kind:data(any) addr:0x021231a0 ambiguous
These are new coinings, not preserved aliases. They replace neutral, address-derived names that were already correct and already worked, with a class name this very commit demonstrates the cartridge does not carry.
Three things make it worth stopping for, when I would otherwise have merged this today:
- They are the first
partition_symbolsrows that have ever existed. I checked:partition_symbolsappears in zero manifests onorigin/main. Whatever these are called is the pattern the next twenty partitions get copied from. - The fix is free precisely because they are new here. Nothing outside this PR references either name. Reverting is a pure rename with no downstream reach — unlike the 238-row coined-name backlog, which is expensive exactly because it was never caught at this moment.
- The naming scheme is right; only the prefix is falsified.
_ZTV19daPropeller_Heyho_cis at0x02123168;0x02123184is+0x1c= word 7,0x021231a0is+0x38= word 14, and the two spans are contiguous (0x1cthen0x44). "VT7"/"VT14" is genuinely descriptive. It is the class token in front of it that the PR itself refutes.
Preferred fix — keep your scheme, fix the token:
daPropeller_Heyho_c_VT7 addr:0x02123184
daPropeller_Heyho_c_VT14 addr:0x021231a0
Also acceptable — revert to the neutral names, which assert nothing:
data_ov070_02123184
data_ov070_021231a0
Either resolution merges. Update the two partition_symbols rows and the evidence/audit blocks that echo them.
Explicitly NOT this PR's debt
FlyGuy_ChangeState, FlyGuy_Spawn and FlyGuy_SpawnInfo (symbols.txt lines 17, 27, 134) are pre-existing and unchanged by this commit. Your manifest note at line 834 already states the honest position — the cartridge proves their addresses and their relationship to this class, not those source spellings — and I am not asking you to churn them here. I am flagging only that after this lands they are the last FlyGuy left, so whoever retires them inherits a smaller job than it looks.
I also deliberately did not block on the converted-backslide-exceptions.jsonl row. It is structurally identical to three Kurumajiku rows already accepted on main from #2004, and blocking on it without first measuring whether the backslide is avoidable would be me applying a standard I have not applied elsewhere.
Non-blocking, for whenever you next touch these lines
- The exception key
src/actors/daPropeller_Heyho_c.cpp#FlyGuy_ChangeStateembeds a symbol name. When that alias is eventually retired the key strands silently — the row stops matching and stops protecting anything, with no error. - That row's
reasonis generic ("raw offsets, unknown fields, and mangled imports"). The actual blocker is specific and worth recording: the shadowstruct C { char pad[0x3bc]; PMF *pp; }and the pointer-to-member-function type it exists to carry. A future reader can act on that; they cannot act on the generic phrase. src/actors/daPropeller_Heyho_c.cpp:117and:137still carry// recovered name: FlyGuy_OnTurnIntoEgg/FlyGuy_OnAimedAtWithEgg. Carried over, not introduced here, but they now name a class this file's own header disproves.
Credit where it is due
The struct-copy lever documented in the manifest — mwccarm's C++ front end scalarizing a plain struct assignment where the C front end emits an ldm/stm block copy, and an array-wrapper view restoring it — is a genuinely new and reusable finding, and you proved it with fdiff before applying it rather than after. The block-scope extern trick for the two members needing a different ApproachAngle signature is the right answer to a real C++ name-hiding constraint. 27/27 matching, objisolate clean, reloc-destinations clean.
Fix the two names and I will merge this.
tangosdev#2091 added a fail-closed boundary -- validated_vtable_partition_symbols, manifest_vtable_partition_rows, and audit_tu_object's validated_vtable_policies parameter, none of which exist on main before it -- that refuses a manifest-declared symbol carved out of a vtable's public range unless partition_vtable_rebiases independently validated it against content-bound baseline ELF metadata. Six tests describe that boundary. Five of them never execute: test_tu_production.py 1 test module IS in tool-tests.yml -> runs test_objisolate.py 1 test module NOT in the list -> never runs test_tubuild.py 4 tests module runs, finds 0 tests -> never run test_tubuild.py has bare `def test_` functions and no unittest.TestCase, so `python -m unittest tools.test_tubuild` prints "Ran 0 tests ... OK" and exits 0. That matters now rather than eventually. Measured on tangosdev#2091's merge tree, the boundary licenses nothing at all -- 0 of 97 manifest entries, 0 partition_symbols rows tree-wide -- and tangosdev#2096 introduces the first two rows that have ever existed (FlyGuy_VT7, FlyGuy_VT14). It goes from unexercised to load-bearing in one PR, with a decorative safety net. tools/test_tubuild_vtable_partitions.py -- new, four tests, 4 of 4 run in CI and none skips. Mock- and dict-driven: no compiler, no ROM, no config/, no build/. Three of the four are tangosdev#2091's own toolchain-free tests, moved verbatim out of test_tubuild.py -- bodies unchanged, `def f():` to `def f(self):` plus one indent level, plain asserts kept -- so the move reviews as a move. The fourth is new and pins the invariant that is easiest to re-break: licensing compares sectionIndex, never section NAMES. A linked ELF names a vtable's output section after its overlay (OV036/OV047/OV070, measured on the baseline link) while a manifest section is one of .rodata/.init/.ctor/.data/ .bss. Comparing them refuses every real input, which is what made the whole path unreachable, and 0b0e319 removed exactly that comparison. Mutation-checked in both directions: drop the sectionIndex invariant -> caught by the new test restore the section-NAME compare -> caught by the new test AND by the moved licensing test tools/test_tubuild.py -- the three moved tests removed (58 -> 55 functions), and all sixteen `if not _toolchain(): return` guards converted to `raise unittest.SkipTest(...)`. A bare return reports PASS having asserted nothing; `pytest -rs` cannot see it and neither can a reviewer reading a green log. This does NOT make the module wirable -- it still has no TestCase and a third of it still compiles with mwccarm -- and the workflow comments that implied otherwise are corrected. .github/workflows/tool-tests.yml -- the new module added to the invocation list (28 -> 29), plus the inventory entry, and every claim in the header that this change falsified. tools.test_objisolate is deliberately still NOT wired: it is a single @unittest.skipUnless(_compiler(), ...) class, so on a runner it contributes 35 tests and 35 skips -- a green that asserts nothing, the exact shape the header refuses a glob for. Full CI invocation, run locally on a tree WITH the toolchain: Ran 519 tests in 61.934s OK (skipped=3) check_python_names.py PASS. check_dead_references.py: no new dead references, no broken markdown links. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WdCK1xgrJdiJzPCh3bAJfQ
andrewboudreau
left a comment
There was a problem hiding this comment.
Correcting my own earlier review. I told you the preferred fix was to rename these to daPropeller_Heyho_c_VT7 / daPropeller_Heyho_c_VT14, and that reverting to the neutral data_ov070_* names was the lesser alternative. That was wrong, and following it would have made this worse rather than better. Please revert to the neutral names.
I found this while reviewing #2104, which coins two symbols the same way. Chasing it there turned up what these addresses actually are.
What FlyGuy_VT7 and FlyGuy_VT14 really name
My earlier objection was that the FlyGuy prefix is falsified by your own PR. That much holds. But the slot half is wrong too — and more seriously, because it is the half that looks checkable.
_ZTV19daPropeller_Heyho_c is at 0x02123168, so +7*4 = 0x02123184 and +14*4 = 0x021231a0. The arithmetic is right. Nothing else about the reading is.
Both words are ordinary function pointers mid-table. From overlay_0070.bin (base 0x0211f000):
slot 6 0x02120210 slot 13 0x0204357c
slot 7 0x02010fd4 <-- slot 14 0x0204349c <--
slot 8 0x02010fc8 slot 15 0x02043494
Slot 16/17 are 0x0211f000 / 0x0211f048 — your D1/D0. It is one contiguous vtable with no break at 7 or 14.
The vtable's own relocations do not explain the symbols. In config/arm9/overlays/ov070/relocs.txt these addresses appear as relocation sources, not targets:
from:0x02123184 kind:load to:0x02010fd4 module:main
from:0x021231a0 kind:load to:0x0204349c module:main
Many slots in this table relocate to main. Two got symbols. So the cause is external.
The cause is overlay aliasing, and the referent is ov073. Each address occurs as a word exactly once in the cartridge, and the two occurrences are adjacent in an arm9 pointer table:
arm9 0x08cb0c -> 0x02123168
arm9 0x08cb10 -> 0x02123184
arm9 0x08cb14 -> 0x021231a0
Three consecutive entries. ov006, ov070 and ov073 all cover that address window. And config/arm9/overlays/ov073/symbols.txt — already on this branch — names all three:
CccArena_SpawnInfo addr:0x02123168
CccBigIce_SpawnInfo addr:0x02123184
CccSmallIce_SpawnInfo addr:0x021231a0
Three consecutive SpawnInfo objects for three actors of one family, reached from an arm9 spawn table. That is a complete and coherent account of what those arm9 words point at, and it is ov073's, not ov070's.
The clincher is the first entry. 0x02123168 is _ZTV19daPropeller_Heyho_c's own base address, and dsd created no spurious ov070 symbol there — because the vtable symbol already occupied it. The only reason ov070 has symbols at vtable+0x1c and vtable+0x38 is that arm9 table, and that table is ov073's.
So FlyGuy_VT7 and FlyGuy_VT14 are not a good name with a falsified prefix. They are names for CccBigIce_SpawnInfo and CccSmallIce_SpawnInfo, borrowed into the wrong overlay's address space by an alias.
Why my earlier advice was actively harmful
daPropeller_Heyho_c_VT7 would have replaced a false prefix with a true one while leaving the false referent in place — a wrong claim wearing a ROM-proven class name, which is harder to catch later, not easier. I gave that as the preferred option. Please disregard it.
The ask
Revert both rows to data_ov070_02123184 / data_ov070_021231a0, in config/arm9/overlays/ov070/symbols.txt and the two symbols/actor_renames.tsv rows.
The neutral names are still artifacts of the same alias, but they assert nothing, and that is the whole difference. Deleting the two entries outright may be the real answer — that is the anti-pattern-3 shape from #2061 — but I have not proven dsd tolerates their absence, so I am not asking for it here.
I have asked for the same reversion on #2104 (RcCarpet_VT22 / RcCarpet_VT29, ten overlays aliased, referents already named in ov022 and ov029). Same defect, same fix. Settling it here settles it for the stack.
Nothing above touches your byte match, and none of it is a criticism of the TU work in this PR, which I reviewed separately and which holds up. This is one config row pair, and it is the only thing between this and my approval.
28587e4 to
98b83fb
Compare
|
Reviewer blocker resolved: both coined FlyGuy_VT7 and FlyGuy_VT14 aliases are replaced by the neutral exact config symbols data_ov070_02123184 and data_ov070_021231a0, and the two actor_renames rows were removed. The generic fail-closed ELF string-table growth needed for those longer aliases is isolated in #2126; this PR is now based on it. All three source commits are patch-identical after restacking. Independent integration proof: strict TU 27/27 exact with clean relocation destinations and exact Vector3D dead-strip behavior; full production 11,088/11,088, 3/3 source-data claims, 106/106 modules, exact stock ROM, zero mismatches; control 11,038/11,038 exact with the same nine pre-existing symbol errors; affected-consumer linkcheck 1,069 verified, 16 known baseline warnings, zero blocking; port references 405/405; TU state note current. The four lifecycle credit-loss warnings are the expected attribution-override case. |
c2f3dc5 to
ab3956d
Compare
andrewboudreau
left a comment
There was a problem hiding this comment.
The rename is right, and I verified it against the cartridge rather than taking the manifest's word for it — every address in your ROM-PROVEN CLASS IDENTITY note checks out byte for byte. One measured blocker remains (the backslide row), plus the mechanical restack now that #2126 has landed.
The class identity is ROM-proven — independently confirmed
I derived ov070's load base from the vtable itself rather than from config: slot 0 of _ZTV at 0x02123168 is 0x021203b4 = InitResources, which puts the base at 0x0211f000. From there:
0x02123160 00 00 00 00 20 31 12 02 <- _ZTV preamble: offset-to-top 0, typeinfo -> 0x02123120
0x02123120 64 a7 09 02 <- __si_class_type_info vtable
2c 31 12 02 <- name -> 0x0212312c
c0 81 10 02 <- base class _ZTI
0x0212312c "19daPropeller_Heyho_c\0" <- prefix 19 == len("daPropeller_Heyho_c") == 19
Exact. And the refutation of the old name is total: FlyGuy occurs in zero bytes of arm9_dec.bin or any of the overlays. It was coined.
Two siblings corroborate the family, both exact-length:
overlay_0002.bin—24daPropeller_Heyho_Fire_coverlay_0006.bin—22dMg3DHeyhoObjAdapter_c, which is exactly the class your newsrc/_ZN22dMg3DHeyhoObjAdapter_cC2Ev.cppnames.
symbols/actor_renames.tsv moves the ten methods and _ZTV in one commit, which is what I ask for on every class rename — a vtable that moves separately from its methods leaves a window where the table names a class that no longer exists. Mangled lengths are all correct (_ZN19daPropeller_Heyho_c...).
Net effect on the refuted-spelling count in ov070/symbols.txt: 11 of 14 retired (ten _ZN6FlyGuy* plus _ZTV6FlyGuy).
Blocker — the backslide-exception row is a marker artifact, not a regression
You added one row:
{"path": "src/actors/daPropeller_Heyho_c.cpp#FlyGuy_ChangeState",
"reason": "... scored with byte-exact legacy members in the same file; their raw offsets,
unknown fields, and mangled imports are required by the intact stock-ROM match"}That reason is an accurate description of the mis-scoring mechanism, but it is describing a bug, not a property of the code. tools/tiers.py:_marked_member_fragment slices a member's source from its own // @symbol marker to the next marker. A member with no marker has no slice of its own, so it gets scored against text that is not its body.
The file has 25 ROM ordinal banners but only 13 @symbol markers. Twelve members are unmarked, and FlyGuy_ChangeState (ordinal 16, line 386) is the first of them — the nearest marker above it is func_ov070_02120070 at line 358, and the next one is not until line 823, so a twelve-member span is in play.
Measured, by inserting a marker under each of the twelve unmarked banners and re-running tiers.score_member on the same file:
FlyGuy_ChangeState as-is with 12 markers
no_raw_offset False -> True FLIPS
no_unk_field False -> True FLIPS
no_mangled_refs False -> True FLIPS
real_name True True
no_codegen_trick True True
shared_header True True
all-criteria-pass members in the file: 5/25 -> 6/25
All three failing criteria flip, and nothing else in the file regresses. So the waiver is unnecessary: add the twelve markers and delete the row. This is the same shape as #2062, where adding markers flipped all eight waived flags and the ratchet went up.
The twelve to add (the D0/D1 pair stays unmarked by convention — _lifecycle_member_fragment scores those from the inline destructor, and 25 banners + 2 structors is your 27):
ordinal 16 FlyGuy_ChangeState ordinal 10 func_ov070_0211f6e0
ordinal 15 func_ov070_0211ffa8 ordinal 9 func_ov070_0211f694
ordinal 14 func_ov070_0211fd98 ordinal 8 func_ov070_0211f62c
ordinal 13 func_ov070_0211fd60 ordinal 7 func_ov070_0211f5f0
ordinal 12 func_ov070_0211fae4 ordinal 5 func_ov070_0211f450
ordinal 11 func_ov070_0211fa80 ordinal 3 func_ov070_0211f100
Accepting the row instead would write a false regression into tracked config that nothing later re-examines.
Restack — #2126 landed, and two files need regenerating not resolving
main is now e1b878601; #2126 is merged, so tools/objisolate.py and tools/test_objisolate.py drop out of your delta entirely. Your 19-character neutral names (data_ov070_02123184, data_ov070_021231a0) now fit — that was the point of it, and it is the right resolution of the FlyGuy_VT7/VT14 ask.
git merge-tree origin/main <head> reports exactly two conflicts:
CONFLICT (content): attribution.json
CONFLICT (content): notes/cpp-tu-current-state.md
Please do not hand-merge either.
notes/cpp-tu-current-state.mdis generated — its own header says so. Take either side to clear the markers, thenpython tools/cpp_tu_state.py --write-noteand--check-note. Hand-merging splices two stale snapshots: the counts derive from the post-merge tree, not from either parent, so a hand-resolved file is wrong even when both sides look right. Generation refuses unstaged/untrackedsrc/,src_tu/,include/,config/inputs, so stage first.attribution.jsonconflicts three-way rather than as a key union. Attribution is a stated non-goal here — takingmain's side is fine.
One check I did make sure of: the two-tree diff against the new main looks like this branch reverts the Kaitendai promotion ({src => src_tu}/actors/daObjKm3_Kaitendai_c.cpp). It does not — that is just your merge base predating #2125. The actual merge tree keeps src/actors/daObjKm3_Kaitendai_c.cpp and drops src/func_ov047_021113bc.c, which is correct. No action needed; flagging it so nobody else reads that diff and panics.
Follow-up, not blocking this PR
Three FlyGuy_* spellings survive in ov070/symbols.txt — FlyGuy_ChangeState, FlyGuy_Spawn, FlyGuy_SpawnInfo. Your manifest note is correctly hedged about them ("the cartridge proves their addresses and relationship to this class, not those exact source spellings"), and all three are pre-existing on main, so this PR does not make anything worse. But now that FlyGuy is positively refuted rather than merely unattested, they are known-wrong coined names, and the neutral spellings (func_ov070_02120020, func_ov070_02120520, data_ov070_02123144) are the safe landing spot. Separate PR.
Verdict
Approve on content. The rename is the strongest-evidenced one I have reviewed — ROM-attested name, zero-hit refutation of the old one, atomic vtable move, correct mangled lengths. Two things before merge:
- Add the twelve
@symbolmarkers and delete the backslide-exception row. Measured above; not negotiable, because the row records a regression that does not exist. - Restack onto
e1b878601, regeneratingnotes/cpp-tu-current-state.mdrather than resolving it.
Push those and I will re-read and gate it — full ROM build, since this is a promotion. Everything else here is clear.
98b83fb to
e24f64c
Compare
|
Restacked onto current Fresh head
The attribution-override label remains applied. Private validation is running on this refreshed head now. |
7a8f1dd to
c77441b
Compare
|
Addressed the blocking review items in head
Fresh proof: exact ROM ( |
…o main converted-baseline.json is a SET ratchet and cpp-tu-current-state.md is a generated note, so the merge conflict from #2096 landing was resolved by regenerating both with tools/tiers_ratchet.py --update and tools/cpp_tu_state.py --write-note rather than by hand-editing either side. Proved by tools/rombuild.py -j16 on this branch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FsXWgEM3Gtbqs6zEdwLMYV
…to main converted-baseline.json is a SET ratchet and cpp-tu-current-state.md is a generated note, so the merge conflict from #2096 landing was resolved by regenerating both with tools/tiers_ratchet.py --update and tools/cpp_tu_state.py --write-note rather than by hand-editing either side. Proved by tools/rombuild.py -j16 on this branch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FsXWgEM3Gtbqs6zEdwLMYV
…tacking onto main converted-baseline.json is a SET ratchet and cpp-tu-current-state.md is a generated note, so the merge conflict from #2096 landing was resolved by regenerating both with tools/tiers_ratchet.py --update and tools/cpp_tu_state.py --write-note rather than by hand-editing either side. Proved by tools/rombuild.py -j16 on this branch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FsXWgEM3Gtbqs6zEdwLMYV
…obj_c0_switch.cpp (#2134) * Rename class SwitchPillar to its cartridge RTTI name daObjC0_Switch_c tools/rom_class_names.py reports ov012 0x02112344 NAKED SwitchPillar -> daObjC0_Switch_c: the ROM's own _ZTS/_ZTI records beside this vtable spell daObjC0_Switch_c, and the decomp carried a coined name. Applied with tools/class_rename.py --with-derived, which rewrites mangled tokens length-first and verifies idempotence. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FsXWgEM3Gtbqs6zEdwLMYV * Promote ov012/daObjC0_Switch_c to a genuine TU as src/actors/d_a_obj_c0_switch.cpp Eight one-function sources collapse into one real translation unit, enrolled as a single `complete` delinks span 0x021111a0-0x02111450. This is the first promotion that goes through the rename first, and that ordering is the point. The class was called `SwitchPillar`, a coined name. tools/tubuild.py apply_compiler_only_policy is explicit that a coined class reaches no symbol home -- an _ZTI/_ZTS record is a length-prefixed mangled string, so a coined spelling misses on both the prefix and the body, the miss reads as "the ROM has no such record", and the record is dropped unverified. A key-function TU therefore cannot be promoted while the class is coined. The cartridge's own spelling was read out of the ROM, not guessed: _ZTS16daObjC0_Switch_c at ov012:0x0211230c is the byte string "16daObjC0_Switch_c", and _ZTI16daObjC0_Switch_c at 0x02112300 reads [0x0209a764, 0x0211230c, 0x021089ec] -- __si_class_type_info's vtable+8, that typeinfo name, and _ZTI10dBgActor_c, which is the cartridge stating the direct base as well. The preceding commit applied that rename with tools/class_rename.py. With the ROM name in place the eleven vague-linkage records this TU emits resolve as `deadstrip-data` and romdata_check compares them word by word. The destructor moved INLINE into include/daObjC0_Switch_c.h. Written out-of-line, mwcc emits D0 before D1 -- the reverse of the ROM's order, which objisolate refuses -- plus a third D2 base-object body with no ROM home. Inline, the retail D1/D0 pair comes out in cartridge order with no D2, while InitResources, the first out-of-line virtual, keeps this TU as the class's key-function TU so it still owns the complete _ZTV/_ZTI/_ZTS group. Proof: tools/rombuild.py -j16 -> 106/106 exact, 100.000000%, mismatching 0, source-owned data claims 4/4 reproducing. tiers_ratchet 2570 -> 2570 (+0/-0): no backslide, unlike the Tree promotion, because the inline destructor keeps both destructor members as real C++ instead of mangled shims. eligible, port_refcheck, check_dead_references and layout_check all clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FsXWgEM3Gtbqs6zEdwLMYV * Regenerate the ledger for cpp/c0switch-rtti-rename-snake-tu after restacking onto main converted-baseline.json is a SET ratchet and cpp-tu-current-state.md is a generated note, so the merge conflict from #2096 landing was resolved by regenerating both with tools/tiers_ratchet.py --update and tools/cpp_tu_state.py --write-note rather than by hand-editing either side. Proved by tools/rombuild.py -j16 on this branch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FsXWgEM3Gtbqs6zEdwLMYV * Correct the manifest claims an agent review showed were stale An agent review of this promotion re-read the cartridge bytes and confirmed the RTTI claims, all 11 compiler_only_output addresses and the exact `complete` span, but found the entry's own verification block still describing the pre-promotion state and, in one case, asserting the opposite of what this change proves: - functions_occur_in_expected_order read "PARTIAL -- ordinal pair(s) not in ROM order: [(0, 1)]". Ordinals 0 and 1 are D1 and D0, and putting them in the cartridge's order is the whole point of defining the destructor inline in the class header -- written out-of-line mwcc emits D0 before D1 and objisolate refuses the TU outright. The criterion is now PASS and says why. - declared_function_set_equals_defined_function_set pointed at an `unlicensed_output_observed` key that does not exist in this JSON, with a count (12) that no longer matched the 11 compiler_only_output rows. It now cites compiler_only_output and the licensed span. - verification.round and .object still named a src_tu/ shadow object that no longer exists; they now name the production build that proves this entry. - partial_isolation records a superseded round: it describes a consolidation whose linker contribution stayed per-function with delinks.txt untouched, which stopped being true when this entry was enrolled as one `complete` span. Marked superseded rather than deleted, since its byte evidence still holds. Also two inaccuracies in the file's header comment: a legacy source listed as `.c` that was always `.cpp`, and "__si_class_type_info's vtable+8" -- 0x0209a764 is where config/arm9/symbols.txt puts _ZTVN3abi20__si_class_type_infoE itself. Prose and manifest metadata only; no code changed. Re-proved with tools/rombuild.py -j16: 106/106 exact, 100.000000%, mismatching 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FsXWgEM3Gtbqs6zEdwLMYV * Regenerate the ledger for cpp/c0switch-rtti-rename-snake-tu on top of cpp/ov002-tree-snake-tu config/converted-baseline.json is a SET ratchet and notes/cpp-tu-current-state.md is a generated note, so both are regenerated with tools/tiers_ratchet.py --update and tools/cpp_tu_state.py --write-note rather than hand-merged. Regenerating them in the same pass as the merge -- rather than in a second pass afterwards, which is what left every child diverged from its parent -- is what keeps the stack trivially mergeable. Proved by tools/rombuild.py -j16 on this branch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FsXWgEM3Gtbqs6zEdwLMYV * Restore the eight pre-rename tree.cpp attribution keys The merge of cpp/ov002-tree-snake-tu resolved attribution.json by dropping the pre-rename src/actors/tree.cpp#_ZN4Tree* keys in favour of their post-rename src/actors/d_a_tree.cpp#_ZN8daTree_c* counterparts. That was wrong: overrides is a historical credit ledger and legitimately retains path#symbol keys whose file is long gone -- it already carries hundreds of src/_ZN*.c per-function paths from earlier consolidations. Resolve by union, as the ledger's own contents require. --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Promotes the ROM-proven daPropeller_Heyho_c / FlyGuy overlay unit into one compiler-owned production translation unit. Twenty-seven legacy source files become
src/actors/daPropeller_Heyho_c.cpp, owning text0x0211f000..0x02120570and data0x02123120..0x021231e4. The compiler emits D1/D0, RTTI, the type name, and the vtable;FlyGuy_Spawnremains the evidence-bounded C-ABI factory seam.Reviewer follow-up:
// @symbolmarkers, so every intended legacy member is scoredFlyGuy_ChangeStateconverted-backslide waiverdata_ov070_02123184anddata_ov070_021231a0Fresh validation:
d1506e90efae5e2d2cf119926a4ac2a291bd5ca78349d09d5024e1a918c478e8The expected squash-attribution changes are covered by the
attribution-overridelabel. No claims, attempt-ledger, or dashboard artifacts are included.