TU: fix stale legacy_source extensions in the manifest - #2016
Conversation
38 `functions[].legacy_source` values across 19 manifest entries still pointed at `src/<name>.c` after the file was migrated to `src/<name>.cpp`. Each stale row blocks `tu_promote` on that entry. Re-derived against current main: a row is stale iff the `.c` path is absent from the tree and the `.cpp` sibling is present. Manifest JSON only; no tooling touched. 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. |
|
Reviewed and queued to merge — the derivation is the right one (re-derived against This supersedes the one salvageable hunk of #1884, which I closed along with #1880/#1882 (see #1880 for why: its Separate follow-up, not for this PR: the same scan found 75 other |
…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. tangosdev#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
What
38
functions[].legacy_sourcevalues across 19 TU manifest entries still point atsrc/<name>.cwhen the file on disk issrc/<name>.cpp— the file was migrated C -> C++ and the manifest row was never updated.Each stale row blocks
tu_promoteon that entry: the promote path resolveslegacy_sourceto a file that no longer exists.Derivation
Re-derived against current
origin/main(25cfc136) by script, not carried over from an older tree. For everyconfig/tu_manifest.d/**/*.json, everylegacy_sourceending in.cwas checked againstgit ls-tree -r --name-only origin/main. A row counts as stale iff the.cpath is absent and the.cppsibling is present..cpplegacy_sourcewhose file is actually.c): 0 — nothing to fix there.data[].legacy_sourcerows were affected; all 38 are underfunctions[].Re-running the same derivation after the change reports 0 stale rows.
Entries changed
arm9/Actor.jsonarm9/ActorDerived.jsonov002/Platform.jsonov002/Warp.jsonov009/CastleWater.jsonov009/Flag.jsonov009/MetalNet.jsonov010/LightBeam.jsonov010/PeachPainting.jsonov014/ShutterBob.jsonov019/IceSlideManager.jsonov019/RacingPenguin.jsonov020/HauntedChair.jsonov029/CageLift.jsonov029/RotatingPlatformWdw.jsonov029/SwitchActivatedPlank.jsonov029/WaterDiamond.jsonov062/KoopaFlag.jsonov077/Spiny.jsonShape
Manifest JSON only — no tooling, no source, no config outside
config/tu_manifest.d/. Applied as a targeted textual substitution of the specific"legacy_source": "src/X.c"strings rather than ajson.load/json.dumpround-trip, so indentation, key order and trailing newlines are byte-identical outside the 38 changed values.git diff --statis 19 files, 38 insertions, 38 deletions; every changed line pair is verified to be an exact.c->.cppswap on the same base name and nothing else.python tools/tu_manifest.py liststill succeeds and still reports 89 TUs, unchanged from main.Provenance
Salvaged from the closed PR #1884 — this is the one clean, mechanical win in it. It was re-derived from current main rather than cherry-picked, because #1884's row set was computed against a 411-commit-old tree. This supersedes the salvageable part of #1884.
🤖 Generated with Claude Code
https://claude.ai/code/session_01WdCK1xgrJdiJzPCh3bAJfQ