tools: add tu_promote and tu_order_check, the TU promotion pair - #1990
Merged
Conversation
Both come from the #1880 -> #1914 promotion chain, which bundled them with the manifest rows and sources they produce. The PR validator restores all of `tools/` from base, so no PR in that chain could ever test its own tool change -- every one of them is UNSTABLE for that reason alone. These are lifted out and land alone, off main, so the content PRs that follow are judged against tools that are already there. `tu_order_check.py` compiles a manifest TU once and reports, per licensed function, bytes and emission order. Production isolation places an object's `.text` into the spanning delink in emission order, so when a TU defines its destructor out of line and mwcc emits `D2, D0, D1` against the cartridge's `D1, D0`, the range does not link. The tool makes that visible before a promotion is attempted rather than after. `tu_promote.py` performs the mechanical half of a promotion and guesses at nothing: it collapses the per-function `delinks.txt` entries into one `complete` entry spanning the manifest's sections, `git mv`s the `src_tu/` source to its production path (R100, so credit follows), `git rm`s every `legacy_source`, rewrites the entry to `status: promoted`, and adds one `attribution.json` override per absorbed symbol. It compiles nothing -- the proof of a promotion is `rombuild.py` reporting 106/106 with `mismatching: 0` over the whole batch. The chain's `objisolate.py`/`rombuild.py`/`romdata_check.py` deltas are deliberately NOT carried: they are the predecessor of the deadstrip-data work that has since landed as #1977, #1987 and #1989, and main's versions supersede them. The remaining three files are comment-only, correcting paths that the promotion moves. Verified against main at c05617c, not against the chain's 350-commit-old base: `tu_promote --dry-run ov002/BlueCoinSwitch ov002/Exit ov045/PoleLift` resolves all three to their production paths, and refuses `ov100/daObjPathLift_c` as already promoted; `tu_order_check ov002/BlueCoinSwitch ov045/PoleLift` compiles both and reports the `D0, D1` + extra `D2` against the wanted `D1, D0`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QhhAeJwXBnfPp7B5DNjCwh
✅ PR validation — Passednoverify: no source/build-data changes in this PR Each changed |
This was referenced Aug 30, 2026
Alberto12345678999
pushed a commit
to Alberto12345678999/sm64ds-decomp
that referenced
this pull request
Aug 30, 2026
… branch pr-validate.yml has exactly one trigger, `pull_request_target:`. There is no `push: branches: [main]`. Nothing builds a ROM after a merge, and every PR is judged against its own base -- so two PRs each green against base X can combine into a red main. That has happened twice in four days (tangosdev#1987, tangosdev#1990). This runs the toolchain-free gates on `git merge-tree --write-tree origin/main <head>` and diffs the verdicts against origin/main. It exits non-zero ONLY when a gate is green on the base and red on the merge result. A gate already red on the base is reported as `pre-existing` and can never fail the run: a tool that blames the wrong PR trains people to ignore it. Gates, all verified toolchain-free by reading them: converted-ratchet, dead-references, duplicate-sources, header-offsets, layout-check, src-tu-refs, and source-coverage as a byte-set delta. rombuild.py and eligible.py are excluded by construction -- build/ is shared between worktrees. Advisory. Nothing in .github/workflows/ runs this. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WdCK1xgrJdiJzPCh3bAJfQ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Lifted out of the #1880 → #1882 → #1884 → #1914 promotion chain, which bundled these two tools with the manifest rows and sources they produce. The PR validator restores all of
tools/from base, so no PR in that chain could ever test its own tool change — every one of them is UNSTABLE, and #1880'sPR validationfailure is exactly this. Landing the tools alone, off main, lets the content PRs that follow be judged against tools that are already there.What's here
tu_order_check.pycompiles a manifest TU once and reports, per licensed function, bytes and emission order. Production isolation places an object's.textinto the spanning delink in emission order, so when a TU defines its destructor out of line and mwcc emitsD2, D0, D1against the cartridge'sD1, D0, the range does not link. This makes that visible before a promotion is attempted.tu_promote.pyperforms the mechanical half of a promotion and guesses at nothing: collapse the per-functiondelinks.txtentries into onecompleteentry spanning the manifest's sections;git mvthesrc_tu/source to its production path (R100, so credit follows);git rmeverylegacy_source; rewrite the entry tostatus: promoted; add oneattribution.jsonoverride per absorbed symbol. It compiles nothing — the proof of a promotion isrombuild.pyreporting 106/106 withmismatching: 0over the whole batch.The remaining three files are comment-only, correcting paths that promotion moves.
What is deliberately NOT carried
The chain's
objisolate.py/rombuild.py/romdata_check.pydeltas. They are the predecessor of the deadstrip-data work that has since landed as #1977, #1987 and #1989; main's versions supersede them. Carrying them would revert main.Verification — against main at
c05617cf2, not the chain's 350-commit-old basetu_promote --dry-run ov002/BlueCoinSwitch ov002/Exit ov045/PoleLift→ all three resolve to their production paths;ov100/daObjPathLift_cis correctly refused as already promoted.tu_order_check ov002/BlueCoinSwitch ov045/PoleLift→ compiles both, reports emittedD0, D1plus an extraD2against the wantedD1, D0.pytest tools/test_objisolate.py test_rombuild.py test_tubuild.py test_romdata_check.py→ 107 passed.rombuild.py -j 16 --no-rom→ 106/106 exact, 100.000000%, mismatching: 0, PASS, 7,812 ROM-data records — identical to main, as an additive tools change should be.🤖 Generated with Claude Code