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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions