Skip to content

fix(turns): skip getReadable on already-cancelled attach - #920

Merged
btipling merged 2 commits into
mainfrom
plan/f5-cancelled-attach
Sep 1, 2026
Merged

fix(turns): skip getReadable on already-cancelled attach#920
btipling merged 2 commits into
mainfrom
plan/f5-cancelled-attach

Conversation

@btipling

@btipling btipling commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes F5 on a durable run that was cancelled hours ago: GET/POST no longer call getReadable() when run.status is already cancelled/failed (plain synthetic SSE). Subscribe-fail (Failed to fetch) no longer hot-resumes. Producer Request cancelled. without an abort clears running (operator attach Stop/Esc still keep-running). Live running/completed still use the #917 wrap.

Fixes #919
Fixes #918
Refs #915
Refs #916
Refs #816
Refs #794

Plan locks

Tests

Targeted + --changed: 10 files, 494 passed (includes +11 new cases: bodyForRun ×6, shouldKickHotResume ×4, producer-cancel fold ×1). tsc --noEmit green.

Baseline last merge-pr full suite was 3011. Full vitest run is the merge-pr gate, not this PR-open round.

Docs

  • docs/agent-stream.md End of turn
  • docs/architecture.md attach getReadable

Do not merge

implement-plan stop. Next: adversarial-review, then explicit merge.

F5 on a dashboard-cancelled run was wrapping getReadable() (pre-headers
Failed to fetch) and then hot-resuming because subscribe-fail stamped
envelope C onto heapApplied. bodyForRun returns synthetic SSE for
cancelled/failed without touching the SDK readable; attach Stop keep-running
requires an aborted signal so producer cancel clears the envelope.
@btipling btipling self-assigned this Sep 1, 2026
@vercel

vercel Bot commented Sep 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
invincible Ignored Ignored Sep 1, 2026 10:09pm UTC

Request Review

btipling commented Sep 1, 2026

Copy link
Copy Markdown
Owner Author

implement-plan for #919.

  • bodyForRun: already-cancelled/failed → synthetic SSE, no getReadable()
  • subscribe-fail does not hot-resume (shouldKickHotResume / streamOpened)
  • producer Request cancelled. without abort clears running; 6i/6j still keep-running
  • tsc --noEmit + vitest run --changed: 10 files / 494 passed
  • not merged; next is adversarial-review

@btipling btipling left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adversarial review — PR #920

Verdict: PASS WITH NOTES
Repo: btipling/invincible
Scope: mainplan/f5-cancelled-attach · 13 files · F5 cancelled-attach unstick (bodyForRun + host hot-resume gate)
Lenses run: L1, L2 (API trust), L3, L5, L6, L8 (skip: L4 no workflow/artifact; L7 no config-seam change; L9 no palette/Wasm chrome)
AGENTS.md read: yes

Findings

Sev Lens Finding Break scenario Refutation attempt Confidence
Minor L8 app/api/turns/[runId]/stream/route.ts file header + handler comment still describe attach as always getRun().getReadable(). Plan-review locked only bodyForRun may call getReadable, and only on the wrap branch. Next implementer reads the route comment, writes const readable = run.getReadable({ startIndex }); return new Response(await bodyForRun(run, readable)) (or calls getReadable then wraps). Cancelled-hours-ago F5 hits the SDK handle again → pre-headers Failed to fetch (#918). Tests on cancelled (row 13b / POST row 5b / bodyForRun unit) would fail if the current route called getReadable. They do not stop a future agent who “fixes” the comment-shaped code. Living docs/architecture.md prose is already correct; the route comment is what implement-plan reads first. high
Nit L8 docs/architecture.md mermaid still labels attach getRun.getReadable. Prose four lines later is the new contract. Diagram-only readers keep wrapping the SDK readable. Prose is the operator/agent contract; mermaid is schematic. high
Nit L6 Route tests lock cancelled skip-getReadable. failed skip is only in lib/agent/pipeRunReadable.test.ts. GET row 13 is named “completed run” but uses default status: 'running'. A route-local branch that special-cases only 'cancelled' would stay green. C16 wrap-on-completed is not locked at the route. Routes do not branch on status; bodyForRun unit tests cover failed + completed wrap. medium

Residual risk

Wrapped running/completed streams still call getReadable() + wrapper start()/getReader() inside the Function before the Response is returned — same shape as #917. If Preview ever proves every wrapped SDK readable pre-header-fails, this PR does not unwrap those paths (plan lock; follow-up). bodyForRun races run.status for ≤1s; hung/rejected status falls through to wrap (or GET 503) and Goal 2 (shouldKickHotResume) still stops the Busy loop, but Goal 1 (clear running) then depends on a later synthetic attach or G22 (#816). Synthetic cancelled/failed drops any buffered frames (hours-ago cancel does not need replay). Operator attach Stop/Esc still keep-running (6i/6j); dashboard cancel without abort now clears.

Merge guidance

  • PASS WITH NOTES: safe to merge from this attack; nits optional (route comment + mermaid + failed/completed route locks).
  • Do not merge until an explicit merge ask (merge-pr). Full vitest run is the merge gate, not this review.

What was not attacked

Live DO runner, prod Gateway, Vercel Workflows getReadable() of a real hours-ago cancelled run, Preview network waterfall, G22 cancel POST (#816).

Adversarial-review #920 follow-up: route comments and architecture mermaid
no longer describe always-getReadable attach. Route tests cover failed
skip-getReadable and completed wrap. Source-lock GET/POST never call
getReadable themselves.

btipling commented Sep 1, 2026

Copy link
Copy Markdown
Owner Author

Adversarial follow-up landed on 2c4115b:

  • GET stream route comments + architecture mermaid now describe bodyForRun (synthetic vs wrap), not always-getReadable
  • GET row 13 actually sets completed; GET row 13c / POST row 5c lock failed skip-getReadable
  • Source-lock: GET/POST route code never calls getReadable( — only bodyForRun

Targeted vitest: 3 files / 85 passed. Still PASS WITH NOTES; not merged.

btipling commented Sep 1, 2026

Copy link
Copy Markdown
Owner Author

Operator still broken on production (Preview skipped, this PR not merged): #922 — hang + stacked Failed to fetch, not the one-reject case this PR targets.

@btipling
btipling merged commit 9960b90 into main Sep 1, 2026
3 checks passed
@btipling
btipling deleted the plan/f5-cancelled-attach branch September 1, 2026 22:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

plan: F5 cancelled attach unstick without calling getReadable bug: F5 on cancelled durable run paints Failed to fetch and stays Busy

1 participant