Skip to content

Adopt Effect internally for typed errors and configurable resilience - #23

Merged
Tyler-R-Kendrick merged 3 commits into
mainfrom
claude/effect-error-handling-uus413
Jul 12, 2026
Merged

Adopt Effect internally for typed errors and configurable resilience#23
Tyler-R-Kendrick merged 3 commits into
mainfrom
claude/effect-error-handling-uus413

Conversation

@Tyler-R-Kendrick

@Tyler-R-Kendrick Tyler-R-Kendrick commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Summary

There was too much scattered try/catch. This adopts the Effect library internally — the public API stays Promise-based and continues to throw/reject — replacing ad-hoc error handling with typed errors at integration boundaries and brand-new, configurable per-operation resilience.

New: injectable resilience policies (packages/training/src/resilience.ts)

Effect-style named policies, exposed as plain config through the additive TrainingSettings.resilience field:

configureTraining({
  resilience: {
    propose:  { timeoutMs: 30_000, retry: { attempts: 3 } }, // engine/LLM call
    evaluate: { retry: { attempts: 2 } },                    // each candidate execution
    store:    {},                                            // capture writes (retries off by default)
  },
});
  • Per-attempt timeoutMs fails with a typed OperationTimeoutError (Data.TaggedError, retryable by default) and aborts the attempt's AbortSignal.
  • Retries use Effect Schedule: jittered exponential backoff, capped delay, retryable predicate, never retried after the caller's signal aborts.
  • withPolicy settles back into an ordinary promise and rethrows the original error unwrapped — no Effect types or fiber wrappers leak to consumers.
  • No policy = byte-for-byte previous behavior; resilience is opt-in.

Simplified error handling

  • training runtime: all background failures (capture/store/evolve) funnel through one #report boundary into TrainingSettings.onError; JSON serialize/parse fallbacks become attempt() one-liners; the duplicated errorMessage helpers are deduped.
  • harness dispatchAction: the two record-then-rethrow try/catch blocks become a linear Effect.gen pipeline where failure records are best-effort tapError taps; AgentActionDeniedError gains a _tag discriminant.
  • harness sandbox: four try/catches become declarative error-to-value mappings (permission_denied, file_not_found, false).
  • ax provider: failed candidate runs score 0 and JSON args fall back via the same helpers.
  • Deliberately untouched: execution.ts try/finally (resource cleanup), loop.ts (already a single observer-error boundary), and the rewrite package's three best-effort load-time guards.

effect@^3.21.4 is added to root, ts-autocode-training, and ts-autocode-harness; the rewrite package stays dependency-free. New public exports: OperationTimeoutError, withPolicy, defaultRetry, and the ResiliencePolicy/RetryOptions/ResilienceSettings/ErrorPhase types.

Testing

  • npm run check passes: all four project typechecks, the full Vitest suite (16 files, 93 tests), and the core build.
  • New packages/training/test/resilience.test.ts covers passthrough identity, retry-until-success, original-error rethrow on exhaustion, non-retryable predicates, typed timeout with attempt-signal abort, timeout-then-retry, and prompt caller-abort during backoff.
  • New training integration tests lock the contracts: store retry persists the record without onError, no-policy store failure still routes to onError(err, "store"), and a rate-limited engine proposal succeeds under a propose retry policy.

🤖 Generated with Claude Code

https://claude.ai/code/session_01W4PmJx4HSwBjFnCuGXdfwK


Generated by Claude Code

Summary by CodeRabbit

  • New Features

    • Added configurable resilience policies for proposal, evaluation, and storage operations.
    • Supports per-attempt timeouts, exponential-backoff retries, jitter, retry conditions, and cancellation.
    • Exposed typed timeout errors and resilience configuration options.
    • Added clearer error-phase reporting for training failures.
  • Bug Fixes

    • Improved handling of file, parsing, execution, and action-dispatch failures with consistent fallbacks.
  • Documentation

    • Added configuration guidance and examples for resilience policies.
  • Tests

    • Added coverage for retries, timeouts, cancellation, and storage/proposal recovery.

Replace the scattered try/catch adapters with Effect-based boundaries
while keeping the public API Promise-based:

- New ts-autocode-training resilience module: named per-operation
  policies (propose/evaluate/store) composing a per-attempt timeout
  (typed OperationTimeoutError) with jittered exponential-backoff
  retries via Effect Schedule, wired through the additive
  TrainingSettings.resilience field. Without a policy every operation
  behaves exactly as before.
- Training runtime routes all background failures (capture, store,
  evolve) through one #report boundary into onError; JSON fallbacks
  become attempt() one-liners.
- Harness dispatchAction becomes a linear Effect pipeline whose
  failure records are best-effort tapError taps; original errors are
  rethrown unwrapped. AgentActionDeniedError gains a _tag.
- Sandbox file operations map errors to their typed fallback values
  declaratively; ax provider scoring/parsing fallbacks likewise.
- rewrite package, loop.ts, and the execution.ts try/finally are left
  as-is: each is already a minimal, intentional boundary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W4PmJx4HSwBjFnCuGXdfwK
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Tyler-R-Kendrick, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 17 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 49036c7d-ae30-467a-9c1e-f8193bf69c75

📥 Commits

Reviewing files that changed from the base of the PR and between 3ac9846 and 6a7c2cb.

📒 Files selected for processing (5)
  • packages/harness/src/attempt.ts
  • packages/harness/src/sandbox.ts
  • packages/training/src/attempt.ts
  • packages/training/test/attempt.test.ts
  • src/attempt.ts
📝 Walkthrough

Walkthrough

Changes

Training resilience

Layer / File(s) Summary
Resilience policy engine
packages/training/src/resilience.ts, packages/training/test/resilience.test.ts, README.md
Adds typed timeout errors, configurable retries, exponential backoff, jitter, abort handling, and configuration documentation.
Training operation integration
packages/training/src/training.ts, packages/training/test/training.test.ts, packages/training/src/index.ts
Applies policies to proposal, evaluation, and storage operations, centralizes error-phase reporting, and validates retry behavior.
Effect fallback utilities
packages/harness/src/attempt.ts, packages/training/src/attempt.ts, src/attempt.ts, packages/harness/src/sandbox.ts, src/providers/ax.ts
Adds synchronous and asynchronous fallback helpers and applies them to filesystem, execution, serialization, and parsing failures.
Effect-based action dispatch
packages/harness/src/dispatch.ts
Replaces imperative dispatch error handling with an Effect pipeline for gates, execution, failure recording, completion recording, and error unwrapping.
Public API and dependencies
src/index.ts, package.json, packages/*/package.json
Re-exports resilience APIs and adds the effect dependency to the affected packages.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Training
  participant withPolicy
  participant Engine
  participant TrainingStore
  Training->>withPolicy: run propose policy
  withPolicy->>Engine: call optimize with attempt signal
  Engine-->>withPolicy: proposal or failure
  withPolicy-->>Training: proposal or retryable failure
  Training->>withPolicy: run store policy
  withPolicy->>TrainingStore: append training record
  TrainingStore-->>withPolicy: persisted record or failure
  withPolicy-->>Training: completion or reported store error
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: internal Effect adoption for typed errors and configurable resilience.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/effect-error-handling-uus413

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/harness/src/attempt.ts`:
- Around line 1-17: Synchronize the duplicated attempt helpers in
packages/harness/src/attempt.ts, packages/training/src/attempt.ts, and
src/attempt.ts by making each export the complete shared API: errorMessage,
attempt, and attemptAsync. Preserve the existing implementations and update each
file consistently so the header’s “keep the copies in sync” contract is
accurate.

In `@packages/training/src/attempt.ts`:
- Around line 1-22: Update the `attempt` helper to use the object form of
`Effect.try` with an identity `catch`, so its `fallback` callback receives the
original thrown value rather than an `UnknownException`; leave `attemptAsync`
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 984026d9-4087-4af0-9899-da4cf2f26a39

📥 Commits

Reviewing files that changed from the base of the PR and between 09640d4 and 3ac9846.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (16)
  • README.md
  • package.json
  • packages/harness/package.json
  • packages/harness/src/attempt.ts
  • packages/harness/src/dispatch.ts
  • packages/harness/src/sandbox.ts
  • packages/training/package.json
  • packages/training/src/attempt.ts
  • packages/training/src/index.ts
  • packages/training/src/resilience.ts
  • packages/training/src/training.ts
  • packages/training/test/resilience.test.ts
  • packages/training/test/training.test.ts
  • src/attempt.ts
  • src/index.ts
  • src/providers/ax.ts

Comment thread packages/harness/src/attempt.ts
Comment thread packages/training/src/attempt.ts
claude and others added 2 commits July 12, 2026 23:02
Address review findings: the Effect.try shorthand wrapped sync throws in
UnknownException, so attempt()'s fallback (and therefore onError in the
capture phase) received the wrapper instead of the original error. Use
the object form with an identity catch, add a regression test locking
the raw-value contract, and make the three duplicated attempt.ts copies
byte-identical (errorMessage + attempt + attemptAsync) as their headers
promise.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W4PmJx4HSwBjFnCuGXdfwK
@Tyler-R-Kendrick
Tyler-R-Kendrick merged commit 94093ed into main Jul 12, 2026
2 checks passed
@Tyler-R-Kendrick
Tyler-R-Kendrick deleted the claude/effect-error-handling-uus413 branch July 12, 2026 23:12
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.

2 participants