fix(web): recover from quiet or stalled connections - #370
Conversation
tt-a1i
left a comment
There was a problem hiding this comment.
Exact-head review: 72b334d
[P1] Timing out prompt admission creates an uncertain mutation that the UI presents as a safe retry. web/ui/app.js:803-825 aborts POST /api/prompt after 30 seconds, removes the optimistic row, restores the draft, and reports failure. The server path at web/host/web-host.ts:492-527 has already generated a new command id and may still complete runtime.sendPrompt(); it neither binds client disconnect to cancellation nor accepts a client idempotency key. Retrying the restored draft therefore creates another command id and can execute the same model/tool turn twice.
Please make admission retry-safe: use a stable client-generated idempotency key with server-side deduplication and a queryable receipt, or expose timeout as unknown/pending and reconcile canonical state before allowing retry. Add a test where the client times out just before the first admission completes, retries, and proves only one turn is admitted.
Verification boundary: exact-head cross-layer Standards + Spec review. Existing liveness tests cover bounded waiting, not the timeout/late-success/retry race.
Problem
The OpenPI Web event stream could remain silently half-open, and quiet streams gave the browser no bounded way to verify canonical state. API and prompt-admission waits were also unbounded, leaving users without actionable feedback when a connection stalled. This completes the remaining liveness/recovery slice of #363 and is intentionally separate from the state-regression work in #364.
Value
Web clients now detect silent failures, periodically reconcile quiet connections with the canonical snapshot, and retain prompt drafts with explicit localized feedback when admission times out.
Approach
AbortController; preserve failed prompt drafts and surface distinct pending, timeout, error, and reconnecting feedback.Validation
bun run check— passed.bun run test— passed: Node 1244 passed / 1 platform skip; Vitest 30 passed.Impact