Skip to content

fix: treat a confirmed dialog as confirmed, and stop pinning a prepared model - #38

Merged
smb merged 2 commits into
mainfrom
fix/confirmation-answer-identity
Sep 4, 2026
Merged

smb merged 2 commits into
mainfrom
fix/confirmation-answer-identity

Conversation

@smb

@smb smb commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Closes #37
Closes #36

Summary

Two defects in the same area, found while investigating a report that models could not be deleted. The first is the one that blocked the user.

#37: every confirmation in the application was ignored. QMessageBox.question() returns a plain int, not a StandardButton member. Measured on PySide6 6.10.3 by clicking the real dialog:

returned: 16384   type: int
is  Yes: False
==  Yes: True

Four call sites compared with is, so pressing Yes was read as a refusal: removing a model weight, Delete outdated, Re-download outdated, and deleting a pinned cut set. The dialog appeared, the user pressed Yes, and nothing happened at all — no action, no message, no error.

#36: a model prepared once could never have its weight deleted again. _active_spec is cleared only in close(), which runs at application shutdown, so the default model was locked for the whole run and the refusal showed up only as a dead button with a tooltip.

Why the suite did not catch #37

Every test monkeypatched QMessageBox.question to return QMessageBox.StandardButton.Yes, the canonical enum member, for which identity does hold. The stub returned something Qt never returns, so the tests asserted the opposite of production. My own first four reproductions passed for exactly the same reason; it only reproduced once a real modal dialog was driven to completion.

The stubs now return int(QMessageBox.StandardButton.Yes), and one new test uses no stub at all: it opens the real confirmation, finds the active modal window, clicks the button whose role is YesRole, and asserts the weight is gone. Deliberately reverting one source fix makes that test fail, which was checked rather than assumed.

The #36 guard, narrowed rather than removed

The guard was stricter than the thing it protects. The segmentation child builds its session from the model bytes and holds no handle on the file afterwards, and prepare() returns the cached result for the same model without reading the file again, so deleting the active weight breaks nothing that is running. The next switch away and back downloads it, which the maintainer accepted as the consequence.

Removal now refuses only while the model is in the cleanup set — the post-failure state where the child is not known to be healthy — and the error names that state. The dialog's guard drops its active-model check as well; refusing while a job runs is what covers a preparation actually in flight. A blocked click now puts its reason in the status line instead of returning in silence.

jobs/models/session.py shrinks from 439 to 437 lines; the baseline records it.

Test plan

  • uv run ruff check . && uv run mypy src && QT_QPA_PLATFORM=offscreen uv run pytest -q: 1620 passed, 4 skipped.
  • New: removal through the real confirmation dialog with no stub; the active model's weight can be removed while a model in the cleanup set still raises MODEL_IN_USE; a blocked click reports its reason.
  • Verified by hand against the real dialog and the production runtime that the active model's Remove button is now enabled.

🤖 Generated with Claude Code

smb and others added 2 commits September 4, 2026 20:45
QMessageBox.question returns a plain int, not a StandardButton member:
on PySide6 6.10.3 pressing Yes yields 16384, and `16384 is
StandardButton.Yes` is False while `==` is True. Four call sites
compared the answer with `is`, so every confirmation in the
application was read as a refusal. The dialog appeared, the user
pressed Yes, and nothing happened at all: no removal, no download, no
message, no error.

Affected: removing one model weight, Delete outdated, Re-download
outdated, and deleting a pinned cut set. All four now compare with
equality, and the one that reads least obviously carries a comment
saying why, so it does not get tidied back into an identity check.

The suite could not have caught this. Every test monkeypatched
QMessageBox.question to return the canonical enum member, for which
identity does hold, so the stub asserted the opposite of what Qt does.
The stubs now return int(StandardButton.Yes), which is what Qt returns,
and one test drives a real QMessageBox to completion by finding the
active modal window and clicking its YesRole button. That test needs no
stub at all and guards the whole class rather than these four
instances.

Closes #37

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A model prepared for one preview stayed the session's active model for
the rest of the run: _active_spec is cleared only in close(), which
runs at application shutdown. Removing that weight raised MODEL_IN_USE
and the button stayed dead, so the default model could never be deleted
without first preparing a different one. Nothing in the interface said
so.

The guard was stricter than the thing it protects. The segmentation
child builds its session from the model bytes and holds no handle on
the file afterwards, and prepare() returns the cached result for the
same model without reading the file again. Deleting the active weight
breaks nothing that is running; the next switch away and back downloads
it, which the maintainer accepted as the consequence.

Removal now refuses only while the model is in the cleanup set, the
post-failure state where the child is not known to be healthy, and the
error says that rather than naming an active session. The manager's
active-model check is gone from the dialog's guard too; the refusal
while a job runs is what covers a preparation actually in flight.

A blocked click now puts the reason in the status line instead of
returning in silence, as the neighbouring actions already do.

jobs/models/session.py shrinks from 439 to 437 lines; the baseline
records it.

Closes #36

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@smb
smb merged commit be788fc into main Sep 4, 2026
11 checks passed
@smb
smb deleted the fix/confirmation-answer-identity branch September 4, 2026 18:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant