Skip to content

fix(server): keep Claude sessions alive for owed wake turns - #36029

Open
syanga wants to merge 1 commit into
pingdotgg:mainfrom
syanga:fix/claude-notice-ownership
Open

syanga wants to merge 1 commit into
pingdotgg:mainfrom
syanga:fix/claude-notice-ownership

Conversation

@syanga

@syanga syanga commented Nov 13, 2026 •

Copy link
Copy Markdown

When a Claude background command exits, its task can leave the live roster while its native notice turn is still owed. The adapter publishes pending background work during that gap, but the session-lifetime predicate returned false. An idle session release could therefore close the process before the notice turn was handled.

The lifetime predicate now consults the existing owedWakeTasksForNativeThread relation for every remembered native thread. It covers awaited and unawaited notices, adds no state or replay, and leaves query replacement unchanged. Existing tests verify release after consumption, continuation retirement, process reset, and Stop.

Assumed decisions

  • Honor all existing owed notices, including unawaited ones. Retention until consumption or retirement is intentional and remains subject to the existing session-manager limits.

Verification

  • Before the fix, four deterministic adapter cases failed because lifetime returned false after task notification, including empty-roster ordering and sibling-task cases.
  • After the fix: vp run check:changed passed on head 538393a5: 409 tests across both selected files, including the whole Claude adapter file; format, lint, both Knip passes, GraphQL verification, and all three affected typechecks passed.
  • Independent standards, spec, and Claude correctness reviews found no defects. The three updated legacy lifetime assertions were checked against owed ownership and their release controls. A fresh reviewer verified that the publication rebase preserved the same patch and release controls.
  • Offline adapter protocol fixtures establish host ownership behavior. No live Claude dispatch, cancellation acknowledgment, or resumed-context normalization was verified.

Signed review: #36029 (review)

The test-status helper could not publish statuses because this account lacks upstream push access. Full local result:

{
  "tested": "538393a5da398f371435f8d63569b3d776ebef2e",
  "base": "0b0a61a01f2fd35319cfc55cb9a4145ec3c27593",
  "command": "vp run check:changed",
  "suites": {
    "apps/server/ClaudeAdapterV2": "pass",
    "check-changed": "pass"
  },
  "summary": "409 tests across 2 selected files, including whole ClaudeAdapterV2.test.ts; format, lint, both Knip passes, GraphQL and all three affected typechecks pass.",
  "scope": "Changed Claude adapter and repository-selected adjacent tests and affected checks.",
  "limits": "Offline protocol fixtures; no live Claude dispatch/cancellation or resumed-context verification. Full repository CI/build not run locally per repository instructions."
}

Related investigation: syanga/astack#165 remains open. This PR addresses only T3 session ownership; cancellation provenance and resumed-context correction remain separate.

Implemented with GPT-6 Astra through Codex in T3 Code; correctness reviewed with Claude Opus 5.5.

@syanga syanga left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

[gpt-6-astra] on behalf of Alan

Reviewed 538393a against 0b0a61a. Converged with no remaining findings.

Standards

Fresh native review found no documented-standard breaches or actionable smells. Existing domain helper reused; tests extend observable behavior.

Spec

Fresh native review found no missing requirements or scope creep. All owed wakes include unawaited notices; no new state/replay; query replacement preserved. Explicit audit of legacy assertions confirmed pending ownership and retained consumption, retirement, reset and Stop release controls.

Correctness

Outside Claude Opus 5.5 review found no defects and recommended shipping. Inspected ownership/helper, tombstones, consumption, retirement, reset/Stop, replacement and ProviderSessionManager idle release. Accepted unconsumed-notice retention remains bounded by existing session-manager policy. No tests run by reviewer; fiber timing and every rememberProviderThread caller were not exhaustively traced.

Original reviewed candidate a2097937d3eda18996fbda13ca1c1e6149d8e0b8 was rebased onto upstream test refactoring. A fresh native focused review confirmed identical ordered patch changes after whitespace normalization, byte-identical production code, preserved fixture messages/release controls, and unchanged lifetime caller. Earlier reviews remain applicable to this head.

Required vp run check:changed passed: 409 tests, format/lint/Knip/GraphQL/three typechecks. No live Claude cancellation/provenance claims; astack #165 remains open.

Remaining findings: Standards 0; Spec 0; Correctness 0.

@github-actions github-actions Bot added vouch:unvouched Author is not yet trusted in the VOUCHED list. size:XS 0-9 changed lines (additions + deletions). labels Nov 13, 2026
@macroscopeapp

macroscopeapp Bot commented Nov 13, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 538393a

Macroscope's review found this PR approvable — Claude sessions now remain alive while already-owed wake turns are pending, preventing idle cleanup from discarding that work. The change is confined to existing session lifecycle tracking and introduces no new product defaults or permissions.

You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

ClaudeAdapterV2.hasPendingBackgroundWork now returns true when any remembered native thread has owed wake tasks. Tests cover pending-work retention across empty roster snapshots, task notifications, and process replacement, plus release after wake processing or an unstarted run.

Changes: Owed wake tracking

Layer / File(s) Summary
Pending-work detection and wake lifecycle
apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts, apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts
hasPendingBackgroundWork checks remembered native threads for owed wake tasks. Tests verify that pending work remains true across empty snapshots, notifications, wake initialization, and process replacement, and becomes false after wake processing or onRunEndedWithoutStarting.

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Possibly related issues

  • pingdotgg/t3code#30156 — The issue reports an awaited task lost when the Claude process exits while idle. This PR covers owed-wake retention but does not address the reported process-death path.

Possibly related PRs

  • pingdotgg/t3code#34568 — Both PRs use owed-wake state; this PR consults that state when determining whether a session has pending background work.

Suggested labels: bugfix

Suggested reviewers: juliusmarminge, macroscopeapp, t3dotgg

Merge Risk: Minimal · up to 53839

Claude sessions now stay alive while a wake notice is owed, so idle release no longer closes the process early. A settings-triggered query replacement can still close the process, but that behavior is not new in this PR. No merge-blocking risk remains.

Pre-merge checks: Passed 4

Check name Status Explanation
Title check Passed The title names the main change: session lifetime now accounts for owed wake turns, so Claude sessions stay alive until those turns are handled. It follows the conventional commit format and is specif…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description check Passed The description follows the template's main purpose and gives a clear account of the problem, change, scope rationale, and verification. It names the root cause: the lifetime predicate ignored owed wa…
Full details: Title check

Explanation

The title names the main change: session lifetime now accounts for owed wake turns, so Claude sessions stay alive until those turns are handled. It follows the conventional commit format and is specific to the changeset.

Full details: Description check

Explanation

The description follows the template's main purpose and gives a clear account of the problem, change, scope rationale, and verification. It names the root cause: the lifetime predicate ignored owed wake tasks after a background command exits. The Change section explains the fix. Scope cites an investigation issue and states that the PR fixes only T3 session ownership. Verification gives the failing-before and passing-after results, the test counts, and the limits of offline fixtures. The template asks for a conventional commit title, and the description does not give one. It also has no separate Problem heading, and the Scope section does not clearly say the change is a bug fix with a linked bug issue. These gaps are minor, so the check passes.

Finishing Touches

Generate unit tests (beta)

  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Recent review info

Run configuration

Commits

Reviewing files that changed from the base of the PR and between c29c26a and 538393a.

Files selected for processing: 2

  • apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts
  • apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XS 0-9 changed lines (additions + deletions). vouch:unvouched Author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant