Skip to content

fix(runtime): bound background-held turns and stop failing busy reused processes as starting - #43

Merged
dviejokfs merged 2 commits into
mainfrom
fix/bound-held-and-startup-turns
Oct 15, 2026
Merged

dviejokfs merged 2 commits into
mainfrom
fix/bound-held-and-startup-turns

Conversation

@dviejokfs

Copy link
Copy Markdown
Contributor

Problem

Two ways a retained Claude turn ends up "in progress" or fails misleadingly:

  1. Held turns have no bound. A turn that has answered and is held open only by background work (a subagent) whose completion is never reported stays open until active_inactivity_timeout, which applications that wait on background work leave unset (falling back to the turn cap, often 24 h).
  2. A busy reused process is failed as "starting". initialization_timeout was applied on every turn of a retained process, because the semantic-activity flag starts false each turn. A process that was merely busy (finishing work an earlier turn left, or running a long tool for a queued message) was failed as turn timed out after 30 seconds even though it had long since started.

Changes

  • ProviderProcessRetention::held_task_quiet_timeout: Option<Duration> (breaking: new field). When a turn has answered and only background work holds it, and nothing is reported for this long, the turn completes successfully with a TurnEvent::Warning naming the work. The process is parked with that work exactly as after a hand-off, so a task that finishes later is still delivered as a background follow-up. Task progress resets the timer. None keeps the previous behavior.
  • A retained process remembers it has answered once (RetainedHandoff::initialized); later turns are judged by the active limits, not initialization_timeout.
  • RuntimeError::StartupTimeout ("did not respond within N seconds of starting") for a process that never answered. Maps to RuntimeFailureKind::Timeout like Timeout, so callers can tell a failed start from a turn that ran too long.

Tests

New in tests/retained_providers.rs (silent-subagent fixture added):

  • a turn held by a silent subagent stops waiting after the quiet limit with a warning, and the process is still reused;
  • a subagent that keeps reporting progress is not cut off by the same limit;
  • a process that never starts reports StartupTimeout;
  • a reused process silent past initialization_timeout is not a startup failure.

cargo test --test retained_providers --test retained_live_messages --test opencode_serve, cargo test --lib, cargo clippy --all-targets --all-features -D warnings and cargo fmt --check pass locally.

Docs: docs/reference/retained-runtimes.md and CHANGELOG.md updated.

🤖 Generated with Claude Code

…d processes as starting

A retained turn that had answered and was held open by background work
that never reported its completion stayed open until the inactivity limit
(unbounded in practice). Add ProviderProcessRetention::held_task_quiet_timeout:
the turn completes with a warning and the process is parked with its work.

A reused retained process was held to initialization_timeout on every turn,
so a process busy with earlier work was failed as 'turn timed out' after the
startup deadline. Track that a process has answered once, and report a real
failed start as RuntimeError::StartupTimeout.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[High impact] The PR appears safe to merge; both previous findings are addressed and no new actionable issue was found.

Summary

Bounds how long retained turns wait for silent background work and keeps reused processes off the startup deadline.

  • A quiet background task no longer keeps an answered turn waiting indefinitely.
  • Startup timeouts now distinguish an unanswered process from a busy reused one.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Turn has answered] --> B{Background work remains?}
  B -->|No| C[Complete turn]
  B -->|Yes| D[Wait for progress]
  D -->|Progress arrives| D
  D -->|Quiet limit expires| E[Warn and complete turn]
  E --> F[Park process with unfinished work]
  F -->|Late answer arrives| G[Announce follow-up]
  G --> H[Continuation delivers answer]
Loading

Reviews (2) · Last reviewed commit: "fix(runtime): do not promise a startup t..." · Reviewed by Greptile

Comment thread src/error.rs Outdated
Comment thread tests/retained_providers.rs Outdated
… late quiet-limit delivery

A startup timeout does not prove the prompt was undelivered (OpenCode sends it
before the acknowledgement arrives), so the variant's docs no longer claim a
retry is safe. The quiet-limit test now releases the silent subagent after the
turn stopped waiting and checks its answer is announced and delivered exactly
once by a continuation, on the same process.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@dviejokfs
dviejokfs merged commit 44e5a17 into main Oct 15, 2026
12 of 13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant