feat(dev): test a change against a base, as a pack of what it changes #2732
Open
github-sync
wants to merge 20 commits from
github/pr/2732 into main
pull from: github/pr/2732
merge into: MagicBeanDev:main
MagicBeanDev:main
MagicBeanDev:github/pr/2893
MagicBeanDev:github/pr/2890
MagicBeanDev:github/pr/2887
MagicBeanDev:github/pr/2878
MagicBeanDev:github/pr/2875
MagicBeanDev:github/pr/2874
MagicBeanDev:github/pr/2873
MagicBeanDev:github/pr/2871
MagicBeanDev:github/pr/2756
MagicBeanDev:github/pr/2897
MagicBeanDev:github/pr/2896
MagicBeanDev:github/pr/2718
MagicBeanDev:github/pr/2842
MagicBeanDev:github/pr/2691
MagicBeanDev:github/pr/2895
MagicBeanDev:github/pr/2743
MagicBeanDev:github/pr/2765
MagicBeanDev:github/pr/2708
MagicBeanDev:github/pr/2755
MagicBeanDev:github/pr/2817
MagicBeanDev:github/pr/2876
MagicBeanDev:release-please--branches--main
MagicBeanDev:release-please--branches--main--release-notes
MagicBeanDev:github/pr/2778
MagicBeanDev:github/pr/2888
MagicBeanDev:github/pr/2866
MagicBeanDev:github/pr/2877
MagicBeanDev:github/pr/2800
MagicBeanDev:github/pr/2829
MagicBeanDev:github/pr/2850
MagicBeanDev:github/pr/2889
MagicBeanDev:github/pr/2882
MagicBeanDev:github/pr/2860
MagicBeanDev:github/pr/2849
MagicBeanDev:github/pr/2886
MagicBeanDev:github/pr/2794
MagicBeanDev:github/pr/2840
MagicBeanDev:github/pr/2830
MagicBeanDev:github/pr/2834
MagicBeanDev:github/pr/2719
MagicBeanDev:github/pr/2742
MagicBeanDev:github/pr/2741
MagicBeanDev:github/pr/2820
MagicBeanDev:github/pr/2771
MagicBeanDev:github/pr/2859
MagicBeanDev:github/pr/2858
MagicBeanDev:github/pr/2823
MagicBeanDev:github/pr/2757
MagicBeanDev:github/pr/2769
MagicBeanDev:github/pr/2844
MagicBeanDev:github/pr/2843
MagicBeanDev:github/pr/2673
MagicBeanDev:github/pr/2855
MagicBeanDev:github/pr/2739
MagicBeanDev:github/pr/2807
MagicBeanDev:github/pr/2851
MagicBeanDev:github/pr/2789
MagicBeanDev:github/pr/2814
MagicBeanDev:github/pr/2818
MagicBeanDev:github/pr/2774
MagicBeanDev:github/pr/2772
MagicBeanDev:github/pr/2750
MagicBeanDev:github/pr/2786
MagicBeanDev:github/pr/2785
MagicBeanDev:github/pr/2782
MagicBeanDev:github/pr/2779
MagicBeanDev:github/pr/2775
MagicBeanDev:github/pr/2706
MagicBeanDev:github/pr/2766
MagicBeanDev:github/pr/2747
MagicBeanDev:github/pr/2759
MagicBeanDev:github/pr/2745
MagicBeanDev:github/pr/2740
MagicBeanDev:github/pr/2733
MagicBeanDev:github/pr/2725
MagicBeanDev:github/pr/2727
MagicBeanDev:github/pr/2728
MagicBeanDev:github/pr/2729
MagicBeanDev:github/pr/2721
MagicBeanDev:github/pr/2722
MagicBeanDev:github/pr/2715
MagicBeanDev:github/pr/2714
MagicBeanDev:github/pr/2707
MagicBeanDev:github/pr/2703
MagicBeanDev:github/pr/2705
MagicBeanDev:github/pr/2697
MagicBeanDev:github/pr/2679
MagicBeanDev:github/pr/2676
MagicBeanDev:github/pr/2669
MagicBeanDev:docs/what-hookshot-connects-to
MagicBeanDev:fix/hdr-encode-test
MagicBeanDev:fix/main-tests
MagicBeanDev:fix/play-cache-files
MagicBeanDev:perf/game-barrier-order
MagicBeanDev:feat/heap-lazy
MagicBeanDev:feat/texture-detail
MagicBeanDev:perf/astc-opaque-bc1
MagicBeanDev:fix/fs-dir-lock
MagicBeanDev:fix/framegen-native-kept
MagicBeanDev:fix/update-no-downgrade
MagicBeanDev:port/k10-gcn-f16-mirrors
MagicBeanDev:fix/framegen-mult-cap
MagicBeanDev:chore/release-as-2.1.1
MagicBeanDev:perf/pcache-load-switch
MagicBeanDev:fix/prepare-progress-log
MagicBeanDev:fix/update-closes-stale-game
MagicBeanDev:fix/hdr-scaled-sampled
MagicBeanDev:perf/first-start-preset
MagicBeanDev:perf/zero-upload
MagicBeanDev:fix/vcore-escape-2s
MagicBeanDev:test/fake-gh-atomic
MagicBeanDev:perf/batch-idle-flush
MagicBeanDev:test/finish-stub-a
MagicBeanDev:chore/record-2.1.0
MagicBeanDev:fix/main-red-tests
MagicBeanDev:fix/test-display-startup-driver-cache
MagicBeanDev:feat/mesh-shader-mode
MagicBeanDev:fix/menu-probes-at-startup
MagicBeanDev:fix/fps-counter-classic
MagicBeanDev:fix/test-log-batch-flake
MagicBeanDev:fix/narrow-screen-hud
MagicBeanDev:ci/prereleases
MagicBeanDev:release/v2.0.15
MagicBeanDev:hotfix/v2.0.15
MagicBeanDev:fix/present-ahead-replay
MagicBeanDev:release/v2.0.13
MagicBeanDev:fix/bracket-paths-2013
MagicBeanDev:fix/hdr-overlays-2013
MagicBeanDev:fix/fps-counter-scale-2013
MagicBeanDev:fix/stick-seen-at-rest
MagicBeanDev:hotfix/menu-fits-screen
MagicBeanDev:fix/f1-always-opens-menu
MagicBeanDev:fix/ini-keep-lines
MagicBeanDev:claude/cranky-pike-7cf247
MagicBeanDev:perf/texture-answer-flat
MagicBeanDev:fix/stamp-test-modules
MagicBeanDev:fix/appimage-fonts
MagicBeanDev:fix/display-awake
MagicBeanDev:release/v2.0.12
MagicBeanDev:fix/discord-windows
MagicBeanDev:rc/v2.0.12-tests
MagicBeanDev:fix/dlss-keep-feature
MagicBeanDev:rc/v2.0.12
MagicBeanDev:button-icons
No reviewers
Labels
Clear labels
accessibility
Barrier affecting people with disabilities
autorelease: pending
autorelease: tagged
bug
Something isn't working
dependencies
Pull requests that update a dependency file
documentation
Improvements or additions to documentation
duplicate
This issue or pull request already exists
enhancement
New feature or request
github_actions
Pull requests that update GitHub Actions code
good first issue
Good for newcomers
help wanted
Extra attention is needed
invalid
This doesn't seem right
question
Further information is requested
testing wanted
wontfix
This will not be worked on
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
question
testing wanted
wontfix
Milestone
Clear milestone
No items
No milestone
Assignees
Clear assignees
No assignees
1 participant
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Hookshot!2732
Loading…
Reference in a new issue
No description provided.
Delete branch "github/pr/2732"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Testing a change in the game means a new full build: a new work folder translates the game again, and a new game folder prepares every shader from cold, 69 minutes in all on an RX 9070 XT. Most of that is the same for every change. This adds a development tool that builds the game once as a base and each change as a small pack over it.
What changed
scripts/dev_packs.py(new, development only):basebuilds a checkout at a commit.--seedcopies an older game folder's shader caches first, as an update keeps them.packbuilds a change. Commits, uncommitted edits and untracked files all count.RX_VK_ONLY, seconds): when every changed file is a renderer file the game isn't built from. Those files are found in the checkout fromhost/'s includes andbuild.py's dependencies.make_game.py, which builds both modules.objs.rsp,vk_objs.rsp) differs from the base's. Bytes can't decide this:nx2/version.pyputs a fingerprint of every source into both modules.nx2/, or a non-C file changed.rtmakes a runtime: the base withromfshard-linked and the other files copied, the packs' files over them. It runs a prepare when the shader translator or the seed changed. Two packs that bring the same file are refused, and so is a pack built on another base.docs/development.md: a new section, Testing a change against a base.AGENTS.md: one bullet pointing agents to it.make_game.pyandbuild.pyas they are; no build or CI file is touched.Testing
tests/test_dev_packs.py(no game):build.py's stamp, and that the version-stamped objects arebuild.py's;Ten review rounds with gpt-6.1-sol, every finding fixed; the last found none.
End to end on Windows (Ryzen 7 5700X3D, RX 9070 XT), packs over a full build of
57ed3aa8:host/nvn_native.chookshot.exehost/nvdrv.c,vk_memory.cppvk_replay.cpphookshot_vk.dllEach pack's modules are byte-identical to that change's own earlier full build, apart from 2 PE header bytes. The renderer-only route gives the same DLL as
make_game.pyfor the same tree.Seeded base on main
46780503from 57ed3aa8's caches:57ed3aa8cold.In the game: a runtime made by
rtfrom two packs over57ed3aa8(the host-only pack's exe, with its new switches off, and the renderer-only pack's DLL):Not tested: Linux. The module names there are
hookshotandhookshot_vk.so, andRX_TARGET=windowsuses the Windows names; neitherpacknorrthas run there.Related: the dev-loop proposal (#2636). This covers part of its "middle loop" without changing the build.
🤖 Generated with Claude Code
Thanks, this is a careful tool, and the end-to-end table (byte-identical modules against full builds, the seeded base time, a played runtime) is the evidence I wanted for something like this. I read the script and the docs and did not run anything. CI is green. Two things I'd like before merge, both small.
1. Say plainly that packs and runtimes stay on the PC. A pack is not a patch of source:
packs/<name>/holds the builthookshot/hookshot_vkmodules (the lifted game is linked into them) andfiles/, the generated files that differ from the base (shader seed, prompts, anything the build writes into romfs). Aruntimeis a whole game folder with the romfs hard-linked. "Pack" is a word people will hear as "something I can send a tester", and the new docs section never says no. CONTRIBUTING.md's main rule is that nothing built from the game leaves the PC, and the DLSS section there spells the same thing out in one sentence for its own files. Please add one to Testing a change against a base (and a few words to the AGENTS.md bullet): a base, a pack and a runtime are built from the game and are never attached, uploaded, or sent to a tester or the chat. What you share is the branch, and the tester builds their own.2.
rttrustspack.json's paths.generated,deletedandfilesare joined onto the runtime folder withos.path.join(rt, rel)and then written, or removed withos.remove(the "replaced, never written through" branch), with no check thatrelis relative and stays insidert. Today the script writes those lists itself, so it is not reachable by accident. But a pack folder is a plain folder with a JSON file, and as soon as someone does send one around (point 1), an absolute path or a..in it deletes or overwrites a file outside the runtime. A one-line guard incmd_rt(reject absolute paths and anything whoserealpathis not underrt) closes it, and it deserves a refusal case intest_guardslike the others. Same formeta["files"]keys.The rest of the safety side looks fine to me: every subprocess is a list (no shell) and the Python children run with
-I; there is no network use;packvalidates its name before itsrmtree;rtrefuses an existing folder and one that overlaps the base's folders;romfsis hard-linked and the pack's files replace the link, not the base's file;baserefuses a non-empty game folder. Hard links with a copy fallback should behave on both NTFS and Linux; you say Linux wasn't run, and I agree that's the part to watch (hookshot/hookshot_vk.so,RX_TARGET), so please keep that line in the description.Overlap with the dev loop (#2636).
nx2/dev.pyandtests/scenarios/are only in that proposal, not on main, so nothing conflicts today. This lands squarely in its "middle loop" (build less, start warm) and takes a different route from it: a base plus a diff instead of a faster incremental build in one work folder. It sits inscripts/as a standalone tool, which is where the proposal says standalone tools go, and it could later becomedev.py base|pack|rtwithout a rewrite. I would not hold this for that.scripts/test_requests.pyis unrelated (asks the team for testers). The existingRX_HOT_OPTsection ends with "never use it for numbers"; this one is the opposite, for A/B numbers against a base, and the doc says so ("compare with a runtime made byrtfrom no pack"), which reads fine side by side.AGENTS.md. The facts match the docs section, and the point about agents sharing one base is useful. It is a 7-line bullet in a list of one-liners, though: nit, cut it to the pointer plus the two rules that matter for agents (one shared base, agree before moving it; development and A/B only), and the new "never leave the PC" sentence. It does not collide with the "tested in the game, with measurements" rule from #2712: it is about how to build for a test, and the description is honest that its own 40.4-41.9 fps run is not a measurement.
Tests. They are meaningful without a game: they cut the real
nx2/build.pyandmake_game.pytext to check the translator list, the stamped objects and the renderer files the host reads, and build a fake base, packs and runtime in a temp folder, with the refusals checked by their own reason. Good, and as the repo's habit goes, they will need updating together with those scripts.Description: it has a real end-to-end use, on a named GPU. Nothing else to ask there.
Thanks, all three are done in
c3f666f6:rtandpack.json. Before anything is made,rtnow refuses a pack whosefilesnames anything but the two modules, and anygeneratedordeletedpath that is absolute or resolves (realpath) outside the runtime.test_guardshas four refusal cases: an absolute path,../x,romfs/../../x, and a module key../x. Each checks that nothing was made and that the file outside is untouched.The Linux line stays in the description.
Extra hardening of point 2 in
ac5af7b0and13075852:rtnow judges a pack path by its text first (no.., not rooted, no:stream names), then re-checks each destination where it is written, once the runtime exists, by file identity through links, so a link cloned from the base's romfs can't lead a write or delete outside the runtime; tests cover each case.I merged
13075852into current main and ran tests/test_dev_packs.py, which passes. The two red jobs are not from this PR. One is test_release_scripts.py (test_nightly_two_runs_at_once_end_at_the_newer_commit), which is flaky. The other is test_pipeline_cache_save.py on Windows part 1, a timing wait on the in-play save. This PR only adds scripts/dev_packs.py, its test and docs, so a re-run should clear both.hckrman101's points are all addressed. Docs and AGENTS.md now say plainly that bases, packs and runtimes never leave the PC.
rtrefuses absolute paths,..paths and module keys other than the two modules, and re-checks each destination by file identity, and test_guards has a refusal case for each. The AGENTS.md bullet is down to three lines. I also readclear_outputand thepackrmtree: both only work inside the dev folder, and the pack name is validated before the delete.The one gap is Linux (
hookshot/hookshot_vk.so), which the description already states. That's fine for a development tool.65a4d874) for #2822's fix of the flaky nightly test ddf7bba29f3c2d80cf) for #2861 and #2883's test fixes b49b89cf6d