fix(server, app): split abort into soft/hard modes; preserve pending question on soft (closes #553)#556
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (2)
📒 Files selected for processing (10)
📝 WalkthroughWalkthroughThis PR implements soft/hard abort modes with diagnostic emission across the abort lifecycle. Hard cancellation forces immediate abort; soft cancellation respects pending questions by returning false without interrupting. Client abort entry points (keydown, submit, session, undo) now pass structured source and mode parameters. The server endpoint validates mode and returns success/failure. Renderer diagnostics track abort events with source, mode, and result. ChangesSoft/Hard Abort Mode Implementation
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Astro-Han
left a comment
There was a problem hiding this comment.
Opus second-pass review (product layer / three questions / soft-hard mapping)
Conclusion: approve
Diff stays within the D scope GPT-X locked. Server-side soft no-op path is exactly the hasAwaitingQuestion extension we asked for, and the four-lock acceptance is observable in tests.
Three questions
Could this be even less? 218+/35- across 12 files. The bulk is necessary: SDK regen (3 files), diagnostic emit at five abort entry points, two new acceptance tests. No drift into question continuation architecture. Check.
Is what remains good enough?
- Server
cancel(sessionID, { mode })returnsboolean, soft +hasAwaitingQuestionshort-circuits tofalse. Hard path untouched. - API
modedefaults to"hard"server-side, so legacy clients without the new field keep the previous full-cancel behavior. Backward compatible. - Diagnostic shape
{ source, mode, result }covers all six callers (stopButton / emptyEnter / ctrlG / escape / undo / revert / autoHeal). Next session export will be self-explanatory. - Check.
Is it reassuring? Acceptance traced against GPT-X's four locks:
- Stop / empty Enter / Ctrl-G with pending question keeps it answerable — covered by
soft cancel preserves a pending question and continues after reply(questions.list remains 1, server returnscancelled=false). - Answer continues the model after reply — same test asserts
Fiber.await(fiber)exitsSuccess, toolstate.status === "completed",metadata.answers === [["Word"]]. This is the lock GPT-X said to weigh hardest; it is the lock the test was written for. - Hard cancel / revert / auto-heal still clears the pending question — not directly asserted by a new test; the hard path is unchanged code, so existing
cancel finalizes subtask tool stateand surrounding cancel coverage carries the contract. GPT-X may want a tighter assertion that revert leaves no stale blocker, but I do not view its absence as a blocker. - No pending question → soft behaves like the old abort — covered by
soft cancel without a pending question still interrupts the running loop(returnstrue, fiber exits withMessageAbortedError).
Findings
P3-1 (not blocking): Escape is marked hard to match its current behavior. Ctrl-G is soft. Both are working-state keyboard shortcuts, so the inconsistency is a latent UX puzzle for a future pass. GPT-X's red line explicitly kept Escape out of scope for #553, so this PR is correct to freeze it. Worth a one-line follow-up issue once we have user feedback on whether Escape should drop into the soft bucket.
P3-2 (nit, do not change): submit.ts abort() default is ("stopButton", "soft"). The default is fine because all three Stop entry points (button click, empty Enter, Ctrl-G) want soft; Escape supplies an explicit override. Slightly implicit, but matches the prevailing convention in this file.
Approve
There was a problem hiding this comment.
Code Review
This pull request introduces a "soft" abort mode for sessions, allowing cancellation requests to be ignored if a user question is currently pending. The changes update the abort API across the SDK, server, and frontend to support mode ("soft" or "hard") and source parameters, while also adding diagnostic logging for these actions. Feedback highlights duplicated diagnostic logic across multiple files and potential inconsistencies in session ID reporting, suggesting a centralized helper function to ensure maintainability and correctness.
Astro-Han
left a comment
There was a problem hiding this comment.
Engineering review passes.
I checked the diff against the approved soft/hard abort boundary:
- Soft abort is limited to Stop, empty prompt Enter, and Ctrl-G.
- Soft abort only no-ops when the server has an awaiting question; otherwise it still interrupts the running loop.
- Hard abort keeps the existing cancel path, so undo, revert, auto-heal restart, and Escape still cancel the turn.
- The question lifecycle and
Question.askabort/failFromAbort path are not reworked. - The added opencode test covers the important continuation case: a pending question survives soft cancel, the user replies, the question tool completes, and answers metadata is recorded.
- Legacy clients remain compatible because omitted
modedefaults tohard.
Local verification I reran:
bun --cwd packages/opencode test test/server/session-actions.test.ts test/session/prompt-effect.test.ts --timeout 10000
51 pass
0 fail
bun --cwd packages/app test src/components/prompt-input/submit.test.ts --preload ./happydom.ts
1048 pass
0 fail
git diff --check dev...HEAD
pass
The Gemini diagnostics-helper thread is valid tech debt, but it is not a blocker for this bugfix. The route/visible/timeline IDs have caller-specific semantics, so extracting a helper should be handled separately without diluting this PR's abort semantics change.
Note: GitHub would not allow this connector identity to submit an approval because it is the PR author identity, so this review is recorded as a COMMENT rather than an APPROVE event.
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
Summary
Closes #553.
This splits session aborts into two modes:
soft: stop generation only. If the session is already waiting on a pending question, the server ignores the abort and keeps the question answerable.hard: cancel the turn. This preserves the old behavior for undo, revert, and question recovery auto-heal.Client mapping:
softhardThe abort route still defaults to
hardso older clients keep the previous behavior.Root Cause
A user stop signal and a newly emitted question could race. The old abort path treated every stop as a full turn cancel, so a pending question could be cancelled after it was emitted but before the client had time to render it.
Implementation
mode=soft|hardtosession.abort.SessionBlocker.hasAwaitingQuestion(sessionID)and returnsfalsewithout cancelling if a question is pending.session.action.abortwithsource,mode, andresult.Verification
Summary by CodeRabbit
New Features
API Changes
softorhard)Improvements