Effect v4
the harness rebuilds itself — agents rewrite agents, skills replace skills.
npx -y skills add theseus-run/theseus --skill effect-v4Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Use when writing, reviewing, or debugging Effect v4 beta code in Theseus, especially core Effect composition, Effect.gen, v3-to-v4 API translation, typed Effect signatures, and deciding which narrower Effect skill applies.
SKILL.md
7.3 KB, as published. Nobody here has run it
Effect v4
Use this skill for general Effect mechanics and routing. For Theseus product/domain choices, use the Theseus design skill.
Route To Narrow Skills
Load the narrower skill when the task clearly matches it:
effect-services-layers- Context.Service, service interfaces, Layer construction, dependency graphs, test seams.effect-errors-schema- typed failures, defects, Cause, Schema decoding/encoding, schema-backed errors, boundary normalization.effect-concurrency-lifecycle- Fiber, Deferred, Queue, PubSub, Stream, Scope, interruption, background loops, backpressure.effect-observability-time- Clock, Duration, timeout, retry schedules, logging, tracing spans, metrics, config, redaction.effect-testing-runtime- Effect.runPromise boundaries, test layers, deterministic services, TestClock/TestContext, runtime verification.
If several apply, load only the ones needed for the change.
Operating Model
Effect code is explicit about:
- what succeeds
- what can fail
- what services are required
- what resources must be acquired and released
- what runs concurrently and how it is interrupted
Good Effect code keeps these facts in the type signature until a deliberate boundary handles them.
Workflow
Effect APIs move. Before using an unfamiliar API:
- Search current repo usage.
- Resolve the installed
effectpackage from the lockfile or package manager layout, then inspect its local type declarations. - Prefer patterns already used in
packages/theseus-coreandpackages/theseus-server. - Treat web examples as v3 unless verified against local v4 types.
Useful searches:
rg "Context.Service|Layer.mergeAll|Layer.provideMerge|Effect.catchTag|Effect.forkDetach" packages
find node_modules -path '*effect*/dist/Effect.d.ts' -o -path '*effect*/dist/Schema.d.ts'
rg "export declare const catch|forkDetach|TaggedErrorClass" node_modules
The repo already has @effect/language-service configured in root tsconfig.json. Trust its diagnostics when Effect types look strange.
Effect language-service commands:
bun run effect:diagnostics- run Effect diagnostics across configured non-web packages.bun run effect:ls:check- check whether local TypeScript is patched for build-time Effect diagnostics.bun run effect:ls:patch- patch local TypeScript so Effect diagnostics surface through compiler tooling.
Type Shape
Effect.Effect<Success, Error, Requirements> means:
Success- success valueError- expected typed failureRequirements- required services/environment
Expected failures belong in the error channel. Defects are bugs, thrown exceptions, rejected promise defects, or violated invariants; handle them only at boundaries.
Core Constructors
Use the constructor that matches the boundary:
Effect.succeed(value) // pure success value
Effect.fail(error) // expected typed failure
Effect.sync(() => value) // sync, non-throwing side effect
Effect.try(() => risky()) // sync code that may throw
Effect.promise(() => p) // promise that cannot usefully map rejection
Effect.tryPromise({ // promise with rejection normalized to typed failure
try: () => fetchThing(),
catch: (cause) => new FetchFailed({ cause }),
})
Rules:
- Use
Effect.try/Effect.tryPromiseat foreign boundaries. - Normalize foreign exceptions into typed errors as early as possible.
- Do not wrap already-effectful code in
tryPromise. - Do not use
Effect.runPromiseas a composition tool.
Composition
Prefer Effect.gen for sequential business logic and pipe combinators for local transformations.
const program = Effect.gen(function* () {
const user = yield* Users.find(userId)
const account = yield* Accounts.find(user.accountId)
yield* Audit.log({ type: "account.viewed", userId })
return account
})
Rules:
- Use
Effect.mapfor success-value transformation. - Use
Effect.flatMapwhen the next step returns an Effect. - Use
Effect.tapfor effectful observation without changing the value. - Use
Effect.allfor independent effects; setconcurrencywhen work is unbounded or expensive. - Use
Effect.partitionwhen partial success is a valid outcome. - Use validation mode on collection operations only when the caller needs all failures, not fail-fast behavior.
- Keep
Effect.genblocks linear; extract named effects when nesting grows. - Use
Effect.fn("Name")for important service methods or runtime operations where tracing and better diagnostic names matter.
Pattern Matching
Use Match for tagged unions or multi-branch domain logic when chained conditionals obscure exhaustiveness.
import { Match } from "effect"
const render = Match.type<Event>().pipe(
Match.tag("Started", (event) => `started ${event.id}`),
Match.tag("Done", (event) => `done ${event.id}`),
Match.exhaustive,
)
Rules:
- Prefer
Match.tag/Match.tagsfor_tagunions. - Prefer exhaustive matching when the input union is closed.
- Keep simple two-branch cases as normal conditionals when that is clearer.
v4 Translation Table
Common v3 or stale examples need translation:
| Stale pattern | Theseus v4 pattern |
|---|---|
Effect.catchAll | Effect.catch |
Effect.catchAllDefect | Effect.catchDefect |
Context.Tag for services | Context.Service<Service, Shape>()("Name") |
uncurried Layer.effect(Tag, effect) | Layer.effect(Tag)(effect) |
ServiceMap.Service | removed; use Context.Service |
Schema.TaggedError | Schema.TaggedErrorClass for schema-backed failures |
| bare millisecond numbers for time | Duration.millis, Duration.seconds, or accepted duration strings when verified |
throw in Effect.gen | return yield* Effect.fail(error) or return yield* error for yieldable errors |
Do not cargo-cult Effect.Service unless local v4 types and repo patterns support it for the case at hand. This repo currently uses Context.Service.
Anti-Patterns
Avoid these unless the file is explicitly a process/test/script boundary:
Effect.runPromiseorEffect.runSyncinside services or domain functions.throwfor expected failures insideEffect.gen.try/catcharoundyield*expecting to catch typed Effect failures.console.login runtime code.- direct
process.envaccess outside config boundaries. Option.getOrThrowin runtime code.- unbounded queues or concurrency without an explicit bounding argument.
- type assertions that erase the error or requirements channel (
as any,as never) instead of fixing the layer/error model.
Theseus Checks
Before finalizing Effect changes:
- Search for matching local patterns.
- Re-read the involved service/layer/error signatures.
- Check whether the code is at a boundary or inside the domain; choose schemas/errors accordingly.
- Run the narrow package test when behavior changed.
- Run
bun run typecheckafter service, layer, schema, or error-channel changes. - If an API was inferred from external material, verify it in the locally installed Effect type declarations before committing the pattern.