Skip to content

@temporal-contract/experimental-effect: opt-in Effect integration for activities and client #297

Description

@btravers

Motivation

oRPC v2 ships @orpc/experimental-effect (beta), and its shape is worth copying even more than its code:

  • A separate experimental-* package with effect as a peer dep — zero cost to non-users, and the experimental prefix keeps semver honest while Effect v4 is itself in beta.
  • Opt-in module augmentation (import '@orpc/experimental-effect/extensions/effect' adds an .effect() builder method) — integrations ship without bloating core.
  • A tiny, principled error bridge: Effect.catch(e => e instanceof ORPCError ? Effect.succeed(e) : Effect.fail(e)) — modeled errors get lifted into the typed channel, everything else stays a defect.
  • DI via well-known context keys ('effect/context', 'effect/wrap') with a WithEffectContext<Services> generic constraining what handlers may yield* at compile time.

Reference: https://v2.orpc.dev/docs/integrations/effect and packages/effect/ in https://github.com/middleapi/orpc.

Proposal

@temporal-contract/experimental-effect, scoped to activities and the client only:

  1. Effectful activity implementations — accept Effect<Output, E, R> (or a generator, mirroring oRPC's handlerGen) in declareActivitiesHandler. The error channel folds onto the existing trichotomy, which maps 1:1 onto unthrown's ok/err/defect:
    • Effect.fail(ContractError) / Effect.fail(ApplicationFailure) → the modeled err channel (typed contract errors serialize as usual: type = error name, validated details, contract-driven nonRetryable);
    • any other failure/defect → rethrown raw (Temporal's default retryable handling), matching the current defect semantics.
  2. Context/Layer DI — provide services per-worker via an Effect Context/Layer supplied to declareActivitiesHandler (composing with the new createContext), with a WithEffectContext<Services>-style generic so implementations can only yield* declared services. A wrap hook (per oRPC's 'effect/wrap') covers tracing/OTel integration.
  3. Effect Schema support — Effect Schema implements Standard Schema (Schema.toStandardSchemaV1), so contracts can already use it; the package can add sugar for accepting Effect Schemas directly in defineActivity/defineWorkflow.
  4. Client sugar (optional) — helpers to lift the typed client's AsyncResult into Effect (err → error channel, defect → Effect defect).

Hard constraint: no Effect in workflow code

The Effect runtime's fiber scheduler touches timers and clocks, which violates workflow determinism (rule #1, .agents/rules/workflow-determinism.md). This package must be activities- and client-side only, and its docs should say so prominently. Workflow-side support would require an Effect runtime driven exclusively by Temporal's patched primitives — out of scope.

Sequencing

Best built after the typed contract errors feature, so the error bridge targets ContractError/typed error maps rather than bare ApplicationFailure.

Note: @orpc/experimental-effect targets effect >= 4.0.0-beta.90; deciding whether to target Effect v3 (stable) or v4 (beta) is part of this issue.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions