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:
- 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.
- 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.
- 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.
- 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
Motivation
oRPC v2 ships
@orpc/experimental-effect(beta), and its shape is worth copying even more than its code:experimental-*package witheffectas a peer dep — zero cost to non-users, and theexperimentalprefix keeps semver honest while Effect v4 is itself in beta.import '@orpc/experimental-effect/extensions/effect'adds an.effect()builder method) — integrations ship without bloating core.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.'effect/context','effect/wrap') with aWithEffectContext<Services>generic constraining what handlers mayyield*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:Effect<Output, E, R>(or a generator, mirroring oRPC'shandlerGen) indeclareActivitiesHandler. 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 modelederrchannel (typed contract errors serialize as usual:type= error name, validateddetails, contract-drivennonRetryable);Context/Layersupplied todeclareActivitiesHandler(composing with the newcreateContext), with aWithEffectContext<Services>-style generic so implementations can onlyyield*declared services. Awraphook (per oRPC's'effect/wrap') covers tracing/OTel integration.Schema.toStandardSchemaV1), so contracts can already use it; the package can add sugar for accepting Effect Schemas directly indefineActivity/defineWorkflow.AsyncResultintoEffect(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 bareApplicationFailure.Note:
@orpc/experimental-effecttargetseffect >= 4.0.0-beta.90; deciding whether to target Effect v3 (stable) or v4 (beta) is part of this issue.🤖 Generated with Claude Code