Skip to content

fix(coding-agent): resume a provider-error-blocked goal on the next user message - #688

Merged
code-yeongyu merged 1 commit into
mainfrom
fix/goal-resume-after-provider-error
Aug 4, 2026
Merged

fix(coding-agent): resume a provider-error-blocked goal on the next user message#688
code-yeongyu merged 1 commit into
mainfrom
fix/goal-resume-after-provider-error

Conversation

@code-yeongyu

@code-yeongyu code-yeongyu commented Aug 4, 2026

Copy link
Copy Markdown
Owner

Problem

A goal blocked by a terminal provider error stayed blocked forever. agent_end writes
blocked with reason provider error ended the turn (retries exhausted) when
AgentEndEvent.willRetry is false, but MECHANICAL_CONTINUATION_BLOCKS only listed the
cap, repetition, and length guards. GoalDirectInputLifecycle.onDisposition reactivates a
blocked goal on accepted direct input only for mechanical blocks, so the user's next
message — the natural retry signal — did nothing, and the blocked notice never said how to
recover.

Change

  • continuation-recovery.ts exports PROVIDER_ERROR_BLOCKED_REASON and adds it to
    MECHANICAL_CONTINUATION_BLOCKS.
  • index.ts writes that shared constant instead of repeating the literal reason, and
    appends continuationCapRecoveryHint(...) to the blocked notice so the warning ends with
    Send any message to resume.
  • direct-input-lifecycle.ts needed no change: the reactivation path already existed; the
    provider-error reason now flows through it.

Intentional blocks stay non-recoverable — user interrupted the turn and model-declared
update_goal blocks encode a decision to stop, and a new regression test pins that.

Prior art check: codex ext/goal (runtime.rs stop_active_goal_for_turn) also marks a
turn error Blocked, but its resume is user/system controlled with no user-message
auto-resume — so this is a deliberate senpi-side policy improvement, not a parity port.

Evidence

RED (before the production change), both criteria failing for the right reason:

FAIL goal-extension.test.ts > resumes a provider-error-blocked goal when the user sends a new message
  expected { status: 'blocked' } to match object { status: 'active' }
FAIL goal-cap-recovery-guidance.test.ts > classifies a terminal provider error as prompt-recoverable
  expected undefined to be 'provider error ended the turn (retries exhausted)'

GREEN:

Test Files  5 passed (5)
     Tests  118 passed (118)

(goal-cap-recovery-guidance, goal-extension, issue-447-goal-continuation, goal-modules, goal-store)

Real-surface run against the actual store + lifecycle modules in a temp goal dir:

1. after terminal provider error -> blocked / provider error ended the turn (retries exhausted)
2. after a new user message -> active
RESULT: PASS
cleanup: removed /var/folders/.../senpi-goal-qa-bwf95O

Root npm run check: clean.

Docs

goal/changes.md gets a dated fork entry with merge-conflict zones; goal/AGENTS.md
CONTINUATION POLICY now states the mechanical/intentional split.


Summary by cubic

Treats terminal provider errors as mechanical blocks so a new user message resumes the goal. The warning now includes “Send any message to resume.”

  • Bug Fixes
    • Added PROVIDER_ERROR_BLOCKED_REASON to MECHANICAL_CONTINUATION_BLOCKS, enabling automatic reactivation via existing direct-input handling.
    • Updated index.ts to use the shared reason and append continuationCapRecoveryHint(...) to the warning.
    • Intentional blocks (user interrupt, model update_goal) remain non-recoverable; tests and docs updated.

Written for commit c3f2997. Summary will update on new commits.

Review in cubic

…ser message

A terminal provider error is infrastructure, not a decision, but the goal it
blocked stayed blocked: only the cap, repetition, and length guards counted as
mechanical, so the user's next message - the natural retry signal - did nothing,
and the blocked notice never said how to recover.

Export PROVIDER_ERROR_BLOCKED_REASON, classify it as a mechanical continuation
block so the existing direct-input reactivation path picks it up, have index.ts
write that shared constant instead of a duplicated literal, and append the
resume hint to the blocked notice. Intentional blocks - a user interrupt or a
model-declared update_goal block - stay non-recoverable.
@code-yeongyu
code-yeongyu force-pushed the fix/goal-resume-after-provider-error branch from c2af293 to c3f2997 Compare August 4, 2026 02:19
@code-yeongyu

Copy link
Copy Markdown
Owner Author

Rebased onto main after #687 merged.

#687 and this PR are complementary halves of the same recovery story and both landed the same day:

  • fix(goal): prompt to resume blocked goals on restart #687 — restart resume prompt: a new session loading a stopped (paused | blocked) goal now prompts Resume blocked goal?. Requires a restart and a user click.
  • this PR — in-session resume: a goal blocked mid-run by a terminal provider error resumes on the next user message, with no restart and no prompt, because the reason is now classified mechanical.

Without this PR, a provider outage mid-session still stranded the goal until the user quit and relaunched senpi.

Rebase touched goal/changes.md only (both PRs prepended a dated entry); both entries are preserved and cross-referenced. goal/AGENTS.md merged cleanly — #687's RESTART RESUME PROMPT section and this PR's CONTINUATION POLICY sentence coexist.

Re-verified on the new base:

full goal surface (7 files)   142 passed (142)
npm run check                 exit 0

Note on the earlier red run: Test (coding-agent 2/3) failed on test/mcp/catalog-cache.test.ts ("refreshes eager servers from the live catalog when a valid warm cache differs"). That file is untouched by this diff — which is confined to goal/ — and passes locally (6/6). Flaky, unrelated to this change.

@code-yeongyu
code-yeongyu merged commit 04665bf into main Aug 4, 2026
14 checks passed
@code-yeongyu
code-yeongyu deleted the fix/goal-resume-after-provider-error branch August 4, 2026 02:26
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