Skip to content

tools: add tu_promote and tu_order_check, the TU promotion pair - #1990

Merged
andrewboudreau merged 1 commit into
mainfrom
tools/tu-promote-order-check
Aug 30, 2026
Merged

tools: add tu_promote and tu_order_check, the TU promotion pair#1990
andrewboudreau merged 1 commit into
mainfrom
tools/tu-promote-order-check

Conversation

@andrewboudreau

Copy link
Copy Markdown
Collaborator

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's PR validation failure 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.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. This makes that visible before a promotion is attempted.

tu_promote.py performs the mechanical half of a promotion and guesses at nothing: collapse the per-function delinks.txt entries into one complete entry spanning the manifest's sections; git mv the src_tu/ source to its production path (R100, so credit follows); git rm every legacy_source; rewrite the entry to status: promoted; add 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 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.py deltas. 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 base

  • tu_promote --dry-run ov002/BlueCoinSwitch ov002/Exit ov045/PoleLift → all three resolve to their production paths; ov100/daObjPathLift_c is correctly refused as already promoted.
  • tu_order_check ov002/BlueCoinSwitch ov045/PoleLift → compiles both, reports emitted D0, D1 plus an extra D2 against the wanted D1, D0.
  • pytest tools/test_objisolate.py test_rombuild.py test_tubuild.py test_romdata_check.py107 passed.
  • rombuild.py -j 16 --no-rom106/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

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

tangos-validator Bot commented Aug 30, 2026

Copy link
Copy Markdown

✅ PR validation — Passed

noverify: no source/build-data changes in this PR

Each changed src/*.c|*.cpp is compiled and its relocated bytes compared to the binary data on a private build box. Passing requires every changed file to reproduce the ROM byte-for-byte with correct relocation targets — this catches WRONG-DEST relocations and non-reproducing near-misses that ledger-scoped linkcheck skips.

@andrewboudreau
andrewboudreau merged commit 6b243ba into main Aug 30, 2026
4 of 5 checks passed
@andrewboudreau
andrewboudreau deleted the tools/tu-promote-order-check branch August 30, 2026 05:13
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
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