-
Notifications
You must be signed in to change notification settings - Fork 866
fix(cursor): do not re-label a completed turn as failed when the stream is aborted #2118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+122
−2
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
67 changes: 67 additions & 0 deletions
67
devlog/_plan/260819_unclaimed_bug_selection/090_1527_abort_slice.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| # 090 — #1527 abort-teardown slice | ||
|
|
||
| Branch: `fix/cursor-abort-teardown` off `fix/tray-registry-encoding`. | ||
| Commit: `346eaa80d`. PR: **#2118** → #2117 → #2116 → `dev`. | ||
|
|
||
| ## The plan pointed at the wrong line, and the ablation is what caught it | ||
|
|
||
| `000` said the fix site was the abort listener calling | ||
| `failAndClear("Cursor request was aborted")` at `live-transport.ts:1157`. That | ||
| reads correctly and is wrong. | ||
|
|
||
| Patching `failAndClear` left **all ten existing tests passing**. That is the | ||
| signal: a change to the actual failure path could not have been invisible. The | ||
| injected failure never reaches that helper — the `open()` seam's `fail` callback | ||
| at `:627` writes a local `failure` variable, and the throw happens later inside | ||
| `run()` at `:651` and `:661`. | ||
|
|
||
| Without the ablation this would have shipped as a green no-op. It is the same | ||
| lesson the campaign already recorded twice, arriving a third time in a new | ||
| costume: **a passing suite after a change proves nothing until you have seen | ||
| that suite fail.** | ||
|
|
||
| ## The second correction: unconditional was too wide | ||
|
|
||
| First working version returned on `emittedTerminal` alone. That broke an | ||
| existing contract test: | ||
|
|
||
| > "a cancel after a terminal was already emitted does not add a second one" — | ||
| > asserts the transport **still throws** the raw cancel. | ||
|
|
||
| That test is deliberate. The adapter's benign check (`cursor.ts:183`) swallows a | ||
| raw cancel one layer up, so the transport throwing it is how provenance stays | ||
| intact without a second terminal reaching the bridge. | ||
|
|
||
| Narrowed to `emittedTerminal && isCursorAbortError(failure)`. Both halves carry | ||
| weight: | ||
|
|
||
| | Condition | Without it | | ||
| |---|---| | ||
| | `emittedTerminal` | a mid-turn abort would be swallowed — nothing delivered, caller never told | | ||
| | `isCursorAbortError` | every post-terminal fault would change shape for the adapter | | ||
|
|
||
| ## Why an abort was not already covered | ||
|
|
||
| `isCursorBenignCancelError` deliberately excludes aborts — a mid-turn abort *is* | ||
| a real failure. And `expectedClose` is set only by `cancelCursorRun()`, so an | ||
| ordinary completion never qualified. A completed turn torn down afterwards fell | ||
| through both guards and was reported `turn-failed` with `expectedClose: false`. | ||
|
|
||
| ## Verification | ||
|
|
||
| ``` | ||
| bun test cursor-cancel-provenance + cursor-eof-terminal | ||
| + cursor-adapter + cursor-errors 46 pass / 0 fail | ||
| bun x tsc --noEmit exit 0 | ||
| ``` | ||
|
|
||
| Ablation: removing both guards gives `7 pass / 1 fail` — exactly the new | ||
| post-terminal-abort test, nothing else. | ||
|
|
||
| ## Scope | ||
|
|
||
| This is one of five residual parts of #1527 after #2054. The other four — | ||
| `kimi-k3` collapse at 79-95k, the `claude-fable-5` 429 asymmetry, full-replay on | ||
| first turn/restart/compaction, and request-shape parity — are acceptance work | ||
| that cannot start until #2054 lands, and the 429 half may be unprovable while | ||
| Connect hides `cache_read_tokens`. Hence `Refs`, not `Closes`. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the real
AbortSignalfires afterdoneis queued but before the iterator consumer handles it, returning here does not preserve completion:src/adapters/cursor.ts:130-133sees the aborted signal and replaces the yieldeddonemessage withCursor turn was aborted.The new test misses this because it injects an abort-shapedErrorwithout aborting anAbortController. Add an adapter-level regression using an actually aborted signal and either allow an already-emitted terminal through the adapter's abort gate or track terminal delivery beyond the transport queue.AGENTS.md reference: src/AGENTS.md:L17-L19
Useful? React with 👍 / 👎.