Agents-busy 409s no longer fail the operation: install, update and
uninstall refusals become queued records that drain automatically
once agents go idle and the operation lock is free.
- /status exposes runningAgents so the client can drain without an
extra round trip
- queued records persist in localStorage (dshm-queue-v1) across
remounts; Tasks panel keeps dequeue + run-now actions
- spec: queue/drain/persist client tests + /status guard test
Maintainer pass on top of @NONEMIN's branch, which had been rebased and
reviewed but not updated for two weeks:
- **A queued row may only run while it still applies.** It drains with no
confirmation, so a row restored from an old session is a destructive
operation launched from a decision the user may have taken back: queue an
uninstall at 10:00, remove the plugin by hand, open the market at 15:00 and
it would run. `queuedRowApplies` (src/client/market-data.ts) requires the
install's catalog entry, the package's presence for uninstall, and a
still-pending update for update; a row that no longer applies is REPORTED
with its reason rather than executed or silently dropped.
- **The queue was lost on every refresh with a cold catalog.** The persist
effect ran on the first render — where `records` is empty — and deleted
`dshm-queue-v1` before the restore, which waits for the catalog, could read
it. The writer now waits for the restore, and the storage item is consumed
only once the rows are actually restored.
- The rule is a unit-tested function rather than a restore-path closure: the
two `update` branches differ by one map lookup and are indistinguishable on
screen.
Verified: 1796 unit tests (four new in tests/client/queued-rows.client.spec.ts,
three new DOM tests pinning "not executed, and said so"), five mutations each
turning exactly one named test red (installed check, update-available check,
catalog check, stale rows dropped instead of reported, storage consumed before
the catalog arrives). npm run check clean, client bundle rebuilt.
Co-authored-by: NONEMIN <199695791+NONEMIN@users.noreply.github.com>