Skip to content

feat(backends): completion-check continuation loop + review fixes#798

Merged
zbigniewsobiecki merged 1 commit intodevfrom
feature/completion-check-continuation-loop
Mar 14, 2026
Merged

feat(backends): completion-check continuation loop + review fixes#798
zbigniewsobiecki merged 1 commit intodevfrom
feature/completion-check-continuation-loop

Conversation

@zbigniewsobiecki
Copy link
Copy Markdown
Member

Summary

  • Completion-check continuation loop: When an agent finishes a run but the post-completion check fails (e.g. no authoritative PR sidecar was written — the agent narrated PR creation rather than calling the tool), the backend now re-prompts the existing session with { continue: true } up to maxContinuationTurns times (now 2) before failing the run.
  • Shared applyCompletionEvidence: Extracted from OpenCode's local scope into the shared completion.ts module — both backends now share identical sidecar-upgrade logic.
  • Session cleanup fix: cleanupPersistedSession was using path.join which strips the leading /, producing a path that never exists. Fixed to encode cwd the same way the SDK does: replaceAll(path.sep, '-').
  • Consistency + quality fixes from internal code review (see details below).

Changes

src/backends/completion.ts

  • New: applyCompletionEvidence() — reads sidecar files and upgrades text-based PR evidence to authoritative (shared by both backends).

src/backends/claude-code/index.ts

  • New: consumeStream() — processes the SDK stream; returns { assistantMessages, resultMessage, turnCount, toolCallCount } (replaces the StreamConsumptionContext mutable-ref anti-pattern).
  • New: countToolCalls() — counts tool_use blocks per assistant message (avoids deep nesting in consumeStream).
  • New: decideContinuation() — encapsulates the completion-failure check, max-turns guard, and continuation-warning log (keeps execute() under complexity limits).
  • New: cleanupPersistedSession() — removes ~/.claude/projects/<encoded-cwd> with correct path encoding.
  • Fix: log message "execution completed""turn completed" (accurate for both initial and continuation turns).
  • Fix: spread pattern ...(bool && obj)...(bool ? obj : {}).
  • Add: toolCallCount to continuation warning log (parity with OpenCode).

src/backends/opencode/index.ts

  • Remove local applyCompletionEvidence(), use the shared import.
  • Fix maxContinuationTurns fallback: 10 (matches Claude Code; safe default when completionRequirements is absent).

src/backends/adapter.ts

  • Bump maxContinuationTurns 1 → 2.

Tests

  • New: tests/unit/backends/completion.test.ts — covers all applyCompletionEvidence cases.
  • Extended: tests/unit/backends/claude-code.test.ts — five continuation-loop scenarios + beforeEach mock reset + queueStream rename to avoid shadowing.
  • Updated: tests/unit/backends/adapter.test.tsmaxContinuationTurns expectation updated.

Test plan

  • npm run typecheck — no errors
  • npm run lint — no warnings
  • npm test — 480 tests passing

🤖 Generated with Claude Code

…dence module

Introduces a continuation loop in both Claude Code and OpenCode backends so agents
that fail a post-completion check (e.g. no authoritative PR sidecar written) can
resume the session and retry rather than immediately failing the run.

## Core changes

### Shared completion module (`src/backends/completion.ts`)
- Extract `applyCompletionEvidence()` from OpenCode's local scope to the shared
  `completion.ts` module so both backends share identical evidence-upgrade logic.

### Claude Code backend (`src/backends/claude-code/index.ts`)
- Add `consumeStream()` helper: processes the SDK stream, returns `turnCount` and
  `toolCallCount` (replaces the `StreamConsumptionContext` mutable-ref pattern).
- Add `countToolCalls()` helper to count `tool_use` blocks without deep nesting.
- Add `decideContinuation()` helper: encapsulates the completion-failure check,
  max-turns guard, and continuation-warning log; keeps `execute()` complexity low.
- Add `cleanupPersistedSession()`: removes `~/.claude/projects/<encoded-cwd>` after
  each worker run. Encodes cwd with `replaceAll(path.sep, '-')` to match the SDK's
  actual directory naming (fix: plain `path.join` silently produced a wrong path).
- Continuation loop in `execute()`: on completion failure, re-prompt the existing
  session with `{ continue: true }` up to `maxContinuationTurns` times.
- Fix log message: "execution completed" → "turn completed" (accurate for both
  initial and continuation turns).
- Fix spread pattern: `...(bool && obj)` → `...(bool ? obj : {})`.
- Log `toolCallCount` in the continuation warning (parity with OpenCode).

### OpenCode backend (`src/backends/opencode/index.ts`)
- Remove local `applyCompletionEvidence()` in favour of the shared import.
- Fix `maxContinuationTurns` fallback: `1` → `0` (match Claude Code; if
  `completionRequirements` is absent there is nothing to check).

### Adapter (`src/backends/adapter.ts`)
- Bump `maxContinuationTurns` from 1 → 2.

## Tests

- `tests/unit/backends/completion.test.ts` (new): covers `applyCompletionEvidence`
  for all cases (no sidecar, sidecar upgrades text evidence, adds missing prUrl,
  default command fallback).
- `tests/unit/backends/claude-code.test.ts`: five new continuation-loop scenarios
  (success after retry, exhausted turns, non-success stops immediately, cost
  accumulation, no-op when completionRequirements absent). Adds `beforeEach` reset
  and renames inner helper to `queueStream` to avoid shadowing the outer `mockStream`.
- `tests/unit/backends/adapter.test.ts`: update `maxContinuationTurns` expectation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@zbigniewsobiecki zbigniewsobiecki merged commit 1730ec8 into dev Mar 14, 2026
6 checks passed
@zbigniewsobiecki zbigniewsobiecki deleted the feature/completion-check-continuation-loop branch March 14, 2026 07:45
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