perf(build): keep unchanged button-prompt assets in source builds #2698

Closed
github-sync wants to merge 1 commit from github/pr/2698 into main
Owner

@mooki000 opened this pull request on GitHub · 2026-11-12 06:59 UTC · original
Merged on GitHub in 467805032c

What this is for: contributors rebuilding from source should not spend another minute drawing the same button prompts. What is measured: on an Intel Core i9-13900K running Arch Linux, the finish stage at main c544828f took 67.707 and 66.898 seconds; two unchanged runs took 0.603 and 0.651 seconds, and the final head took 0.616 seconds, clearing the run-time files as well. Other work was running throughout. Not measured: Windows, a packaged Builder update, a complete compile/lift, or gameplay performance.

Refs #2636.

Problem

nx2/make_game.py removes the game folder's prompts directory and runs prompt_assets.py on every build when fontTools is available. Most source edits do not change anything those assets depend on.

This avoids drawing unchanged assets; it does not make the drawing itself faster. #2636's A1 plans to measure the finish stage, but its A1–A10 items do not already propose this prompt cache. It complements #2674 and #2675, which address unnecessary retranslation rather than prompt generation.

What changed

  • nx2/make_game.py: source-checkout builds request incremental prompt generation. Packaged builders still regenerate every time, using the finish stage's existing launcher distinction. RX_RELEASE cannot distinguish them: make_game sets it for source builds too. Missing fontTools still removes the replacement prompts, keeping the existing fallback to the game's icons.
  • nx2/prompt_assets.py: fingerprint the inputs before generating, draw into a fresh sibling directory, and record the successful outputs only if a second fingerprint still matches. Reuse requires both matching inputs and every recorded output's size and SHA-256. A failed generation still removes the prompts, as before, so the game shows its own icons. An interrupted builder can leave the previous complete set without a stamp; the next build regenerates it. The drawing, encoding and asset formats are unchanged.
  • tests/test_prompt_skip.py: game-free fixtures run the actual finish-stage prompt call and production cache/publication functions, with small authored files and a stand-in for expensive drawing.
  • docs/development.md: explain reuse, its limits, and HOOKSHOT_FORCE_PROMPTS=1. The force switch follows env_on: unset, empty, 0, false, off and no are off, ignoring case and surrounding spaces.

What prevents a wrong skip

The input record covers:

  • The four prompt source modules, including controller, keyboard and binding tables.
  • The game's icon font and optional bold label font, compression dictionary, common texture/layout archives, and matching resource-size table names and contents.
  • The repository's controller and keyboard fonts, and added, changed or removed PNG overrides.
  • Python/platform; fontTools, Pillow, ASTC encoder, texture decoder, NumPy, zstandard and archspec availability and versions; Pillow's native library versions and the selected ASTC backend.
  • Generation parameters and relevant library switches, including SOURCE_DATE_EPOCH, FREETYPE_PROPERTIES and the encoder's custom CPU definitions.

Languages and saved bindings are not build inputs here: the generator uses its source tables, and the runtime applies the selected controls.

The output record includes files and directories: the keyboard directory itself is a runtime marker. Legitimately absent optional outputs stay absent; a missing, truncated or same-size-corrupted recorded file forces regeneration. The runtime's own compressed copies and control-specific keys are excluded before hashing, not mistaken for generator output. The files the game derives at run time are still cleared by every build, including an unchanged hit: their own keys do not cover changes to host/prompts.c. Only the known runtime filenames are removed; if one cannot be cleared, the build regenerates instead of keeping on doubt.

An unreadable input cannot authorize reuse. The log gives the reason, even if a forced regeneration subsequently fails. A source edited during generation leaves no stamp, addressing the stale-input certification problem discussed in #2674's review.

How it was tested

2026-11-12; Arch Linux, kernel 7.2.15-1-omarchy, x86-64, Intel Core i9-13900K, 32 logical CPUs, Python 3.12.16. Base c544828ff; final head 2ec89de8. No game or GPU program was launched for these checks.

Finish-stage measurements

These execute the real finish-stage statements through completion, including binary/image copies, the unchanged shader-list check, pipeline-list work and prompt generation, in a disposable copy of an existing built game folder. Compilation, translation and shader preparation are not included. They are not whole rebuild times.

All optional prompt packages were present: fontTools 4.68.0, Pillow 13.0.0, astc-encoder-py 0.1.12, texture2ddecoder 1.0.6, archspec 0.2.7, NumPy 2.5.4 and zstandard 0.25.0.

Run Source Finish time 1-minute load before → after Top other work
Before 1 c544828f 67.707 s 7.13 → 7.40 Existing game 316 → 315% CPU; file indexing 66 → 79%; relay check also active
Before 2 c544828f 66.898 s 7.06 → 8.24 Existing game 315 → 318%; terminal process about 41%
First generation with cache 63d5c965 66.400 s 8.24 → 8.19 Existing game 318 → 320%; terminal process about 42%
Unchanged 1 63d5c965 0.603 s 8.19 → 8.19 Existing game 320%; terminal process about 42%
Unchanged 2 63d5c965 0.651 s 8.19 → 8.19 Existing game 320%; terminal process about 42%
Changed table source 681208dd 63.147 s 6.99 → 6.95 Existing game 320 → 321%; terminal process about 42%
Regenerate, final head 2ec89de8 65.007 s 5.77 → 5.86 Existing game 317 → 316%; terminal process about 43%
Unchanged, final head, with run-time files present 2ec89de8 0.616 s 5.79 → 5.79 Existing game 316%; terminal process about 43%

Other work was running; none was a quiet measurement. CPU percentages are lifetime averages from snapshots, not interval measurements. The first generation is included to show that this is a skip, not a faster drawing algorithm.

The unchanged runs print:

prompts: unchanged, kept

Adding a harmless comment to the button-table source forces a fresh generation:

prompts: regenerated (table changed)

The comment was removed afterwards. A timestamp-only touch does not regenerate: the key checks content. An additional run on the final head verifies an unchanged hit with all six runtime-derived files present: all six are removed, while the 16 generated originals and the stamp retain their sizes and SHA-256 values. The focused fixture also compares the retained originals byte for byte. The final timing rows above include that cleanup.

Asset parity: all 16 regenerated files, 274,704,680 bytes, compare byte for byte with a full generation by main c544828f. Only the new stamp is excluded. Both sides used the same SOURCE_DATE_EPOCH because fontTools otherwise writes the current time into the OpenType header. This comparison setting is not introduced into normal builds. A development cache hit retains the earlier font timestamp; packaged builds retain their old full-generation behavior.

Game-free checks

  • 23 focused tests pass on the final change. They cover unchanged reuse, every input kind, package versions/presence, switch/parameter changes, missing/truncated/same-size-corrupted output, empty directories, clearing runtime derivatives while retaining byte-identical originals, regeneration when runtime cleanup fails, interruption, mid-generation edits, unreadable input, force-switch parsing, incomplete output, publication failure, and the packaged/no-fontTools paths. A non-zero generator result removes prompts for both source and packaged builds; interruption before that result leaves the old set unstamped.

  • The same unchanged-build test against main's finish stage fails behaviorally: 2 != 1: unchanged finish must not draw the prompts twice.

  • Removing each of 18 guards/key components in memory makes its corresponding behavior test fail on the final change. Three later corrections also fail against the earlier version of this patch: returned failure retained old prompts in both build paths, runtime files survived a hit, and an uncleared runtime file did not force regeneration. The unreadable-input diagnostic has a separate before-fails/after-passes proof.

  • The whole suite one commit earlier, at f20ea58b (before a failed generation was made to remove the prompts again and the run-time files to be cleared), python -I -B tests/run_all.py --jobs 8:

    472 passed, 0 failed, 20 skipped in 133 s (linux, 8 at once)
    

    Load was 7.37 → 19.48. The first full run, at 32 checks at once, was 471 passed, 1 failed, 20 skipped: a clang++ child compiling texture_pass.cpp for nx2/test_depth_sample_types.py died with SIGKILL. That fixture and its input trees are unchanged from main. No kernel OOM line was found, so the cause is not established. The complete run with eight workers passes, including that check. The failed run is retained, not counted as a pass. On the final head 2ec89de8 the focused checks, the baseline failure and all guard mutations were rerun, and the whole suite once more with eight workers: 471 passed, 1 failed, 20 skipped in 136 s (load 11.14 → 22.62). The one failure is tests/test_release_scripts.py, test_nightly_two_runs_at_once_end_at_the_newer_commit, the intermittent race reported in #2626; this change touches none of its files.

  • No core dumps were recorded. Nothing generated from the game is in this change.

Not tested

  • Windows filesystem behavior or a Windows build.
  • The packaged Windows Builder or Linux AppImage build/update end to end. Their always-regenerate selection is covered by a game-free test only.
  • A full compile/lift or a new gameplay run, other CPUs, or gameplay/frame-rate benefits.

No screenshots: the regenerated asset bytes match, and no visual change is intended.

Risks

The input list must grow when the generator gains new file dependencies or outputs outside the prompt directory. A regression flags new local module imports, but it does not replace reviewing new file reads. Generation temporarily needs room for both old and new assets. Publication uses completed directories and rollback on a rename error; interruption cannot create a stamped half-set, though a process killed between the two directory renames may require regeneration on the next build.

Overlaps

The open-PR scan found 47 PRs touching one of these files. The actual conflict hunks were read:

  • #2151: adjacent item-button generation conflicts with the finish-stage prompt call/error branch. Preserve both steps and account for added prompt dependencies.
  • #2261: substantive overlap. It adds --wheel-icons, another input folder and output outside prompts. Keep that work outside this skip or extend the input/output proof and staging; accepting both hunks mechanically is not enough.
  • #1872, #1958, #2304, #2317, #2430, #2465, #2474, #2478, #2608, #2673, #2675 and #2686 share code files but merged cleanly in the scan. That is not proof that any newly added generator dependency is covered.

Assisted-by: GPT-6 Astra
Co-Authored-By: GPT-6 Astra [email protected]

> [@mooki000](https://github.com/mooki000) opened this pull request on GitHub · 2026-11-12 06:59 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2698) > **Merged on GitHub** in https://git.magicbean.dev/MagicBeanDev/Hookshot/commit/467805032c70d21cb1e5e633faf7eb556b02935f **What this is for:** contributors rebuilding from source should not spend another minute drawing the same button prompts. **What is measured:** on an Intel Core i9-13900K running Arch Linux, the finish stage at main `c544828f` took 67.707 and 66.898 seconds; two unchanged runs took 0.603 and 0.651 seconds, and the final head took 0.616 seconds, clearing the run-time files as well. Other work was running throughout. **Not measured:** Windows, a packaged Builder update, a complete compile/lift, or gameplay performance. Refs #2636. ## Problem `nx2/make_game.py` removes the game folder's `prompts` directory and runs `prompt_assets.py` on every build when fontTools is available. Most source edits do not change anything those assets depend on. This avoids drawing unchanged assets; it does not make the drawing itself faster. #2636's A1 plans to measure the finish stage, but its A1–A10 items do not already propose this prompt cache. It complements #2674 and #2675, which address unnecessary retranslation rather than prompt generation. ## What changed - **`nx2/make_game.py`:** source-checkout builds request incremental prompt generation. Packaged builders still regenerate every time, using the finish stage's existing launcher distinction. `RX_RELEASE` cannot distinguish them: make_game sets it for source builds too. Missing fontTools still removes the replacement prompts, keeping the existing fallback to the game's icons. - **`nx2/prompt_assets.py`:** fingerprint the inputs before generating, draw into a fresh sibling directory, and record the successful outputs only if a second fingerprint still matches. Reuse requires both matching inputs and every recorded output's size and SHA-256. **A failed generation still removes the prompts, as before**, so the game shows its own icons. An interrupted builder can leave the previous complete set without a stamp; the next build regenerates it. The drawing, encoding and asset formats are unchanged. - **`tests/test_prompt_skip.py`:** game-free fixtures run the actual finish-stage prompt call and production cache/publication functions, with small authored files and a stand-in for expensive drawing. - **`docs/development.md`:** explain reuse, its limits, and `HOOKSHOT_FORCE_PROMPTS=1`. The force switch follows `env_on`: unset, empty, `0`, `false`, `off` and `no` are off, ignoring case and surrounding spaces. ### What prevents a wrong skip The input record covers: - The four prompt source modules, including controller, keyboard and binding tables. - The game's icon font and optional bold label font, compression dictionary, common texture/layout archives, and matching resource-size table names and contents. - The repository's controller and keyboard fonts, and added, changed or removed PNG overrides. - Python/platform; fontTools, Pillow, ASTC encoder, texture decoder, NumPy, zstandard and archspec availability and versions; Pillow's native library versions and the selected ASTC backend. - Generation parameters and relevant library switches, including `SOURCE_DATE_EPOCH`, `FREETYPE_PROPERTIES` and the encoder's custom CPU definitions. Languages and saved bindings are not build inputs here: the generator uses its source tables, and the runtime applies the selected controls. The output record includes files **and directories**: the keyboard directory itself is a runtime marker. Legitimately absent optional outputs stay absent; a missing, truncated or same-size-corrupted recorded file forces regeneration. The runtime's own compressed copies and control-specific keys are excluded before hashing, not mistaken for generator output. **The files the game derives at run time are still cleared by every build**, including an unchanged hit: their own keys do not cover changes to `host/prompts.c`. Only the known runtime filenames are removed; if one cannot be cleared, the build regenerates instead of keeping on doubt. An unreadable input cannot authorize reuse. The log gives the reason, even if a forced regeneration subsequently fails. A source edited during generation leaves no stamp, addressing the stale-input certification problem discussed in [#2674's review](https://git.magicbean.dev/MagicBeanDev/Hookshot/pulls/2674). ## How it was tested 2026-11-12; Arch Linux, kernel `7.2.15-1-omarchy`, x86-64, Intel Core i9-13900K, 32 logical CPUs, Python 3.12.16. Base `c544828ff`; final head `2ec89de8`. No game or GPU program was launched for these checks. ### Finish-stage measurements These execute the real finish-stage statements through completion, including binary/image copies, the unchanged shader-list check, pipeline-list work and prompt generation, in a disposable copy of an existing built game folder. Compilation, translation and shader preparation are not included. They are **not whole rebuild times**. All optional prompt packages were present: fontTools 4.68.0, Pillow 13.0.0, astc-encoder-py 0.1.12, texture2ddecoder 1.0.6, archspec 0.2.7, NumPy 2.5.4 and zstandard 0.25.0. | Run | Source | Finish time | 1-minute load before → after | Top other work | |---|---|---:|---:|---| | Before 1 | `c544828f` | 67.707 s | 7.13 → 7.40 | Existing game 316 → 315% CPU; file indexing 66 → 79%; relay check also active | | Before 2 | `c544828f` | 66.898 s | 7.06 → 8.24 | Existing game 315 → 318%; terminal process about 41% | | First generation with cache | `63d5c965` | 66.400 s | 8.24 → 8.19 | Existing game 318 → 320%; terminal process about 42% | | Unchanged 1 | `63d5c965` | 0.603 s | 8.19 → 8.19 | Existing game 320%; terminal process about 42% | | Unchanged 2 | `63d5c965` | 0.651 s | 8.19 → 8.19 | Existing game 320%; terminal process about 42% | | Changed table source | `681208dd` | 63.147 s | 6.99 → 6.95 | Existing game 320 → 321%; terminal process about 42% | | Regenerate, final head | `2ec89de8` | 65.007 s | 5.77 → 5.86 | Existing game 317 → 316%; terminal process about 43% | | Unchanged, final head, with run-time files present | `2ec89de8` | 0.616 s | 5.79 → 5.79 | Existing game 316%; terminal process about 43% | Other work was running; none was a quiet measurement. CPU percentages are lifetime averages from snapshots, not interval measurements. The first generation is included to show that this is a skip, not a faster drawing algorithm. The unchanged runs print: ```text prompts: unchanged, kept ``` Adding a harmless comment to the button-table source forces a fresh generation: ```text prompts: regenerated (table changed) ``` The comment was removed afterwards. A timestamp-only touch does not regenerate: the key checks content. An additional run on the final head verifies an unchanged hit with all six runtime-derived files present: all six are removed, while the 16 generated originals and the stamp retain their sizes and SHA-256 values. The focused fixture also compares the retained originals byte for byte. The final timing rows above include that cleanup. **Asset parity:** all 16 regenerated files, 274,704,680 bytes, compare byte for byte with a full generation by main `c544828f`. Only the new stamp is excluded. Both sides used the same `SOURCE_DATE_EPOCH` because fontTools otherwise writes the current time into the OpenType header. This comparison setting is not introduced into normal builds. A development cache hit retains the earlier font timestamp; packaged builds retain their old full-generation behavior. ### Game-free checks - **23 focused tests pass** on the final change. They cover unchanged reuse, every input kind, package versions/presence, switch/parameter changes, missing/truncated/same-size-corrupted output, empty directories, clearing runtime derivatives while retaining byte-identical originals, regeneration when runtime cleanup fails, interruption, mid-generation edits, unreadable input, force-switch parsing, incomplete output, publication failure, and the packaged/no-fontTools paths. A non-zero generator result removes prompts for both source and packaged builds; interruption before that result leaves the old set unstamped. - The same unchanged-build test against main's finish stage fails behaviorally: `2 != 1: unchanged finish must not draw the prompts twice`. - Removing each of **18 guards/key components** in memory makes its corresponding behavior test fail on the final change. Three later corrections also fail against the earlier version of this patch: returned failure retained old prompts in both build paths, runtime files survived a hit, and an uncleared runtime file did not force regeneration. The unreadable-input diagnostic has a separate before-fails/after-passes proof. - The whole suite one commit earlier, at `f20ea58b` (before a failed generation was made to remove the prompts again and the run-time files to be cleared), `python -I -B tests/run_all.py --jobs 8`: ```text 472 passed, 0 failed, 20 skipped in 133 s (linux, 8 at once) ``` Load was 7.37 → 19.48. The first full run, at 32 checks at once, was **471 passed, 1 failed, 20 skipped**: a clang++ child compiling `texture_pass.cpp` for `nx2/test_depth_sample_types.py` died with SIGKILL. That fixture and its input trees are unchanged from main. No kernel OOM line was found, so the cause is not established. The complete run with eight workers passes, including that check. The failed run is retained, not counted as a pass. On the final head `2ec89de8` the focused checks, the baseline failure and all guard mutations were rerun, and the whole suite once more with eight workers: **471 passed, 1 failed, 20 skipped in 136 s** (load 11.14 → 22.62). The one failure is `tests/test_release_scripts.py`, `test_nightly_two_runs_at_once_end_at_the_newer_commit`, the intermittent race reported in #2626; this change touches none of its files. - No core dumps were recorded. Nothing generated from the game is in this change. ## Not tested - Windows filesystem behavior or a Windows build. - The packaged Windows Builder or Linux AppImage build/update end to end. Their always-regenerate selection is covered by a game-free test only. - A full compile/lift or a new gameplay run, other CPUs, or gameplay/frame-rate benefits. No screenshots: the regenerated asset bytes match, and no visual change is intended. ## Risks The input list must grow when the generator gains new file dependencies or outputs outside the prompt directory. A regression flags new local module imports, but it does not replace reviewing new file reads. Generation temporarily needs room for both old and new assets. Publication uses completed directories and rollback on a rename error; interruption cannot create a stamped half-set, though a process killed between the two directory renames may require regeneration on the next build. ## Overlaps The open-PR scan found 47 PRs touching one of these files. The actual conflict hunks were read: - **#2151:** adjacent item-button generation conflicts with the finish-stage prompt call/error branch. Preserve both steps and account for added prompt dependencies. - **#2261:** substantive overlap. It adds `--wheel-icons`, another input folder and output outside `prompts`. Keep that work outside this skip or extend the input/output proof and staging; accepting both hunks mechanically is not enough. - #1872, #1958, #2304, #2317, #2430, #2465, #2474, #2478, #2608, #2673, #2675 and #2686 share code files but merged cleanly in the scan. That is not proof that any newly added generator dependency is covered. Assisted-by: GPT-6 Astra Co-Authored-By: GPT-6 Astra <[email protected]> <!-- github-sync:item:2698 -->
Fingerprint prompt inputs before generation and check them again before stamping a complete staged output set. Verify every recorded output by size and content, preserve full generation in packaged builders, and document the force switch.

Assisted-by: GPT-6 Astra
Co-Authored-By: GPT-6 Astra <[email protected]>
Author
Owner

@hckrman101 commented on nx2/prompt_assets.py line 1109 on GitHub · 2026-11-12 07:30 UTC · original

Nit: if the builder is killed hard here (SIGKILL, a closed console on Windows, power loss), this .prompts-* directory stays in the game folder with up to two full sets in it (about 550 MB), and nothing removes it later. A quick sweep of stale .prompts-* siblings at the start of make_prompts would clean that up.

> [@hckrman101](https://github.com/hckrman101) commented on `nx2/prompt_assets.py` line 1109 on GitHub · 2026-11-12 07:30 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2698#discussion_r4547550070) Nit: if the builder is killed hard here (SIGKILL, a closed console on Windows, power loss), this `.prompts-*` directory stays in the game folder with up to two full sets in it (about 550 MB), and nothing removes it later. A quick sweep of stale `.prompts-*` siblings at the start of `make_prompts` would clean that up. <!-- github-sync:rc:4547550070 -->
Author
Owner

@hckrman101 commented on nx2/prompt_assets.py line 1070 on GitHub · 2026-11-12 07:30 UTC · original

Nit: the first build after this lands, and any build after a failed one, has no stamp at all, so the log will say stamp or outputs unreadable, which sounds like something broke. Catching FileNotFoundError on the stamp separately and saying no stamp yet would make it read better.

> [@hckrman101](https://github.com/hckrman101) commented on `nx2/prompt_assets.py` line 1070 on GitHub · 2026-11-12 07:30 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2698#discussion_r4547550081) Nit: the first build after this lands, and any build after a failed one, has no stamp at all, so the log will say `stamp or outputs unreadable`, which sounds like something broke. Catching `FileNotFoundError` on the stamp separately and saying `no stamp yet` would make it read better. <!-- github-sync:rc:4547550081 -->
Author
Owner

@hckrman101 approved on GitHub · 2026-11-12 07:30 UTC · original

Thanks, this is a nice win for anyone rebuilding from source: a minute saved on every finish stage, and the skip is careful about what it trusts.

I read the key against everything generate() opens and imports (the four prompt modules, the romfs font, label font, dictionary, textures, layouts and size table, the Kenney fonts, the PNG overrides, the package versions), and the runtime list against what host/prompts.c writes into prompts/ (the .zs copies, the two .key files, rsizetable.mod.zs). Both line up. The publish path keeps the old behaviour where it matters: no fontTools or a failed generator still removes the folder so the game falls back to its own icons, the stamp is removed before drawing so an interrupted run can't certify a stale set, and the second key check covers edits made mid-generation. launcher is the same packaged/source split the finish stage already uses, and HOOKSHOT_FORCE_PROMPTS goes through env_on like the other switches. CI is green on Linux and Windows, and the branch is up to date with main.

Keeping packaged builders on full regeneration is fine for now. They rebuild everything anyway, and we can widen it later once this has had some use from source builds.

Two small nits inline, neither blocking. Also, as you note, #2261 adds an input folder and output outside prompts/, so whichever of the two lands second has to extend prompt_key/prompt_outputs rather than just merging the hunks. I'll keep that in mind when merging.

> [@hckrman101](https://github.com/hckrman101) approved on GitHub · 2026-11-12 07:30 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2698#pullrequestreview-5870714239) Thanks, this is a nice win for anyone rebuilding from source: a minute saved on every finish stage, and the skip is careful about what it trusts. I read the key against everything `generate()` opens and imports (the four prompt modules, the romfs font, label font, dictionary, textures, layouts and size table, the Kenney fonts, the PNG overrides, the package versions), and the runtime list against what `host/prompts.c` writes into `prompts/` (the `.zs` copies, the two `.key` files, `rsizetable.mod.zs`). Both line up. The publish path keeps the old behaviour where it matters: no fontTools or a failed generator still removes the folder so the game falls back to its own icons, the stamp is removed before drawing so an interrupted run can't certify a stale set, and the second key check covers edits made mid-generation. `launcher` is the same packaged/source split the finish stage already uses, and `HOOKSHOT_FORCE_PROMPTS` goes through `env_on` like the other switches. CI is green on Linux and Windows, and the branch is up to date with main. Keeping packaged builders on full regeneration is fine for now. They rebuild everything anyway, and we can widen it later once this has had some use from source builds. Two small nits inline, neither blocking. Also, as you note, #2261 adds an input folder and output outside `prompts/`, so whichever of the two lands second has to extend `prompt_key`/`prompt_outputs` rather than just merging the hunks. I'll keep that in mind when merging. <!-- github-sync:r:5870714239 -->
github-sync closed this pull request 2026-11-12 08:31:26 +00:00
Author
Owner

@mooki000 commented on GitHub · 2026-11-12 07:53 UTC · original

Both taken, thank you: #2710.

  • A missing stamp now reads prompts: regenerated (no stamp yet); a stamp that exists and cannot be read keeps its own line.
  • Leftover .prompts-* directories are removed at the start of the next build. Each generation now holds a lock on an owner file in its staging directory, so a leftover is told from a running build by the lock (free: removed at once, also right after a kill; held: left alone), not by age. Directories from this pull request's version carry no owner file and go after 24 hours. Tried with a real kill -9 on Linux; the Windows locking call is exercised with a stand-in only.
> [@mooki000](https://github.com/mooki000) commented on GitHub · 2026-11-12 07:53 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2698#issuecomment-6818008470) Both taken, thank you: #2710. - A missing stamp now reads `prompts: regenerated (no stamp yet)`; a stamp that exists and cannot be read keeps its own line. - Leftover `.prompts-*` directories are removed at the start of the next build. Each generation now holds a lock on an owner file in its staging directory, so a leftover is told from a running build by the lock (free: removed at once, also right after a kill; held: left alone), not by age. Directories from this pull request's version carry no owner file and go after 24 hours. Tried with a real `kill -9` on Linux; the Windows locking call is exercised with a stand-in only. <!-- github-sync:c:6818008470 -->
Commenting is not possible because the repository is archived.
No description provided.