config: repoint 12 stale legacy_source rows left behind by symbol renames - #2021
Conversation
…ames Mechanism. `legacy_source` on a TU manifest function row names the per-function `src/` file that function is delinked from. When a `func_ovNNN_AAAAAAAA` symbol is later named, the delinks entry and the file are renamed together to the mangled spelling -- but the manifest row is not, so its `legacy_source` keeps pointing at a path that no longer exists. #2016 fixed the rows whose `.c` had become a `.cpp`; these are the residue where NEITHER extension exists because the stem itself changed. Derivation. Over the 89 entries in config/tu_manifest.d there are 1108 `legacy_source` rows (only `functions` rows carry the field; `data`/`bss` never do). On d773264: 0 stale (.c row / .cpp on disk), 0 inverse (.cpp row / .c on disk), 80 dangling (neither extension present). Resolution rule -- by address, never by name. For each dangling row, parse the module's config/arm9/overlays/<mod>/delinks.txt (config/arm9/delinks.txt for arm9) and find the entry whose section range STARTS at the row's `address`. All 12 rows fixed here resolved to exactly one entry, and in every case the match was exact on both ends -- start == address and end == address + size -- and the entry is still marked `complete`. No range moved and none was handed back to the cartridge; this is stale bookkeeping only. Example: ov020/HauntedChair's `func_ov020_021129dc` (0x021129dc + 0x124) is owned by src/_ZN12HauntedChair6State3Ev.cpp, complete, 0x021129dc-0x02112b00. Promoted entries are deliberately left alone -- 68 of the 80. All six `promoted` entries in the manifest are 100% dangling; none is mixed, so leaving them is self-consistent per entry. Dangling there is the designed terminal state, not a defect: notes/translation-unit-reconstruction-plan.md section 6 lists the field as "legacy source paths to remove on promotion", and tools/tu_promote.py `git rm`s every `legacy_source` as part of promoting, collapsing the N per-function delinks entries into one spanning `complete` entry at `promoted_source`. Repointing them would also be actively harmful: tu_promote's rewrite_attribution keys one attribution override per absorbed symbol off the legacy path's STEM (`lineage.get("src/" + stem)`), so collapsing 25 distinct stems onto one TU path would destroy that 1:1 symbol->author mapping. Resolving them by address confirms the reading -- only the first function of each promoted TU resolves at all (its address is the promoted span's start, and it maps to `promoted_source`); the other 62 interior addresses are no longer entry starts, exactly as promotion intends. Counts: dangling 80 -> 68 (all 68 promoted, deliberate). stale 0 -> 0, inverse 0 -> 0. Every row on a non-promoted entry now resolves to a path that exists, with no duplicate `legacy_source` introduced inside any entry. Config only: 12 changed lines across 4 manifest files, patched in place so the 2-space indent, LF endings and trailing newline are byte-preserved. No delinks.txt, symbols.txt, relocs.txt, src/, src_tu/, include/ or tools/ change, so no gate input moves and no rebuild is implied. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WdCK1xgrJdiJzPCh3bAJfQ
✅ PR validation — PassedCommitted merge introduces no reconstruction or attribution regression. Full merge validation
Byte-verified means the range carries 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. |
|
Gatekeeper check on the 12 repointed rows, independent of the derivation: every new path exists on disk (12/12), and the scope is Agreed on leaving the 68 promoted-entry rows alone: Filed the Cleared; merging when |
|
Correction to my note above, before anyone chases it: the
The So the delinks gap at The Nothing here changes my clearance of the 12 rows. |
What this is
legacy_sourceon a TU-manifest function row names the per-functionsrc/file thatfunction is delinked from. When a
func_ovNNN_AAAAAAAAsymbol is later named, thedelinks entry and the file get renamed together to the mangled spelling — but the
manifest row does not, so its
legacy_sourcekeeps pointing at a path that no longerexists. #2016 fixed the rows whose
.chad become a.cpp. This is the residue: rowswhere neither extension exists, because the stem itself changed.
Derivation
Across the 89 entries in
config/tu_manifest.d/**/*.jsonthere are 1108legacy_sourcerows (onlyfunctionsrows carry the field —dataandbssneverdo). Classified against
git ls-tree -r origin/main:.crow,.cppon disk.cpprow,.con diskAddress-resolution rule
For each dangling row, parse the module's
config/arm9/overlays/<mod>/delinks.txt(
config/arm9/delinks.txtforarm9) and take the entry whose section range startsat the row's
address. Never guessed from the symbol name.All 12 rows fixed here resolved to exactly one entry, and every match was exact on
both ends —
start == addressandend == address + size— with the entry stillmarked
complete. No range moved; none was handed back to the cartridge. This is stalebookkeeping, not lost coverage.
ov020/HauntedChairov029/WaterDiamondov010/PeachPaintingov002/Enemyov070/daKrpa_c(promoted)ov070/daKpFr_c(promoted)ov100/daObjPathLift_c(promoted)ov002/daObjAbuku_c(promoted)arm9/ActorDerived(promoted)arm9/ActorBase_SceneNode(promoted)Representative resolution:
The promoted entries are deliberately left alone (68 of the 80)
All six
promotedentries in the manifest are 100% dangling — every row, not some.None is mixed, so leaving them untouched is fully self-consistent per entry, and there
is no other promoted entry establishing a different convention to match.
Dangling there is the designed terminal state, not a defect:
notes/translation-unit-reconstruction-plan.md§6 lists the field as"legacy source paths to remove on promotion".
tools/tu_promote.pygit rms everylegacy_sourceas part of promoting, andreplaces the N per-function delinks entries with one spanning
completeentry atpromoted_source. It rewritesstatusandsourceand deliberately leaveslegacy_sourceas the historical record.Repointing them would also be actively harmful:
tu_promote.rewrite_attributionwrites one attribution override per absorbed symbol, keyed off the legacy path's
stem (
lineage.get("src/" + stem)). Collapsing 25 distinct stems onto one TU pathwould destroy that 1:1 symbol→author mapping.
Resolving them by address confirms the reading: only the first function of each
promoted TU resolves at all — its address is the promoted span's start and it maps to
promoted_source(e.g._ZN8daKrpa_cD1Ev@0x02121118→src/actors/daKrpa_c.cpp.text 0x02121118-0x02121b48). The other 62 interior addresses are no longer entrystarts, exactly as promotion intends.
Post-change verification
Re-derived on the resulting tree via
python tools/tu_manifest.py export:promotedentries, named individually above — the setdeliberately left;
legacy_sourceon a non-promoted entry resolves to a path that exists;legacy_sourceintroduced within any entry;delinks.txt— theprecondition
tu_promote.pychecks, which a dangling row would have failed. Fixingthese unblocks promotion for these four entries.
No delinks / symbols / relocs / src / src_tu / include / tools changes
12 changed lines, one per row, patched in place — the 2-space indent, LF endings
and trailing newline are byte-preserved and no unrelated line reflows. Nothing here is
a gate input, so no rebuild is implied. Pre-push passed unaided (
port_refcheck405references,
duplicate-sources11250 stems,check_src_tu_compiles89/89).Unrelated pre-existing finding (not touched here)
While asserting the
tu_promoteprecondition tree-wide, one row outside the 80 failsit:
ov009/Bird→func_ov009_0211145c. Itslegacy_sourcesrc/func_ov009_0211145c.cdoes exist on disk, but has zero entries inconfig/arm9/overlays/ov009/delinks.txt— there is a gap at0x0211145c-0x021115d8(0x17c bytes, exactly the manifest's declared size) between the entries ending and
starting on either side. So the manifest licenses a range that is still handed back to
the cartridge. It predates this branch (also absent on
origin/main) and fixing itwould mean touching
delinks.txt, so it is out of scope here — flagging it for afollow-up. Tree-wide, 105
src/files are unenrolled in anydelinks.txt, but theother 104 are SDK/BIOS stubs (
CpuFastSet.c,BitUnPack.c, …) that no manifest rowclaims; this is the only one a manifest entry points at.