Selecting a queue entry filled the review panel but left the form blank, so the inputs a song was built from were only reachable through the EDIT & REBUILD button. Selection now loads them: pick a song, change a line of lyrics, hit REBUILD SONG. The rebuild endpoint already reruns only the stages your edit invalidates, so fixing a lyric realigns without re-downloading or re-separating.
No backend change was needed. GET /api/jobs/{id}/input already existed and already returned the form's field names, and the input.json sidecar already persists them across restarts. This is entirely a matter of the page not asking.
Decisions worth flagging
EDIT & REBUILD is gone. With selection doing the loading, its only remaining job was moving focus, and two controls that both mean "edit this song" is worse than one. CANCEL becomes NEW SONG INSTEAD, which is what it now does — the form is rarely empty, so "cancel" no longer describes it.
The landing page still lands blank. The job auto-selected on load fills the review panel only; the form stays a blank NEW SONG. I built it both ways and checked the alternative in a browser first: routing the auto-selection through the same path means that whenever the newest job is queued or running — which is most times you reload to check on something — you land on a locked form and need a click to get out of it. Creating a song is the more common first action, so it keeps the default. Clicking any queue entry is what loads it. There is a comment on that line so it does not get "fixed" later.
Three things the old button-driven flow never had to handle
Selection is a much cheaper action than a button press, so cases that were previously unreachable are now one stray click away:
Advanced opens when the job has anything in it. Genre, year, language, bpm and a kept cover all carry into a rebuild whether or not the section is expanded. Prefilling them behind a closed summary would mean rebuilding with values that were never on screen. This one I only found by driving the page — the round-trip test could not fill a hidden field.
Submit is disabled while the selected job is queued or running, with the reason next to it, because /rebuild answers 409 until the job settles. renderActions already suppressed its controls mid-run; the form now agrees. It re-enables on the next poll when the job finishes.
Edits are not silently discarded. Selecting a different entry confirms first if you have typed anything, and deleting the job the form is holding exits rebuild mode rather than leaving it pointed at an id that would 404.
One bug in my own first draft, caught before it shipped: the early return that avoids reloading the already-loaded job also skipped the selection change, so after a RERUN — which selects the new job while the form still holds the parent — clicking the parent's card did nothing. The reload and the selection are now separate.
Testing
tests/test_web.py carries a mutation-testing harness over index.html: each checker has to reject a plausible regression of the thing it guards. Extended _rebuild_flaws with five new checks and added four mutations for them — selection not loading inputs, edits discarded without asking, advanced fields left hidden, an in-progress job submittable anyway, and the loaded job deleted out from under the form. startRebuild is renamed to loadInputs, and the existing guard and the ["edit", ...] action-list mutation are updated to match.
Full suite: 284 passed, 1 skipped. tests/test_audio_io.py and tests/test_separate.py were not run — they import torch, which is not installable in the environment I had; neither touches this change.
Two pre-existing flaky tests, test_upload_dir_cleaned_up_after_success and test_job_has_timestamps, each fail roughly one run in three. I confirmed both flake the same way on an unmodified origin/main worktree. They are worker-thread timing, unrelated to this branch, which touches no Python outside the frontend guards.
Beyond the unit tests I drove the real page with Playwright and checked the states rather than assuming them: landing blank, a done job loading with its lyrics, a failed job also offering RERUN, a running job loading locked with the reason shown, the discard prompt both dismissed and accepted, and NEW SONG INSTEAD clearing back to a blank form. Then the full round trip — select a song carrying genre/year/language, confirm Advanced auto-opened with them visible, edit the lyrics and the year, submit, and verify the new job came back with the edits applied, the untouched fields carried through, rebuilt_from set, and the form reset.
docs/images/webui.png is regenerated. The screenshot script already clicks a song, so the committed screenshot now shows the form populated beside its matching review, which is the feature.
**Summary generated by LLM agent**
Selecting a queue entry filled the review panel but left the form blank, so the inputs a song was built from were only reachable through the EDIT & REBUILD button. Selection now loads them: pick a song, change a line of lyrics, hit REBUILD SONG. The rebuild endpoint already reruns only the stages your edit invalidates, so fixing a lyric realigns without re-downloading or re-separating.
No backend change was needed. `GET /api/jobs/{id}/input` already existed and already returned the form's field names, and the `input.json` sidecar already persists them across restarts. This is entirely a matter of the page not asking.
## Decisions worth flagging
**EDIT & REBUILD is gone.** With selection doing the loading, its only remaining job was moving focus, and two controls that both mean "edit this song" is worse than one. CANCEL becomes NEW SONG INSTEAD, which is what it now does — the form is rarely empty, so "cancel" no longer describes it.
**The landing page still lands blank.** The job auto-selected on load fills the review panel only; the form stays a blank NEW SONG. I built it both ways and checked the alternative in a browser first: routing the auto-selection through the same path means that whenever the newest job is queued or running — which is most times you reload to check on something — you land on a locked form and need a click to get out of it. Creating a song is the more common first action, so it keeps the default. Clicking any queue entry is what loads it. There is a comment on that line so it does not get "fixed" later.
## Three things the old button-driven flow never had to handle
Selection is a much cheaper action than a button press, so cases that were previously unreachable are now one stray click away:
- **Advanced opens when the job has anything in it.** Genre, year, language, bpm and a kept cover all carry into a rebuild whether or not the section is expanded. Prefilling them behind a closed summary would mean rebuilding with values that were never on screen. This one I only found by driving the page — the round-trip test could not fill a hidden field.
- **Submit is disabled while the selected job is queued or running**, with the reason next to it, because `/rebuild` answers 409 until the job settles. `renderActions` already suppressed its controls mid-run; the form now agrees. It re-enables on the next poll when the job finishes.
- **Edits are not silently discarded.** Selecting a different entry confirms first if you have typed anything, and deleting the job the form is holding exits rebuild mode rather than leaving it pointed at an id that would 404.
One bug in my own first draft, caught before it shipped: the early return that avoids reloading the already-loaded job also skipped the selection change, so after a RERUN — which selects the new job while the form still holds the parent — clicking the parent's card did nothing. The reload and the selection are now separate.
## Testing
`tests/test_web.py` carries a mutation-testing harness over `index.html`: each checker has to reject a plausible regression of the thing it guards. Extended `_rebuild_flaws` with five new checks and added four mutations for them — selection not loading inputs, edits discarded without asking, advanced fields left hidden, an in-progress job submittable anyway, and the loaded job deleted out from under the form. `startRebuild` is renamed to `loadInputs`, and the existing guard and the `["edit", ...]` action-list mutation are updated to match.
Full suite: 284 passed, 1 skipped. `tests/test_audio_io.py` and `tests/test_separate.py` were not run — they import torch, which is not installable in the environment I had; neither touches this change.
Two pre-existing flaky tests, `test_upload_dir_cleaned_up_after_success` and `test_job_has_timestamps`, each fail roughly one run in three. I confirmed both flake the same way on an unmodified `origin/main` worktree. They are worker-thread timing, unrelated to this branch, which touches no Python outside the frontend guards.
Beyond the unit tests I drove the real page with Playwright and checked the states rather than assuming them: landing blank, a done job loading with its lyrics, a failed job also offering RERUN, a running job loading locked with the reason shown, the discard prompt both dismissed and accepted, and NEW SONG INSTEAD clearing back to a blank form. Then the full round trip — select a song carrying genre/year/language, confirm Advanced auto-opened with them visible, edit the lyrics and the year, submit, and verify the new job came back with the edits applied, the untouched fields carried through, `rebuilt_from` set, and the form reset.
`docs/images/webui.png` is regenerated. The screenshot script already clicks a song, so the committed screenshot now shows the form populated beside its matching review, which is the feature.
Selecting a queue entry filled the review panel but left the form empty, so
the only way to adjust a song was the EDIT & REBUILD button, and the only way
to see what a song was built from was to click it. Selection now loads the
job's inputs, which is what the form is for: pick a song, change a line of
lyrics, hit REBUILD SONG.
That makes the EDIT & REBUILD action redundant, so it goes. CANCEL becomes
NEW SONG INSTEAD, which is what it does now that the form is rarely empty.
The landing page deliberately keeps its blank form: the job auto-selected on
load fills the review panel only, so opening the page is still the way to
start a new song.
Three things the old button-driven flow never had to handle:
- Advanced is opened when the job has a genre, year, language, bpm or a kept
cover. Those carry into a rebuild either way, so leaving them behind a
closed summary meant rebuilding with values that were never on screen.
- Submit is disabled, with the reason shown, while the selected job is queued
or running, since /rebuild answers 409 until it settles.
- Selecting a different entry asks before discarding edits, and deleting the
job the form is holding drops it rather than leaving the form pointed at an
id that would 404.
branderson
approved these changes 2026-09-25 23:09:27 -07:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary generated by LLM agent
Selecting a queue entry filled the review panel but left the form blank, so the inputs a song was built from were only reachable through the EDIT & REBUILD button. Selection now loads them: pick a song, change a line of lyrics, hit REBUILD SONG. The rebuild endpoint already reruns only the stages your edit invalidates, so fixing a lyric realigns without re-downloading or re-separating.
No backend change was needed.
GET /api/jobs/{id}/inputalready existed and already returned the form's field names, and theinput.jsonsidecar already persists them across restarts. This is entirely a matter of the page not asking.Decisions worth flagging
EDIT & REBUILD is gone. With selection doing the loading, its only remaining job was moving focus, and two controls that both mean "edit this song" is worse than one. CANCEL becomes NEW SONG INSTEAD, which is what it now does — the form is rarely empty, so "cancel" no longer describes it.
The landing page still lands blank. The job auto-selected on load fills the review panel only; the form stays a blank NEW SONG. I built it both ways and checked the alternative in a browser first: routing the auto-selection through the same path means that whenever the newest job is queued or running — which is most times you reload to check on something — you land on a locked form and need a click to get out of it. Creating a song is the more common first action, so it keeps the default. Clicking any queue entry is what loads it. There is a comment on that line so it does not get "fixed" later.
Three things the old button-driven flow never had to handle
Selection is a much cheaper action than a button press, so cases that were previously unreachable are now one stray click away:
/rebuildanswers 409 until the job settles.renderActionsalready suppressed its controls mid-run; the form now agrees. It re-enables on the next poll when the job finishes.One bug in my own first draft, caught before it shipped: the early return that avoids reloading the already-loaded job also skipped the selection change, so after a RERUN — which selects the new job while the form still holds the parent — clicking the parent's card did nothing. The reload and the selection are now separate.
Testing
tests/test_web.pycarries a mutation-testing harness overindex.html: each checker has to reject a plausible regression of the thing it guards. Extended_rebuild_flawswith five new checks and added four mutations for them — selection not loading inputs, edits discarded without asking, advanced fields left hidden, an in-progress job submittable anyway, and the loaded job deleted out from under the form.startRebuildis renamed toloadInputs, and the existing guard and the["edit", ...]action-list mutation are updated to match.Full suite: 284 passed, 1 skipped.
tests/test_audio_io.pyandtests/test_separate.pywere not run — they import torch, which is not installable in the environment I had; neither touches this change.Two pre-existing flaky tests,
test_upload_dir_cleaned_up_after_successandtest_job_has_timestamps, each fail roughly one run in three. I confirmed both flake the same way on an unmodifiedorigin/mainworktree. They are worker-thread timing, unrelated to this branch, which touches no Python outside the frontend guards.Beyond the unit tests I drove the real page with Playwright and checked the states rather than assuming them: landing blank, a done job loading with its lyrics, a failed job also offering RERUN, a running job loading locked with the reason shown, the discard prompt both dismissed and accepted, and NEW SONG INSTEAD clearing back to a blank form. Then the full round trip — select a song carrying genre/year/language, confirm Advanced auto-opened with them visible, edit the lyrics and the year, submit, and verify the new job came back with the edits applied, the untouched fields carried through,
rebuilt_fromset, and the form reset.docs/images/webui.pngis regenerated. The screenshot script already clicks a song, so the committed screenshot now shows the form populated beside its matching review, which is the feature.