Skip to content

fix(server, app): split abort into soft/hard modes; preserve pending question on soft (closes #553)#556

Merged
Astro-Han merged 1 commit into
devfrom
fix/abort-soft-hard-mode
May 11, 2026
Merged

fix(server, app): split abort into soft/hard modes; preserve pending question on soft (closes #553)#556
Astro-Han merged 1 commit into
devfrom
fix/abort-soft-hard-mode

Conversation

@Astro-Han

@Astro-Han Astro-Han commented May 11, 2026

Copy link
Copy Markdown
Owner

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:

  • Stop button, empty prompt Enter, Ctrl-G: soft
  • Escape, undo, revert, auto-heal restart: hard

The abort route still defaults to hard so 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

  • Added mode=soft|hard to session.abort.
  • Server-side soft abort checks SessionBlocker.hasAwaitingQuestion(sessionID) and returns false without cancelling if a question is pending.
  • Hard abort keeps the existing cancel path.
  • Added renderer diagnostics session.action.abort with source, mode, and result.
  • Added coverage for preserving a pending question and continuing after a reply.

Verification

bun --cwd packages/opencode test
2705 pass
9 skip
1 todo
0 fail
Ran 2715 tests across 207 files. [192.36s]
bun --cwd packages/app test --preload ./happydom.ts
1048 pass
0 fail
Ran 1048 tests across 156 files. [938.00ms]
bun --cwd packages/opencode typecheck
$ tsgo --noEmit
bun --cwd packages/app typecheck
$ tsgo -b
bun --cwd packages/opencode test test/server/session-actions.test.ts test/session/prompt-effect.test.ts --timeout 10000
51 pass
0 fail
Ran 51 tests across 2 files. [23.60s]
bun --cwd packages/app test src/components/prompt-input/submit.test.ts --preload ./happydom.ts
1048 pass
0 fail
Ran 1048 tests across 157 files. [899.00ms]
git diff --check
# pass

Summary by CodeRabbit

  • New Features

    • Introduced soft and hard abort modes for canceling in-progress sessions with improved control over termination behavior
  • API Changes

    • Extended session abort endpoint to support optional abort mode parameter (soft or hard)
  • Improvements

    • Enhanced session abort diagnostics with detailed source and outcome tracking
    • Refined session cancellation handling during concurrent operations

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 11, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 16077098-b957-4ad7-8c9b-799fa12a9421

📥 Commits

Reviewing files that changed from the base of the PR and between 3c15b37 and 855a405.

⛔ Files ignored due to path filters (2)
  • packages/sdk/js/src/v2/gen/sdk.gen.ts is excluded by !**/gen/**
  • packages/sdk/js/src/v2/gen/types.gen.ts is excluded by !**/gen/**
📒 Files selected for processing (10)
  • packages/app/src/components/prompt-input/keydown.ts
  • packages/app/src/components/prompt-input/submit.test.ts
  • packages/app/src/components/prompt-input/submit.ts
  • packages/app/src/pages/session.tsx
  • packages/app/src/pages/session/use-session-commands.tsx
  • packages/opencode/src/server/instance/session.ts
  • packages/opencode/src/session/prompt.ts
  • packages/opencode/test/server/session-actions.test.ts
  • packages/opencode/test/session/prompt-effect.test.ts
  • packages/sdk/openapi.json

📝 Walkthrough

Walkthrough

This 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.

Changes

Soft/Hard Abort Mode Implementation

Layer / File(s) Summary
Type Definitions & API Schema
packages/app/src/components/prompt-input/submit.ts, packages/opencode/src/server/instance/session.ts, packages/sdk/openapi.json
AbortMode enum ("soft" | "hard") and AbortSource enum ("stopButton" | "emptyEnter" | "ctrlG" | "escape" | "revert" | "autoHeal" | "undo") introduced. OpenAPI endpoint updated to accept optional mode query parameter.
Server Cancellation Logic
packages/opencode/src/session/prompt.ts, packages/opencode/src/server/instance/session.ts
SessionPrompt.cancel now accepts { mode?: "soft" | "hard" }, returns boolean indicating apply success, and short-circuits soft cancels when hasAwaitingQuestion is true (logs ignore, returns false). SessionBlocker wired into service layer. Server abort endpoint validates mode and calls SessionPrompt.cancel with mode, returning the result instead of constant true.
Client Abort Interface
packages/app/src/components/prompt-input/keydown.ts
PromptKeydownDeps.abort dependency changes from abort: () => void to abort: (source?: AbortSource, mode?: AbortMode) => void.
Diagnostic Emission
packages/app/src/components/prompt-input/submit.ts, packages/app/src/pages/session.tsx, packages/app/src/pages/session/use-session-commands.tsx
emitAbortDiagnostic helpers added to standardize session.action.abort renderer diagnostic emission, including source, mode, result, and session routing IDs.
Abort Call Sites
packages/app/src/components/prompt-input/submit.ts, packages/app/src/components/prompt-input/keydown.ts, packages/app/src/pages/session.tsx, packages/app/src/pages/session/use-session-commands.tsx
submit.ts abort function now accepts source and mode with defaults ("stopButton", "soft"), emits diagnostics on both queued and in-flight paths, maps response to aborted vs ignored_awaiting_question. Empty-submit path passes "emptyEnter" source for keyboard triggers. keydown.ts Escape calls abort("escape", "hard"), Ctrl+G calls abort("ctrlG"). session.tsx halt flow calls haltAbort with source ("revert" or "autoHeal") and mode "hard". use-session-commands.tsx undo calls abort with mode "hard" and emits diagnostic.
Tests & Validation
packages/app/src/components/prompt-input/submit.test.ts, packages/opencode/test/server/session-actions.test.ts, packages/opencode/test/session/prompt-effect.test.ts
submit.test.ts abort expectations updated to structured { sessionID, mode: "soft" } entries. session-actions.test.ts asserts cancel called with { mode: "hard" }. prompt-effect.test.ts adds e2e tests: soft cancel with pending question returns false and preserves question for answering; soft cancel without pending question returns true and interrupts loop with MessageAbortedError.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • [Bug] Question tool cancelled mid-propagation surfaces as a fake error to users #553 ([Bug] Question tool cancelled mid-propagation surfaces as a fake error to users): This PR's implementation of session.action.abort renderer diagnostics with source tracking (stopButton, emptyEnter, revert, autoHeal, undo) directly addresses the issue's suggested fix #2 to emit structured abort diagnostics from all non-submit abort entry points so the trigger is recoverable from session exports. The soft/hard cancel semantics also support the suggested fix #1 by allowing soft cancels to preserve pending questions during the propagation race window.

🐰 Soft and hard cancels, a waltz so fine,
Questions await, diagnostics align,
Abort sources tracked in the telemetry line,
Fuzzy logic dances—no more race to design! 🎪

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: splitting abort into soft/hard modes and preserving pending questions on soft abort, with the issue reference.
Description check ✅ Passed The description includes all key sections: Summary, Why (Root Cause), Implementation details, and comprehensive Verification results with multiple test suites passing.
Linked Issues check ✅ Passed The PR implements all three suggested fixes from issue #553: adds soft/hard abort modes preserving pending questions [#553], implements renderer diagnostics with source tracking [#553], and supports the differentiated abort behavior needed by all calling paths [#553].
Out of Scope Changes check ✅ Passed All changes are scoped to implementing soft/hard abort modes and related diagnostics as required by issue #553; no unrelated refactors or out-of-scope modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/abort-soft-hard-mode

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Astro-Han Astro-Han added bug Something isn't working P2 Medium priority app Application behavior and product flows harness Model harness, prompts, tool descriptions, and session mechanics labels May 11, 2026

@Astro-Han Astro-Han 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.

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 }) returns boolean, soft + hasAwaitingQuestion short-circuits to false. Hard path untouched.
  • API mode defaults 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:

  1. 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 returns cancelled=false).
  2. Answer continues the model after reply — same test asserts Fiber.await(fiber) exits Success, tool state.status === "completed", metadata.answers === [["Word"]]. This is the lock GPT-X said to weigh hardest; it is the lock the test was written for.
  3. 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 state and 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.
  4. No pending question → soft behaves like the old abort — covered by soft cancel without a pending question still interrupts the running loop (returns true, fiber exits with MessageAbortedError).

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

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Comment thread packages/app/src/pages/session/use-session-commands.tsx

@Astro-Han Astro-Han 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.

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.ask abort/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 mode defaults to hard.

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.

@Astro-Han

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 11, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Astro-Han Astro-Han merged commit dc1e9b8 into dev May 11, 2026
23 checks passed
@Astro-Han Astro-Han deleted the fix/abort-soft-hard-mode branch May 11, 2026 12:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app Application behavior and product flows bug Something isn't working harness Model harness, prompts, tool descriptions, and session mechanics P2 Medium priority

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Question tool cancelled mid-propagation surfaces as a fake error to users

1 participant