WIP: feat(build): nx2/dev.py says why the next build translates again, shows the builds' times, and builds again #28

Draft
dyst wants to merge 4 commits from dyst/Longshot:feat/dev-tool into main
First-time contributor

Why

When a build translates the game again (about 5 minutes on Windows) there was no way to see why beyond "the key changed", and a build started any other way than make_game.py's own had another environment, so other object and lift keys. This adds one entry point for developers, nx2/dev.py, and puts the build's environment in one place.

What changed

This PR is stacked on #22: its diff against main carries #22's lift-key change (lift_reads_env(), LIFT_ENV_* in nx2/make_game.py, check_lift_env() in tests/test_lift_cache.py). The bullets below are this PR's own.

  • build: nx2/profiles.py (new): the environment the lift and build.py run with, the dictionary make_game.py built in place, as build_env(), in two profiles chosen by profile(): release (the builders') and dev (LONGSHOT_PROFILE=dev: no RX_RELEASE, so a console and no shader preparation at start; the lift is the same, since #22's key no longer hashes RX_RELEASE). TOOLCHAIN_ENV names the toolchain's variables.

  • build: nx2/make_game.py:

    • main takes its environment from profiles.build_env(), and record_build() records how the build was started in <work>/last_build.json: its arguments, its Python, the profile, the toolchain's variables;
    • lift_key keeps what each key was made of in KEY_PARTS (the Python, the HLE functions, each file's hash by name, the variables), and write_lift_key writes the stored key's parts to <work>/sa/lift_key_parts.json (LIFT_PARTS), outside gen/.
  • tools: nx2/dev.py (new, standard library only):

    • status: whether the next build skips the lift, and if not, which file, HLE function, variable or Python changed (lift_status(), differences());
    • times [-n N]: the last builds' phases side by side (times_table(), from NotRexed/Hookshot#2693's build_times.json);
    • build [--dev]: the last build again, with its arguments, Python and tools, its --log left out (build(), replayed());
    • compdb: the last build's compile_commands.json (NotRexed/Hookshot#2893) at the checkout's top, for clangd (compdb()).

    Nothing it prints comes from the game's files: names, times and hashes of the tools' own inputs.

  • tests: tests/test_dev_tool.py (new): the differences status names (a file, a variable, the Python, an HLE function), the work folder it picks, times' table, build's replayed command (without --log) and its environment for each profile, compdb. tests/test_lift_cleanup_error.py takes write_lift_key's new globals (KEY_PARTS, LIFT_PARTS).

Flags and settings

LONGSHOT_PROFILE is the one new switch. The other names are existing variables the diff writes in quotes: the ones TOOLCHAIN_ENV records and dev.py build passes on, the two moved into profiles.build_env(), and the ones #22's lift-key change, carried in this diff, names.

Flag New or changed What it does Default now Goal: on by default? Helps on Doesn't help on Untested on Untested, but should help on
LONGSHOT_PROFILE new the build's profile: unset or release (the builders'), dev (no RX_RELEASE: a console, no shader preparation at start); dev.py build --dev sets it unset (release) no (a developer switch) - - Linux -
RX_RELEASE changed: set by profiles.build_env() in the release profile only (make_game.py set it always); out of the lift key (#22) the release build's switch 1 in the release profile, as before no (a developer switch) - - Linux -
RX_PGO unchanged: moved into profiles.build_env() the play profile for the hot pieces, when the work folder has one this compiler reads set by the build, as before no (a developer switch) - - Linux -
LONGSHOT_MINGW existing; in TOOLCHAIN_ENV (recorded, passed on by dev.py build); named in LIFT_ENV_NOT_OUTPUT (#22) where the compiler is unset no (a developer switch) - - Linux -
LONGSHOT_MSVC existing; in TOOLCHAIN_ENV a toolchain variable, recorded and passed on by dev.py build unset no (a developer switch) - - Linux -
LONGSHOT_NGX_SDK existing; in TOOLCHAIN_ENV a toolchain variable, recorded and passed on by dev.py build unset no (a developer switch) - - Linux -
LONGSHOT_FSR existing; in TOOLCHAIN_ENV a toolchain variable, recorded and passed on by dev.py build unset no (a developer switch) - - Linux -
LONGSHOT_JOBS existing; in TOOLCHAIN_ENV a toolchain variable, recorded and passed on by dev.py build unset no (a developer switch) - - Linux -
RX_CPM existing; in TOOLCHAIN_ENV a toolchain variable, recorded and passed on by dev.py build unset no (a developer switch) - - Linux -
RX_MARCH existing; in TOOLCHAIN_ENV; out of the lift key (#22, named in its test) a toolchain variable, recorded and passed on by dev.py build unset no (a developer switch) - - Linux -
RX_TARGET existing; in TOOLCHAIN_ENV; stays in the lift key (LIFT_ENV_NAMES, #22) the target the emitter and its tables are made for unset no (a developer switch) - - Linux -
LONGSHOT_LIFT_JOBS existing; named in LIFT_ENV_NOT_OUTPUT (#22) how many processes the lift runs unset no (a developer switch) - - Linux -
LONGSHOT_LIFT_NO_FORK existing; named in LIFT_ENV_NOT_OUTPUT (#22) how the lift's processes are started unset no (a developer switch) - - Linux -
RX_JOBS existing; out of the lift key (LIFT_ENV_NOT_OUTPUT, #22) how many processes unset no (a developer switch) - - Linux -
RX_VK_NAN_GUARD existing; out of the lift key (LIFT_ENV_NOT_OUTPUT, #22) a renderer switch; read by the lift's sources only for nx2/bake_ds's own run unset no (a developer switch) - - Linux -
RX_VK_NO_DRAW_PARAMS existing; out of the lift key (LIFT_ENV_NOT_OUTPUT, #22) a renderer switch; read by the lift's sources only for nx2/bake_ds's own run unset no (a developer switch) - - Linux -
RX_VK_NO_F16 existing; out of the lift key (LIFT_ENV_NOT_OUTPUT, #22) a renderer switch; read by the lift's sources only for nx2/bake_ds's own run unset no (a developer switch) - - Linux -
RX_VK_SHARED_48K existing; out of the lift key (LIFT_ENV_NOT_OUTPUT, #22) a renderer switch; read by the lift's sources only for nx2/bake_ds's own run unset no (a developer switch) - - Linux -
RX_VK_NO_PROGRAM_CACHE existing; named in #22's test only (test only) test only: the check that an RX_VK_ switch leaves the lift key unchanged unset no (a developer switch) - - Linux -

Measurements

Nothing to measure in the game: the change is to the build's tools and environment; the game from these builds plays (below).

What was run, on the GitHub version (NotRexed/Hookshot#2909), not on Longshot. Windows 11, in a work folder with a translation:

  • dev.py status after A64_NO_VFPX=1 in the shell: lift: the next build translates the game again: then environment A64_NO_VFPX: (unset) -> 1.
  • dev.py build replayed the last build (the same arguments and Python, shown quoted).
  • dev.py times -n 3 showed the last builds' phases side by side.
  • The game from these builds plays: in Kokiri Forest from a save, RTX 3080 (NVIDIA 621.14), 28-31 fps, nothing in the log about a lost device or a crash.
  • Every check that reads the changed files (55), through run_all.py, rebased on that day's GitHub main, Windows: 54 pass; test_linux_update fails the same way on main there.

Testing

Tested: the runs under Measurements (on the GitHub version) and the game-free checks this description names. Checked but not measured: dev.py status, build and times, and the game from these builds (above).

Not tested: Linux.

Needs other testers:

  • Linux: python -I nx2/dev.py status after a build, expecting it to name what changed since (request to open)

Screenshots

Nothing on screen.

Risk and rollback

make_game.py now takes its environment from profiles.build_env(); a difference from the dictionary it built in place would change object or lift keys and recompile or translate again (tests/test_dev_tool.py checks the release profile is what make_game.py built). record_build and write_lift_key write two more files in the work folder (last_build.json, sa/lift_key_parts.json); record_build ignores a failed write. dev.py is a new tool nothing else calls. Reverting the commits is enough.

Reviewer notes

Carried over from NotRexed/Hookshot#2909 with the move to Longshot: the same change on Longshot's main, with its rename applied. Its review and discussion are there.

Stacked on #22 (carried from NotRexed/Hookshot#2874): review the commits after it; this rebases to them alone once #22 is in.

The dev-loop proposal's A9 (NotRexed/Hookshot#2636); its later pieces (scenario runs, where, the bench lock, warnings) build on it.

The checks and the in-game runs above were done on the GitHub version; this branch is the same change with the rename applied (the same files and line counts). Forgejo runs no checks, and they were not run again here. Only the checks that read the changed files were run, not the whole run_all.py.

Issues

See NotRexed/Hookshot#2636 (A9), NotRexed/Hookshot#2874.

Checklist

For everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description).
  • Rebased on current main.
  • python -I -B tests/run_all.py run and passing (any failure that also fails on main is named above).
  • Tested in the game, or what wasn't is named under Testing.
  • Testing filled in: what was tested, what wasn't, and what needs other testers (with test requests).
  • Measured: main and this PR at named commits, on the same PC, scene and settings, at least 2 interleaved runs each, the median reported.
  • No game files, dumps, build output or generated files.
  • third_party/ untouched (a new folder with a PROVENANCE.md instead).
  • The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled.

If applicable

  • AI assistance recorded with Assisted-by: / Co-Authored-By: trailers.
  • Flags table filled in; README / docs/development.md switch reference updated.
  • Screenshots for a change that shows on screen.
  • A row in docs/tested-configurations.md for each measured run.
  • MKWVK_API_VERSION bumped (gpu/vk/vk_api.h's MkwVkHost or MkwVkRenderer changed).
  • The SPIR-V headers regenerated with glslang (a shader changed).
  • A game-free test added for the fix, failing on main and passing here.
  • A check under tests/ that reads changed source updated in this PR, not weakened.
  • Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths.
  • Docs updated.

🤖 Generated with Claude Code

## Why When a build translates the game again (about 5 minutes on Windows) there was no way to see why beyond "the key changed", and a build started any other way than `make_game.py`'s own had another environment, so other object and lift keys. This adds one entry point for developers, `nx2/dev.py`, and puts the build's environment in one place. ## What changed This PR is stacked on #22: its diff against `main` carries #22's lift-key change (`lift_reads_env()`, `LIFT_ENV_*` in `nx2/make_game.py`, `check_lift_env()` in `tests/test_lift_cache.py`). The bullets below are this PR's own. - build: `nx2/profiles.py` (new): the environment the lift and `build.py` run with, the dictionary `make_game.py` built in place, as `build_env()`, in two profiles chosen by `profile()`: `release` (the builders') and `dev` (`LONGSHOT_PROFILE=dev`: no `RX_RELEASE`, so a console and no shader preparation at start; the lift is the same, since #22's key no longer hashes `RX_RELEASE`). `TOOLCHAIN_ENV` names the toolchain's variables. - build: `nx2/make_game.py`: - `main` takes its environment from `profiles.build_env()`, and `record_build()` records how the build was started in `<work>/last_build.json`: its arguments, its Python, the profile, the toolchain's variables; - `lift_key` keeps what each key was made of in `KEY_PARTS` (the Python, the HLE functions, each file's hash by name, the variables), and `write_lift_key` writes the stored key's parts to `<work>/sa/lift_key_parts.json` (`LIFT_PARTS`), outside `gen/`. - tools: `nx2/dev.py` (new, standard library only): - `status`: whether the next build skips the lift, and if not, which file, HLE function, variable or Python changed (`lift_status()`, `differences()`); - `times [-n N]`: the last builds' phases side by side (`times_table()`, from [NotRexed/Hookshot#2693](https://github.com/NotRexed/Hookshot/pull/2693)'s `build_times.json`); - `build [--dev]`: the last build again, with its arguments, Python and tools, its `--log` left out (`build()`, `replayed()`); - `compdb`: the last build's `compile_commands.json` ([NotRexed/Hookshot#2893](https://github.com/NotRexed/Hookshot/pull/2893)) at the checkout's top, for clangd (`compdb()`). Nothing it prints comes from the game's files: names, times and hashes of the tools' own inputs. - tests: `tests/test_dev_tool.py` (new): the differences `status` names (a file, a variable, the Python, an HLE function), the work folder it picks, `times`' table, `build`'s replayed command (without `--log`) and its environment for each profile, `compdb`. `tests/test_lift_cleanup_error.py` takes `write_lift_key`'s new globals (`KEY_PARTS`, `LIFT_PARTS`). ## Flags and settings `LONGSHOT_PROFILE` is the one new switch. The other names are existing variables the diff writes in quotes: the ones `TOOLCHAIN_ENV` records and `dev.py build` passes on, the two moved into `profiles.build_env()`, and the ones #22's lift-key change, carried in this diff, names. | Flag | New or changed | What it does | Default now | Goal: on by default? | Helps on | Doesn't help on | Untested on | Untested, but should help on | |---|---|---|---|---|---|---|---|---| | `LONGSHOT_PROFILE` | new | the build's profile: unset or `release` (the builders'), `dev` (no `RX_RELEASE`: a console, no shader preparation at start); `dev.py build --dev` sets it | unset (`release`) | no (a developer switch) | - | - | Linux | - | | `RX_RELEASE` | changed: set by `profiles.build_env()` in the release profile only (`make_game.py` set it always); out of the lift key (#22) | the release build's switch | `1` in the release profile, as before | no (a developer switch) | - | - | Linux | - | | `RX_PGO` | unchanged: moved into `profiles.build_env()` | the play profile for the hot pieces, when the work folder has one this compiler reads | set by the build, as before | no (a developer switch) | - | - | Linux | - | | `LONGSHOT_MINGW` | existing; in `TOOLCHAIN_ENV` (recorded, passed on by `dev.py build`); named in `LIFT_ENV_NOT_OUTPUT` (#22) | where the compiler is | unset | no (a developer switch) | - | - | Linux | - | | `LONGSHOT_MSVC` | existing; in `TOOLCHAIN_ENV` | a toolchain variable, recorded and passed on by `dev.py build` | unset | no (a developer switch) | - | - | Linux | - | | `LONGSHOT_NGX_SDK` | existing; in `TOOLCHAIN_ENV` | a toolchain variable, recorded and passed on by `dev.py build` | unset | no (a developer switch) | - | - | Linux | - | | `LONGSHOT_FSR` | existing; in `TOOLCHAIN_ENV` | a toolchain variable, recorded and passed on by `dev.py build` | unset | no (a developer switch) | - | - | Linux | - | | `LONGSHOT_JOBS` | existing; in `TOOLCHAIN_ENV` | a toolchain variable, recorded and passed on by `dev.py build` | unset | no (a developer switch) | - | - | Linux | - | | `RX_CPM` | existing; in `TOOLCHAIN_ENV` | a toolchain variable, recorded and passed on by `dev.py build` | unset | no (a developer switch) | - | - | Linux | - | | `RX_MARCH` | existing; in `TOOLCHAIN_ENV`; out of the lift key (#22, named in its test) | a toolchain variable, recorded and passed on by `dev.py build` | unset | no (a developer switch) | - | - | Linux | - | | `RX_TARGET` | existing; in `TOOLCHAIN_ENV`; stays in the lift key (`LIFT_ENV_NAMES`, #22) | the target the emitter and its tables are made for | unset | no (a developer switch) | - | - | Linux | - | | `LONGSHOT_LIFT_JOBS` | existing; named in `LIFT_ENV_NOT_OUTPUT` (#22) | how many processes the lift runs | unset | no (a developer switch) | - | - | Linux | - | | `LONGSHOT_LIFT_NO_FORK` | existing; named in `LIFT_ENV_NOT_OUTPUT` (#22) | how the lift's processes are started | unset | no (a developer switch) | - | - | Linux | - | | `RX_JOBS` | existing; out of the lift key (`LIFT_ENV_NOT_OUTPUT`, #22) | how many processes | unset | no (a developer switch) | - | - | Linux | - | | `RX_VK_NAN_GUARD` | existing; out of the lift key (`LIFT_ENV_NOT_OUTPUT`, #22) | a renderer switch; read by the lift's sources only for `nx2/bake_ds`'s own run | unset | no (a developer switch) | - | - | Linux | - | | `RX_VK_NO_DRAW_PARAMS` | existing; out of the lift key (`LIFT_ENV_NOT_OUTPUT`, #22) | a renderer switch; read by the lift's sources only for `nx2/bake_ds`'s own run | unset | no (a developer switch) | - | - | Linux | - | | `RX_VK_NO_F16` | existing; out of the lift key (`LIFT_ENV_NOT_OUTPUT`, #22) | a renderer switch; read by the lift's sources only for `nx2/bake_ds`'s own run | unset | no (a developer switch) | - | - | Linux | - | | `RX_VK_SHARED_48K` | existing; out of the lift key (`LIFT_ENV_NOT_OUTPUT`, #22) | a renderer switch; read by the lift's sources only for `nx2/bake_ds`'s own run | unset | no (a developer switch) | - | - | Linux | - | | `RX_VK_NO_PROGRAM_CACHE` | existing; named in #22's test only (test only) | test only: the check that an `RX_VK_` switch leaves the lift key unchanged | unset | no (a developer switch) | - | - | Linux | - | ## Measurements Nothing to measure in the game: the change is to the build's tools and environment; the game from these builds plays (below). What was run, on the GitHub version (NotRexed/Hookshot#2909), not on Longshot. Windows 11, in a work folder with a translation: - `dev.py status` after `A64_NO_VFPX=1` in the shell: `lift: the next build translates the game again:` then `environment A64_NO_VFPX: (unset) -> 1`. - `dev.py build` replayed the last build (the same arguments and Python, shown quoted). - `dev.py times -n 3` showed the last builds' phases side by side. - The game from these builds plays: in Kokiri Forest from a save, RTX 3080 (NVIDIA 621.14), 28-31 fps, nothing in the log about a lost device or a crash. - Every check that reads the changed files (55), through `run_all.py`, rebased on that day's GitHub `main`, Windows: 54 pass; `test_linux_update` fails the same way on `main` there. ## Testing **Tested:** the runs under Measurements (on the GitHub version) and the game-free checks this description names. Checked but not measured: `dev.py status`, `build` and `times`, and the game from these builds (above). **Not tested:** Linux. **Needs other testers:** - Linux: `python -I nx2/dev.py status` after a build, expecting it to name what changed since (request to open) ## Screenshots Nothing on screen. ## Risk and rollback `make_game.py` now takes its environment from `profiles.build_env()`; a difference from the dictionary it built in place would change object or lift keys and recompile or translate again (`tests/test_dev_tool.py` checks the release profile is what `make_game.py` built). `record_build` and `write_lift_key` write two more files in the work folder (`last_build.json`, `sa/lift_key_parts.json`); `record_build` ignores a failed write. `dev.py` is a new tool nothing else calls. Reverting the commits is enough. ## Reviewer notes Carried over from [NotRexed/Hookshot#2909](https://github.com/NotRexed/Hookshot/pull/2909) with the move to Longshot: the same change on Longshot's `main`, with its rename applied. Its review and discussion are there. **Stacked on #22** (carried from NotRexed/Hookshot#2874): review the commits after it; this rebases to them alone once #22 is in. The dev-loop proposal's A9 ([NotRexed/Hookshot#2636](https://github.com/NotRexed/Hookshot/pull/2636)); its later pieces (scenario runs, `where`, the bench lock, warnings) build on it. The checks and the in-game runs above were done on the GitHub version; this branch is the same change with the rename applied (the same files and line counts). Forgejo runs no checks, and they were not run again here. Only the checks that read the changed files were run, not the whole `run_all.py`. ## Issues See [NotRexed/Hookshot#2636](https://github.com/NotRexed/Hookshot/pull/2636) (A9), [NotRexed/Hookshot#2874](https://github.com/NotRexed/Hookshot/pull/2874). ## Checklist ### For everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`). - [ ] Rebased on current `main`. - [ ] `python -I -B tests/run_all.py` run and passing (any failure that also fails on `main` is named above). - [x] Tested in the game, or what wasn't is named under Testing. - [x] Testing filled in: what was tested, what wasn't, and what needs other testers (with test requests). - [ ] Measured: main and this PR at named commits, on the same PC, scene and settings, at least 2 interleaved runs each, the median reported. - [x] No game files, dumps, build output or generated files. - [x] `third_party/` untouched (a new folder with a `PROVENANCE.md` instead). - [x] The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled. ### If applicable - [x] AI assistance recorded with `Assisted-by:` / `Co-Authored-By:` trailers. - [ ] Flags table filled in; README / docs/development.md switch reference updated. - [ ] Screenshots for a change that shows on screen. - [ ] A row in `docs/tested-configurations.md` for each measured run. - [ ] `MKWVK_API_VERSION` bumped (`gpu/vk/vk_api.h`'s `MkwVkHost` or `MkwVkRenderer` changed). - [ ] The SPIR-V headers regenerated with glslang (a shader changed). - [x] A game-free test added for the fix, failing on `main` and passing here. - [x] A check under `tests/` that reads changed source updated in this PR, not weakened. - [ ] Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths. - [ ] Docs updated. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The key took every A64_, OOT_ and RX_ variable, so an RX_VK_ switch left set in the shell, or RX_RELEASE, which the lift
does not read, translated the game again (~5 minutes on Windows), and so did the same game in another work or game
folder: OOT_WORK, OOT_EXEFS, OOT_ROMFS, OOT_DUMP and A64_HOT_FUNCS are paths, and what the lift reads through them is
hashed by its contents already. The emitter's own SUYU_RECOMP_ switches were not in the key at all. This is the dev-loop
proposal's A2 (#2636), with hckrman101's point that the paths keep two checkouts of one tree from sharing a lift.

lift_key now hashes the variables lift_reads_env() names: A64_ and SUYU_RECOMP_, and RX_TARGET, but not the paths
(LIFT_PATH_ENV) nor the compile's own (COMPILE_ONLY_ENV). tests/test_lift_cache.py checks that the same game and inputs in
other folders give the same key, that RX_RELEASE, an RX_VK_ switch and RX_MARCH leave it and SUYU_RECOMP_STRICT, RX_TARGET
and an A64_ switch change it, and that every variable the lift's sources read is in the key, a path, or in
LIFT_ENV_NOT_OUTPUT with why it changes nothing. The key's form changes, so the first build after this translates once.

Measured on Windows: after that first lift, a build with RX_VK_NO_PROGRAM_CACHE=1 in the shell skipped the lift (15 s).

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The build's environment was made in make_game.py alone, so a build started any other way had other object keys and lift
key, and nothing could say why a build translated the game again (~5 minutes on Windows) beyond "the key changed". This
is the dev-loop proposal's A9 (#2636).

- nx2/profiles.py makes the environment the lift and build.py run with (the dictionary make_game.py built in place), in
  two profiles: release, the builders', and dev (LONGSHOT_PROFILE=dev: no RX_RELEASE, so a console and no shader
  preparation at start; the lift is the same since the key no longer hashes RX_RELEASE).
- make_game.py takes it from there, and records how it was started in <work>/last_build.json: its arguments, its Python,
  the profile, the toolchain's variables.
- lift_key keeps what each key was made of (the Python, the HLE functions, each file's hash by name, the variables), and
  write_lift_key writes the stored key's parts to <work>/sa/lift_key_parts.json, outside gen/.
- nx2/dev.py status: whether the next build skips the lift, and if not which file, function, variable or Python changed;
  dev.py times: the last builds' phases side by side (build_times.json); dev.py build [--dev]: the last build again.

tests/test_dev_tool.py is new; test_lift_cleanup_error.py takes write_lift_key's new globals.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
For clangd and clang-tidy; the file is nx2/build.py's (#2893). Without one,
it says to build first.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
dyst changed title from feat(build): nx2/dev.py says why the next build translates again, shows the builds' times, and builds again to WIP: feat(build): nx2/dev.py says why the next build translates again, shows the builds' times, and builds again 2026-11-13 01:13:50 +00:00
Author
First-time contributor

Back to WIP under #48's process: this was carried over before the new template. Its description will be brought to the template and measured on Longshot (main against this PR, at named commits) when it's wanted for review; ask here if you'd like it sooner.

Back to WIP under #48's process: this was carried over before the new template. Its description will be brought to the template and measured on Longshot (main against this PR, at named commits) when it's wanted for review; ask here if you'd like it sooner.
Author
First-time contributor

Description brought to Longshot's PR template (#48). Still WIP: its numbers are from the GitHub version, and it isn't rebased on current main; it gets measured on Longshot (main against this PR, at named commits) before it is marked ready.

Description brought to Longshot's PR template (#48). Still WIP: its numbers are from the GitHub version, and it isn't rebased on current main; it gets measured on Longshot (main against this PR, at named commits) before it is marked ready.
Author
First-time contributor

Description moved to the template's Testing section and whole checklist (#72): what was tested, what wasn't, and what needs other testers (their test requests to open while this is WIP).

Description moved to the template's Testing section and whole checklist (#72): what was tested, what wasn't, and what needs other testers (their test requests to open while this is WIP).
This pull request is marked as a work in progress.
This branch is out-of-date with the base branch
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u 'feat/dev-tool:dyst-feat/dev-tool'
git switch 'dyst-feat/dev-tool'
Sign in to join this conversation.
No description provided.