Skip to content

fix: one failed LLM call no longer aborts the whole run - #61

Open
UditAkhourii wants to merge 1 commit into
mainfrom
bot/issue-44-partial-failure-resilience
Open

UditAkhourii wants to merge 1 commit into
mainfrom
bot/issue-44-partial-failure-resilience

Conversation

@UditAkhourii

Copy link
Copy Markdown
Owner

Summary

  • run() used Promise.all for both the divergence fan-out (frames.map(...)) and the deepen pass (toDeepen.map(...)). If a single branch's callLLM threw (rate limit, timeout, an unrecoverable malformed response), the whole run rejected and every other already-completed branch's work was discarded.
  • Both fan-outs now use Promise.allSettled. Fulfilled results are kept as before; rejected ones are recorded instead of propagating.
  • Added RunResult.failedFrames: { frameId: string; error: string }[] (types.ts) so callers/the CLI can see partial failure instead of a silent gap or a hard crash.
  • Added a "frame:failed" RunEvent, surfaced by cli.ts's progress printer ( ✗ <frame> failed: <reason>) and by render.ts's text output ("Partial failures" section) when failedFrames is non-empty.
  • A run now only hard-fails if every divergence branch fails (all frames threw) — matches the issue's requirement exactly.
  • divergeBranch, scoreIdeas, and clusterIdeas already failed open internally on JSON-parse errors; this only changes the network/LLM-call layer that could previously throw before ever reaching parseJSON.

Testing

  • npm run typecheck — clean.
  • npm test — existing suite passes (1/1). I did not add a new automated test for this fix: exercising the failure path would require mocking the Claude Agent SDK's query() call, and the repo has no existing mocking setup for LLM calls (the one existing engine-adjacent test only covers the pure buildQueryOptions helper) — introducing one felt out of scope for a targeted bug fix. Verified the reconciliation logic by inspection and by running npm run build && node dist/cli.js --help.
  • npm run build — clean.

Closes #44

🤖 Generated with Claude Code

run() used Promise.all for the divergence fan-out and the deepen pass,
so a single branch's callLLM throwing (rate limit, timeout, an
unrecoverable response) rejected the entire run and discarded every
other branch's completed work.

- Swap Promise.all for Promise.allSettled in both the divergence
  fan-out and the deepen pass.
- Add RunResult.failedFrames so callers/CLI can see which frames/ideas
  failed and why, instead of a silent gap or a hard crash.
- Emit a "frame:failed" RunEvent so cli.ts's progress printer and
  render.ts's text output can surface partial failures.
- A run only hard-fails now if every divergence branch fails.

Closes #44

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

One failed LLM call aborts the whole run — no partial-failure resilience

1 participant