Skip to content

TU: fix stale legacy_source extensions in the manifest - #2016

Merged
andrewboudreau merged 1 commit into
mainfrom
tu/legacy-source-extensions-2
Aug 30, 2026
Merged

TU: fix stale legacy_source extensions in the manifest#2016
andrewboudreau merged 1 commit into
mainfrom
tu/legacy-source-extensions-2

Conversation

@andrewboudreau

Copy link
Copy Markdown
Collaborator

What

38 functions[].legacy_source values across 19 TU manifest entries still point at src/<name>.c when the file on disk is src/<name>.cpp — the file was migrated C -> C++ and the manifest row was never updated.

Each stale row blocks tu_promote on that entry: the promote path resolves legacy_source to a file that no longer exists.

Derivation

Re-derived against current origin/main (25cfc136) by script, not carried over from an older tree. For every config/tu_manifest.d/**/*.json, every legacy_source ending in .c was checked against git ls-tree -r --name-only origin/main. A row counts as stale iff the .c path is absent and the .cpp sibling is present.

  • Stale rows found: 38, across 19 files.
  • Inverse case (a .cpp legacy_source whose file is actually .c): 0 — nothing to fix there.
  • No data[].legacy_source rows were affected; all 38 are under functions[].

Re-running the same derivation after the change reports 0 stale rows.

Entries changed

entry rows
arm9/Actor.json 7
arm9/ActorDerived.json 1
ov002/Platform.json 2
ov002/Warp.json 1
ov009/CastleWater.json 2
ov009/Flag.json 2
ov009/MetalNet.json 1
ov010/LightBeam.json 2
ov010/PeachPainting.json 2
ov014/ShutterBob.json 1
ov019/IceSlideManager.json 1
ov019/RacingPenguin.json 2
ov020/HauntedChair.json 2
ov029/CageLift.json 1
ov029/RotatingPlatformWdw.json 2
ov029/SwitchActivatedPlank.json 2
ov029/WaterDiamond.json 2
ov062/KoopaFlag.json 2
ov077/Spiny.json 3

Shape

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 a json.load/json.dump round-trip, so indentation, key order and trailing newlines are byte-identical outside the 38 changed values.

git diff --stat is 19 files, 38 insertions, 38 deletions; every changed line pair is verified to be an exact .c -> .cpp swap on the same base name and nothing else.

python tools/tu_manifest.py list still 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

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
@tangos-validator

tangos-validator Bot commented Aug 30, 2026

Copy link
Copy Markdown

✅ PR validation — Passed

Committed merge introduces no reconstruction or attribution regression.

Full merge validation

Check Result
Committed test merge yes
Byte-verified functions 10,998 / 11,347 (96.92%, +0)
Byte-verified code bytes 2,045,248 / 2,211,124 (92.50%, +0)
Claimed, not byte-verified 218 functions, 60,056 bytes (+0)
Perfect source moves 0 R100
Enrolled ranges (delinks complete) 11,058 functions, 2,052,772 bytes (92.84%, +0) -- differs from byte-verified by +60
Contributor credit 0 added, 0 changed, 0 lost
Relocation check 0 checked; no affected slots
Module fidelity 106/106 exact; 100.000000% compared bytes
Code linked from verified source 11,087 functions, 2,066,772 bytes (93.47%)
Module bytes from source 2,066,772 / 3,049,600 (67.8%); 811,492 (26.6%) are data no delink entry reaches
ROM data reproduced from source 456 symbol(s) exact, 241 partial, 9 differ

Byte-verified means the range carries complete in a delinks.txt, so the ROM build compiled it and compared it to the cartridge. The 218 claimed functions have a src/ file named after the symbol with no NONMATCHING banner, and nothing compiles them -- dsd fills their addresses with the ROM's own bytes. Both together are the 11,216 this project calls matched.

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

Copy link
Copy Markdown
Collaborator Author

Reviewed and queued to merge — the derivation is the right one (re-derived against origin/main via git ls-tree, not cherry-picked from #1884's 13bf9222f, whose row set was computed against a 411-commit-old tree). Regex-targeted substitution rather than a json.dump round-trip is exactly right here; it keeps the diff at 38/38 with byte-identical formatting elsewhere.

This supersedes the one salvageable hunk of #1884, which I closed along with #1880/#1882 (see #1880 for why: its plan_many change dropped every _ZTV/_ZTI/_ZTS section uncompared, a design main deliberately rejected in favour of #1987's deadstrip-data licensing).

Separate follow-up, not for this PR: the same scan found 75 other legacy_source values pointing at paths that exist in neither extension — all of ov070/daKpFr_c.json and ov070/daKrpa_c.json, plus ov002/daObjAbuku_c.json, arm9/ActorBase_SceneNode.json, and scattered func_* rows in ov010/PeachPainting, ov020/HauntedChair, ov029/WaterDiamond. Those are entries whose one-function files were consolidated into merged TUs, so the legacy path is gone rather than renamed. That is a different and non-mechanical problem — correctly left alone here, but it wants its own look, since a dangling legacy_source is exactly the kind of pointer that reads as evidence without being any.

@andrewboudreau
andrewboudreau merged commit 645bc1d into main Aug 30, 2026
4 checks passed
@andrewboudreau
andrewboudreau deleted the tu/legacy-source-extensions-2 branch August 30, 2026 17:06
Alberto12345678999 pushed a commit to Alberto12345678999/sm64ds-decomp that referenced this pull request Aug 30, 2026
…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
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