Skip to content

ExecutionPlan: lifecycle events for retries and execution observability - #7026

Merged
tim-smart merged 2 commits into
mainfrom
agent/claude-engineer/8c575839
Aug 5, 2026
Merged

ExecutionPlan: lifecycle events for retries and execution observability#7026
tim-smart merged 2 commits into
mainfrom
agent/claude-engineer/8c575839

Conversation

@tim-smart

Copy link
Copy Markdown
Contributor

Summary

Adds an optional onEvent handler to Effect.withExecutionPlan and Stream.withExecutionPlan so individual plan attempts can be observed from outside the effect, for logging and metrics.

Effect.withExecutionPlan(program, plan, {
  onEvent: (event) => Effect.log("execution plan event", event)
})

The handler receives an ExecutionPlan.Event<E>, a tagged union of:

  • AttemptStart — emitted before each attempt runs
  • AttemptSuccess — the attempt succeeded (always the final event), with duration
  • AttemptFailure — the attempt failed, with duration and the full failure Cause

Each event carries attempt (cumulative across steps, matching ExecutionPlan.CurrentMetadata.attempt), stepAttempt (1-based within the current step), and stepIndex.

Design notes

  • The hook lives on the combinator rather than on ExecutionPlan.make, so a shared plan can be observed differently at different call sites.
  • The handler is (event) => Effect<void, never, R>: it is awaited inline (events are strictly ordered), cannot fail (observation cannot change the plan's outcome), and its requirements union into the result's R.
  • Every AttemptStart is followed by exactly one terminal event. Terminal events run like finalizers, so an interrupted attempt emits AttemptFailure with the interruption cause; keying off Cause means defects are reported too, even though the plan does not retry them.
  • The per-attempt effect is instrumented before the retry schedule is applied, so each retry within a step produces its own event pair, and the synthetic replay used when falling over to a later step emits no events.
  • Effect.withExecutionPlan moved from dual(2, ...) to dual(isEffect(args[0]), ...) to support the optional third argument, matching Stream.withExecutionPlan.
  • Stream attempts are wrapped with Stream.onStart / Stream.onExit (scope finalizer), giving streams full parity including defects and interruption, which the existing error-channel catch_ loop cannot observe.

Closes EFF-467
Closes #6699

Validation

  • pnpm vitest run packages/effect/test/ExecutionPlan.test.ts (16 tests, including 10 new event tests asserting full ordered event arrays)
  • pnpm test-types Effect.tst.ts / pnpm test-types Stream.tst.ts (new handler-requirement propagation tests)
  • pnpm doctest --run packages/effect/src/ExecutionPlan.ts packages/effect/src/Effect.ts packages/effect/src/Stream.ts
  • pnpm check, pnpm lint
  • Package-local pnpm docgen fails on a pre-existing, unrelated HttpStaticServer example error (reproduced on the base commit); the new example typechecks.

🤖 Generated with Claude Code

@changeset-bot

changeset-bot Bot commented Aug 5, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 7e60e62

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 30 packages
Name Type
effect Patch
@effect/ai-anthropic Patch
@effect/ai-openai Patch
@effect/ai-openai-compat Patch
@effect/ai-openrouter Patch
@effect/atom-react Patch
@effect/atom-solid Patch
@effect/atom-vue Patch
@effect/docgen Patch
@effect/doctest Patch
@effect/openapi-generator Patch
@effect/opentelemetry Patch
@effect/platform-browser Patch
@effect/platform-bun Patch
@effect/platform-deno Patch
@effect/platform-node Patch
@effect/platform-node-shared Patch
@effect/sql-clickhouse Patch
@effect/sql-d1 Patch
@effect/sql-libsql Patch
@effect/sql-mssql Patch
@effect/sql-mysql2 Patch
@effect/sql-pg Patch
@effect/sql-pglite Patch
@effect/sql-sqlite-bun Patch
@effect/sql-sqlite-do Patch
@effect/sql-sqlite-node Patch
@effect/sql-sqlite-react-native Patch
@effect/sql-sqlite-wasm Patch
@effect/vitest Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

Reviewed PR #7026 at head c2f06dde against main (b8cc53d):

  • Added public ExecutionPlan.Event tagged union (AttemptStart, AttemptSuccess, AttemptFailure) and ExecutionPlan.Options type.
  • Updated Effect.withExecutionPlan and Stream.withExecutionPlan to accept optional options.onEvent and union handler requirements into the result R.
  • Implemented event instrumentation in internal/executionPlan.ts for both Effect (uninterruptibleMask + onExit) and Stream (onStart + onExit scope finalizer).
  • Added 10 new event tests and type tests for requirement propagation.
  • Added effect patch changeset.

Validation run: pnpm vitest run packages/effect/test/ExecutionPlan.test.ts, pnpm test-types packages/effect/typetest/Effect.tst.ts, pnpm test-types packages/effect/typetest/Stream.tst.ts, pnpm doctest --run packages/effect/src/ExecutionPlan.ts packages/effect/src/Effect.ts packages/effect/src/Stream.ts, pnpm check, pnpm lint — all passed.

Pullfrog  | ⚠️ this action is pinned to a commit SHA, which freezes the cleanup step — switch to @v0 or keep the SHA fresh with Dependabot | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Bundle Size Analysis

Generated from PR build output; treat the content below as untrusted.

File Name Current Size Previous Size Difference
basic.ts 7.06 KB 7.06 KB 0.00 KB (0.00%)
batching.ts 9.86 KB 9.86 KB 0.00 KB (0.00%)
brand.ts 6.34 KB 6.34 KB 0.00 KB (0.00%)
cache.ts 10.71 KB 10.71 KB 0.00 KB (0.00%)
config.ts 20.73 KB 20.73 KB 0.00 KB (0.00%)
differ.ts 20.31 KB 20.31 KB 0.00 KB (0.00%)
http-client.ts 21.53 KB 21.53 KB 0.00 KB (0.00%)
logger.ts 10.84 KB 10.84 KB 0.00 KB (0.00%)
metric.ts 8.98 KB 8.98 KB 0.00 KB (0.00%)
optic.ts 7.18 KB 7.18 KB 0.00 KB (0.00%)
pubsub.ts 14.99 KB 14.99 KB 0.00 KB (0.00%)
queue.ts 11.66 KB 11.66 KB 0.00 KB (0.00%)
schedule.ts 10.83 KB 10.83 KB 0.00 KB (0.00%)
schema-class.ts 19.27 KB 19.27 KB 0.00 KB (0.00%)
schema-fromJsonSchemaDocument.ts 29.09 KB 29.09 KB 0.00 KB (0.00%)
schema-representation-roundtrip.ts 25.40 KB 25.40 KB 0.00 KB (0.00%)
schema-string-transformation.ts 13.42 KB 13.42 KB 0.00 KB (0.00%)
schema-string.ts 10.95 KB 10.95 KB 0.00 KB (0.00%)
schema-template-literal.ts 15.21 KB 15.21 KB 0.00 KB (0.00%)
schema-toArbitraryLazy.ts 22.02 KB 22.02 KB 0.00 KB (0.00%)
schema-toCodeDocument.ts 24.45 KB 24.45 KB 0.00 KB (0.00%)
schema-toCodecJson.ts 19.28 KB 19.28 KB 0.00 KB (0.00%)
schema-toEquivalence.ts 19.11 KB 19.11 KB 0.00 KB (0.00%)
schema-toFormatter.ts 18.97 KB 18.97 KB 0.00 KB (0.00%)
schema-toJsonSchemaDocument.ts 22.69 KB 22.69 KB 0.00 KB (0.00%)
schema-toRepresentation.ts 19.60 KB 19.60 KB 0.00 KB (0.00%)
schema.ts 18.52 KB 18.52 KB 0.00 KB (0.00%)
stm.ts 12.63 KB 12.63 KB 0.00 KB (0.00%)
stream.ts 9.80 KB 9.80 KB 0.00 KB (0.00%)

Comment thread packages/effect/src/ExecutionPlan.ts Outdated

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

Reviewed the incremental changes on PR #7026 since the prior pullfrog review at c2f06dde:

  • Inlined the option shape directly into both Effect.withExecutionPlan and Stream.withExecutionPlan signatures and removed the ExecutionPlan.Options type; moved the worked example and handler-semantics JSDoc into Effect.withExecutionPlan.
  • Added an Effect test verifying that event attempt stays in sync with ExecutionPlan.CurrentMetadata across a step-failover boundary.
  • Added Stream tests confirming AttemptFailure is emitted for both interruption and defects without retrying.

The prior human review thread asking to inline ExecutionPlan.Options is addressed by the new commit. Targeted runtime tests, type tests, and doctests all pass.

Pullfrog  | ⚠️ this action is pinned to a commit SHA, which freezes the cleanup step — switch to @v0 or keep the SHA fresh with Dependabot | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

tim-smart and others added 2 commits August 5, 2026 21:13
Adds an optional onEvent handler to Effect.withExecutionPlan and
Stream.withExecutionPlan, emitting AttemptStart, AttemptSuccess, and
AttemptFailure events for each plan attempt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…interruption

Removes the ExecutionPlan.Options interface per review, inlining the
onEvent option object into both withExecutionPlan signatures. Adds
Stream test twins for interruption and defects, a CurrentMetadata
parity test across a failover boundary, and documents that early
downstream termination reports AttemptSuccess.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@tim-smart
tim-smart force-pushed the agent/claude-engineer/8c575839 branch from 0d32de7 to 7e60e62 Compare August 5, 2026 21:14

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Incremental review of the latest push (0d32de707e60e62d). The new commits rebase the prior ExecutionPlan work and add the onEvent lifecycle observer.

Reviewed:

  • New ExecutionPlan.AttemptStart, ExecutionPlan.AttemptSuccess, and ExecutionPlan.AttemptFailure<E> event types and the ExecutionPlan.Event<E> union.
  • Effect.withExecutionPlan / Stream.withExecutionPlan optional onEvent handler wiring and requirement propagation.
  • Internal makeEventEmitter implementation: event ordering, stepAttempt reset per step, duration measurement, and uninterruptible start/end for effects / onStart/onExit for streams.
  • Tests covering retries, failover, layer build failures, interruption, defects, and partial-stream fallback.
  • Type tests for data-first/data-last requirement propagation.

Validation run:

  • pnpm --filter effect test --run test/ExecutionPlan.test.ts
  • pnpm test-types packages/effect/typetest/Effect.tst.ts packages/effect/typetest/Stream.tst.ts
  • pnpm doctest --run packages/effect/src/ExecutionPlan.ts packages/effect/src/Effect.ts packages/effect/src/Stream.ts
  • pnpm --filter effect check
  • pnpm lint

No new issues found. Approving.

Pullfrog  | ⚠️ this action is pinned to a commit SHA, which freezes the cleanup step — switch to @v0 or keep the SHA fresh with Dependabot | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

@tim-smart
tim-smart merged commit d44cead into main Aug 5, 2026
20 checks passed
@tim-smart
tim-smart deleted the agent/claude-engineer/8c575839 branch August 5, 2026 22:14
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.

Effect AI ExecutionPlan: Add lifecycle hooks for retries and execution observability

1 participant