fix(server): usage-limit auto-resume no longer arms children of settled parents#27951

merged4
main
fix/issue-27944
opened3d agobyJJulius Marminge
View on GitHubTwo-way sync enabledPR last imported 18h ago

Problem

Turning on Resume after usage limits made the usage-limit recovery sweep arm and resume delegated child threads that had stopped on a usage limit while the setting was off, even when their parent thread was settled. When a resumed child finished, its delegated-completion wake could pull the settled parent back to active and start a new run. The candidate query only checked the child's own archivedAt and settledOverride and never looked at its lineage.

Reproduced on main (2b15ba077d) with a projection-level test. It builds two parent/subagent pairs whose children failed on a usage limit and have nothing armed; one parent is settled. With autoResume on (and separately with snooze on), both children get a limit-arm: command, including the child of the settled parent.

Change

getLimitRecoveryCandidates gets one extra condition in the branch that freshly arms a recovery (both the auto-resume arm and the snooze arm). A thread whose lineage.relationshipToParent is subagent and whose direct parent has settledOverride = 'settled' is not offered for a new arm. The parent lookup goes through the threads table's primary key and only runs for rows that already passed the usage-limit pre-filter.

The resume branch is unchanged: a recovery that was already armed for the child's latest failed run still resumes at its reset. Unsettling the parent emits a thread.* event, which already makes the sweep due, so the child becomes eligible again on the next tick.

Scope and approval

Fixes pingdotgg/t3code#27944

Verification

  • New test ProjectionStoreV2 > does not freshly arm limit recovery for a subagent of a settled parent in apps/server/src/orchestration-v2/ProjectionStore.test.ts. It checks that a child of a settled parent is not armed under autoResume or snooze, that a child of an unsettled parent still is, and that a child armed before its parent settled still produces its limit-resume: dispatch.
  • Red with main's ProjectionStore.ts (2b15ba077d):
    1× does not freshly arm limit recovery for a subagent of a settled parent
    2AssertionError: only the child of the unsettled parent is armed ({"now":"1970-01-01T00:00:00.000Z","autoResume":true,"snooze":false}): expected [ …(2) ] to deeply equal [ Array(1) ]
    3 actual: [ 'thread:settled-parent-limit:active:child', 'thread:settled-parent-limit:settled:child' ],
    4 expected: [ 'thread:settled-parent-limit:active:child' ]
    5Tests 1 failed | 44 skipped (45)
  • Green with the fix (003df33f85): Tests 1 passed | 44 skipped (45).
  • ProjectionStore.test.ts and UsageLimitRecoveryWorker.test.ts: 46/46 pass. runtimeLayer.test.ts -t "limit": 20 pass. vp run check:changed passes (format, lint, knip, typecheck, tests).
  • Server-only change with no visible surface, so there are no captures. Not checked: a live provider hitting a real usage limit, which a VM cannot trigger. The proof is the projection test above.

Claude Opus 5.5 (Claude Code in T3 Code; reviews by GPT-6.1 Sol on Codex and Claude Opus 5.5).

🤖 Generated with Claude Code

Checks

21 successful · 18 skipped
All checks passed
8f9e45d

Added 3 commits

  • fix(server): usage-limit auto-resume no longer arms children of settled parents003df33
  • Merge remote-tracking branch 'origin/main' into fix/issue-279445abe8a0
  • test(server): reproduce #27944 settled-parent subagents freshly armed for usage-limit resume8f558db
Synced from GitHub

Approvability

Verdict: Approved at 5abe8a0

Macroscope's review found this PR approvable — Fresh usage-limit recovery will no longer arm subagents of explicitly settled parents, preventing abandoned work from waking those parents. Already-armed recoveries and product preference defaults remain unchanged.

No code changes detected at 8f9e45d. Prior analysis still applies.

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

Synced from GitHub

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire5.0 KiB5.0 KiB0 B (0.0%)6.8 KiB✅
CodexThread snapshot wire3.8 KiB3.8 KiB0 B (0.0%)4.9 KiB✅
CodexLive turn WebSocket wire1.2 KiB1.2 KiB0 B (0.0%)2.0 KiB✅
CodexLive turn WebSocket decoded21.6 KiB21.6 KiB0 B (0.0%)24.4 KiB✅
CodexLive turn messages220 (0.0%)8✅
ClaudeTotal thread wire5.0 KiB5.0 KiB−18 B (−0.3%)6.8 KiB✅
ClaudeThread snapshot wire3.8 KiB3.8 KiB0 B (0.0%)4.9 KiB✅
ClaudeLive turn WebSocket wire1.2 KiB1.2 KiB−18 B (−1.4%)2.0 KiB✅
ClaudeLive turn WebSocket decoded21.9 KiB21.9 KiB−41 B (−0.2%)24.4 KiB✅
ClaudeLive turn messages21−1 (−50.0%)8✅

Baseline: f6503f0 · PR result: 8f9e45d · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

Synced from GitHub

Review in Change Stack →

No actionable comments were generated in the recent review.

Walkthrough

The limit-recovery candidate query now excludes subagent threads whose parent has settledOverride set to "settled". Tests check fresh recovery selection under two option sets and verify that an already-armed recovery can still produce a resume command after its reset time.

Changes: Usage-limit recovery

Layer / File(s)Summary
Filter fresh recovery candidates
apps/server/src/orchestration-v2/ProjectionStore.ts, apps/server/src/orchestration-v2/ProjectionStore.test.ts
The query excludes subagent threads when their parent has settledOverride set to "settled". Tests cover both option sets and confirm that an already-armed recovery can still yield a resume command after the reset time.

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

Change: Bug fix · Severity of issue fixed: Medium

Possibly related issues

Suggested labels: size:M

Suggested reviewers: t3dotgg, macroscopeapp

Merge Risk: External image Minimal · up to 8f9e4

Fresh recovery selection excludes children of settled parents while preserving already-armed recoveries. No merge-blocking issue was identified.

Pre-merge checks: Passed 3 · Failed 1

Failed Failed checks: 1 warning

Check nameStatusExplanationResolution
Linked Issues checkWarningIssue #27944 requires that a child whose direct parent is settled is not resumed by the usage-limit sweep. ProjectionStore.ts excludes the child only when the sweep would create a fresh recovery arm…Also exclude subagents of settled parents from the already-armed resume path, or cancel their recovery before dispatch. Keep a regression test that settles the parent after arming and verifies that no resume dispatch occurs.
Passed Passed checks: 3 passed
Check nameStatusExplanation
Title checkPassedThe title clearly and concisely describes the main server fix: preventing usage-limit auto-resume from freshly arming children of settled parents.
Description checkPassedThe description includes all required sections: Problem, Change, Scope and approval, and Verification. It explains the bug, implementation, linked issue, focused tests, observed results, and untested …
Out of Scope Changes checkPassedThe changes stay within issue #27944. The production change updates usage-limit candidate selection, and the test covers settled and unsettled parents, auto-resume, snooze, and existing recovery behav…
Full details: Description check

Explanation

The description includes all required sections: Problem, Change, Scope and approval, and Verification. It explains the bug, implementation, linked issue, focused tests, observed results, and untested live-provider scenario.

Full details: Out of Scope Changes check

Explanation

The changes stay within issue #27944. The production change updates usage-limit candidate selection, and the test covers settled and unsettled parents, auto-resume, snooze, and existing recovery behavior.

Full details: Linked Issues check

Explanation

Issue #27944 requires that a child whose direct parent is settled is not resumed by the usage-limit sweep. ProjectionStore.ts excludes the child only when the sweep would create a fresh recovery arm. UsageLimitRecoveryWorker.ts still dispatches limit-resume: when an existing recovery matches the latest failed run, without checking the parent's settlement. The added test confirms this behavior for an already-armed child.

Resolution

Also exclude subagents of settled parents from the already-armed resume path, or cancel their recovery before dispatch. Keep a regression test that settles the parent after arming and verifies that no resume dispatch occurs.

Finishing Touches
Generate docstrings
  • Commit to this branch
  • Create a new PR
Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Some tools did not complete. Review the errors below.

External image ast-grep (0.51.1)

ast-grep could not parse rule config: /ast-grep-rules/ast-grep-essentials/rules/ruby/security/insufficient-rsa-key-size-ruby.yml

Usage-based review receipt

Note

This review was completed with usage-based billing: files reviewed beyond your plan's included limits are billed at $0.25/file. View usage-based billing.

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.
External image Recent review info
External image Run configuration
External image Commits

Reviewing files that changed from the base of the PR and between 5abe8a0c4b9641116dbb6dd8aaba09e6c263157d and 8f9e45d918d61b9a26206e55500c1de6ddf558cb.

External image Files selected for processing: 2
  • apps/server/src/orchestration-v2/ProjectionStore.test.ts
  • apps/server/src/orchestration-v2/ProjectionStore.ts

Limit details: You’ve used all 10 included reviews currently available.

Added 1 commit

  • Merge branch 'main' into fix/issue-279448f9e45d