9.9 KiB
created, status, requirements, system_design, legacy_specs
| created | status | requirements | system_design | legacy_specs | ||
|---|---|---|---|---|---|---|
| 2026-09-15 | complete |
|
|
Implementation Plan: Interrupt running FIFO turns with Send Now
Overview
Allow a new Send Now request to replace an ordinary FIFO-delivered turn after its prompt handoff completes. First implement the shared ownership transition with deterministic backend regressions. Then verify the desktop/phone path, real transport errors, and public guidance. Implementation is complete.
The user accepted this contract change on 2026-09-15. It amends AC-UI-MESSAGE-QUEUE-SEND-NOW-001.8 and adds .9-.11. The existing replacement-turn ADR remains authoritative for cancellation and workflow behavior.
Evidence and reproduction
The reporter's Darwin build, commit 18d38c11a, launched Cursor ACP. An earlier
queued input was dispatched at 10:32:15 +0100. A second input was queued at
10:35:06. Five Send Now attempts from 10:35:11 through 10:35:38 returned
Another cancellation or Send Now operation is in progress. Cursor continued
producing output. No dynamic-profile selection failure was observed.
The source retained ordinary FIFO reservations in queuedDispatchAccepted
until settlement because the live transition was gated to Send Now. The
accepted phase triggered ErrSendNowConflict. This implemented the former
whole-turn exclusion in criterion .8; the repair now applies the same guarded
accepted-to-live transition to ordinary FIFO.
Existing TestSendQueuedNowConflictsAfterFIFOHandoffAccepted passes and proves
the provider-independent rejection through a real service FIFO drain. It does
not replay the reporter's entire Cursor process. The bundle does not identify
which conflict predicate fired, so incident attribution is strongly supported
rather than a directly logged guard branch.
Scope
In scope
- Shared accepted-to-live transition for FIFO and Send Now reservations.
- Handoff, cancellation, successor, and late-completion regression coverage.
- Fixed and persisted dynamic-to-Cursor session attribution in service tests.
- Desktop and phone reproduction through the existing queue panel.
- Real
WebSocketRequestErrorregression for Send Now conflict feedback. - Clarify queue steering in the existing public how-to sections.
Out of scope
- Provider adapters, dynamic routing policy, schema or protocol changes.
- Queue UI redesign, new copy, bulk header controls, or retry automation.
- Changes to explicit Cancel, queue editing, merging, or persistence policy.
Technical approach
Follow the paired design. Keep the accepted ownership record while promoting its phase at the existing successful guarded prompt-claim boundary. Remove the Send Now-only eligibility condition. Do not clear ownership to make the conflict disappear.
Rewrite the old whole-turn conflict test as two distinct cases: conflict before handoff completion and successful interruption after claim ownership. Preserve existing pending-FIFO supersession and live Send Now successor tests.
Current queue-api.ts already handles WebSocketRequestError. Verify this with
a Send Now-specific test; do not backport the reporter's old client code or
add redundant production mapping.
ASCII UI preview
UI-01: Existing expanded queue while FIFO input A is running. Shared composition; this package changes the action result, not layout.
[Agent is working on A]
Queue [Auto-run: ON] [Auto-merge: OFF] [Clear all]
B: urgent correction [Send Now] [row actions]
C: later work [Send Now] [row actions]
[Composer]
After Send Now on B:
[Cancel pending] -> [Agent is working on B]
Queue [Auto-run: ON] [Auto-merge: OFF] [Clear all]
C: later work [Send Now] [row actions]
[Composer]
Desktop exposes row actions on hover/focus. Phone exposes touch actions
without hover, with at least 44px targets. Reuse the shipped
mobile-message-queue-management.spec.ts inline panel: one queue scroll owner,
visible composer, no horizontal overflow. A genuine handoff conflict shows the
existing localized conflict toast, preserves pending rows, and permits retry
after completion. Labels above are illustrative; existing translations and
control order remain authoritative. Maps to .1, .2, .9, .11.
Tests
- .2/.7/.9:
TestSendQueuedNowCancelsLiveFIFOTurn, table-driven for entry/all scope and fixed/dynamic attribution, inqueue_send_now_test.go. - .8:
TestSendQueuedNowConflictsAfterFIFOHandoffAccepted, a barrier before successful guarded claim completion, in the same file. - .10:
TestStreamCompletePreservesLiveFIFOSuccessorForStalePromptGenerationplus the claim-to-provider-I/O half ofTestSendQueuedNowCancelsLiveFIFOTurnin the same file; retain existing captured-turn, duplicate-cancel, workflow, and restoration cases. - .9/.10:
TestSendQueuedNowSerializesFIFOPredispatchAdmissionholds ordinary FIFO A after guarded claim completion and before provider admission, races Send Now B, and verifies that B waits for A's bounded admission, replaces it exactly once, preserves C, and survives late A completion without stale turn, ownership, or prompt-attempt mutation. - Dispatch recovery:
TestPromptTaskReleasesDispatchGuardBeforeMissingExecutionRecoverydrives queued identity validation toErrExecutionNotFoundand verifies that fresh-launch recovery can reacquire the per-session guard without deadlock. - Model switching:
TestPromptTaskKeepsQueuedDispatchGuardThroughModelSwitchpauses provider model selection before prompt claim and verifies that the queued dispatch guard remains held across that I/O. - .11: real-class conflict test in
queue-api.test.ts.
Task 01 records the backend RED and GREEN results in its work order. Task 02 records the client, browser, and public-doc results after the backend change.
E2E tests
message-queue.spec.ts, chromium: ordinary FIFO starts A; wait for its distinct output, queue B/C, click B's Send Now, verify B once and C afterward.mobile-message-queue-management.spec.ts, mobile-chrome: the same scenario using tap, with visible touch controls and no horizontal overflow.- Use
watchWsbefore navigation/actions and API/agent-output evidence for lifecycle boundaries. Do not use elapsed sleeps to infer handoff completion. - Mock provider scripts make the scenario deterministic. Real Cursor credentials are not required; service tests cover dynamic concrete-execution identity.
Companion packages
The implemented original package remains historical delivery evidence. Its whole-turn exclusion is replaced by this package and amended criterion .8. Existing queue-run and automation behavior is unchanged. Do not relabel historical checks as proof of the new contract.
Work orders
Verification results
Diagnosis completed before implementation:
(cd apps/backend && go test -tags fts5 ./internal/orchestrator -run '^TestSendQueuedNowConflictsAfterFIFOHandoffAccepted$' -count=1)
PASS. This asserts the old rejection behavior, not the proposed fix. Package validation passed:
python3 scripts/list-docs.py validate: 272 decisions and 935 specifications.python3 scripts/lint-spec-files.test.py: 36 tests passed.python3 scripts/lint-spec-files.py --all: passed.- Package Markdown links and acceptance IDs resolve to existing artifacts.
git diff --check: passed.
Task 01 implementation results:
TestSendQueuedNowCancelsLiveFIFOTurn: PASS for entry/all scope, fixed and dynamic-to-Cursor execution attribution.TestSendQueuedNowConflictsAfterFIFOHandoffAccepted: PASS with a prompt-claim barrier; the accepted handoff still fails closed.TestStreamCompletePreservesLiveFIFOSuccessorForStalePromptGeneration: PASS; a stale predecessor cannot settle the live FIFO successor.TestSendQueuedNowSerializesFIFOPredispatchAdmission: PASS; the ordinary FIFO cancellation guard remains held from live promotion through provider acceptance, then B replaces A exactly once, C remains queued, and a late A completion cannot mutate B's turn, ownership, or prompt-attempt record.TestPromptTaskReleasesDispatchGuardBeforeMissingExecutionRecovery: PASS; queued identity validation can enter missing-execution recovery only after releasing the transferred dispatch guard.TestPromptTaskKeepsQueuedDispatchGuardThroughModelSwitch: PASS; queued provider model selection remains inside the dispatch admission guard.go test -tags fts5 -race ./internal/orchestrator -run 'Test.*(SendNow|SendQueuedNow|QueuedDispatch|FIFOHandoff)' -count=1: PASS.git diff --check: PASS.
Task 02 implementation results:
pnpm exec vitest run lib/api/domains/queue-api.test.ts: 30 tests passed.- Web typecheck, Prettier, and targeted ESLint passed.
- Chromium Send Now subset: 3 tests passed, including the FIFO interruption scenario.
- Mobile Chrome Send Now subset: 3 tests passed, including the FIFO interruption scenario and overflow check.
- Public-doc tests: 62 passed; public-doc validation covered 46 pages.
- Specification catalog validation covered 272 decisions and 935 specifications; specification lint passed.
git diff --check: passed.
Risks
- Removing accepted ownership rather than changing phase can admit duplicate drains or let a late completion clear the replacement.
- The claim-to-provider-I/O window must remain serialized against cancellation.
- Existing tests intentionally assert the old contract and need a real handoff barrier; simply deleting their assertions would remove race protection.
- The reporter ran an older build. The generic-toast mapping is already repaired on this branch and needs a regression test, not another production fix.