agentleFS
theseus-runtheseus-run/theseus/.agents/skills/effect-v4/SKILL.md

effect-v4

Skill1 starsChanged 6 months agoMITView on GitHub

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.

  • Reads credentials

8 other projects use a version of this.

Adopted by

Most recently taken up by

The file

Show its rules and text

We split this skill into the rules it gives an agent

30 rules. 0 are common, 12 are distinctive, and 18 are very specific to this project.

  • 0

    Common

    Most of the 6 edited versions we read keep these.

  • 12

    Distinctive

    This author's own choices. Worth a look.

  • 18

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Use this skill for general Effect mechanics and routing.

    line 8
  2. Distinctive:

    If several apply, load only the ones needed for the change.

    line 20
  3. Distinctive:

    Search current repo usage.

    line 38
  4. Very specific:

    Prefer patterns already used in packages/theseus-core and packages/theseus-server.

    line 40
  5. Distinctive:

    Treat web examples as v3 unless verified against local v4 types.

    line 41
  6. Distinctive:

    Use the constructor that matches the boundary:

    line 71
  7. Very specific:

    Use Effect.try / Effect.tryPromise at foreign boundaries.

    line 87
  8. Very specific:

    Do not wrap already-effectful code in tryPromise.

    line 89
  9. Very specific:

    Do not use Effect.runPromise as a composition tool.

    line 90
  10. Very specific:

    Prefer Effect.gen for sequential business logic and pipe combinators for local transformations.

    line 94
  11. Very specific:

    Use Effect.map for success-value transformation.

    line 107
  12. Very specific:

    Use Effect.flatMap when the next step returns an Effect.

    line 108
  13. Very specific:

    Use Effect.tap for effectful observation without changing the value.

    line 109
  14. Very specific:

    Use Effect.all for independent effects.

    line 110
  15. Very specific:

    Use Effect.partition when partial success is a valid outcome.

    line 111
  16. Distinctive:

    Use validation mode on collection operations only when the caller needs all failures, not fail-fast behavior.

    line 112
  17. Very specific:

    Keep Effect.gen blocks linear.

    line 113
  18. Very specific:

    Use Effect.fn("Name") for important service methods or runtime operations where tracing and better diagnostic names matter.

    line 114
  19. Very specific:

    Use Match for tagged unions or multi-branch domain logic when chained conditionals obscure exhaustiveness.

    line 118
  20. Very specific:

    Prefer Match.tag / Match.tags for _tag unions.

    line 132
  21. Distinctive:

    Prefer exhaustive matching when the input union is closed.

    line 133
  22. Distinctive:

    Keep simple two-branch cases as normal conditionals when that is clearer.

    line 134
  23. Very specific:

    Do not cargo-cult Effect.Service unless local v4 types and repo patterns support it for the case at hand.

    line 151
  24. Very specific:

    Avoid these unless the file is explicitly a process/test/script boundary:

    line 155
  25. Distinctive:

    Before finalizing Effect changes:

    line 168
  26. Distinctive:

    Search for matching local patterns.

    line 170
  27. Distinctive:

    Check whether the code is at a boundary or inside the domain.

    line 172
  28. Distinctive:

    Run the narrow package test when behavior changed.

    line 173
  29. Very specific:

    Run bun run typecheck after service, layer, schema, or error-channel changes.

    line 174
  30. Distinctive:

    If an API was inferred from external material, verify it in the locally installed Effect type declarations before committing the pattern.

    line 175

“Common” is measured: kept by at least 6 in 10 of the 6 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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:

  1. Search current repo usage.
  2. Resolve the installed effect package from the lockfile or package manager layout, then inspect its local type declarations.
  3. Prefer patterns already used in packages/theseus-core and packages/theseus-server.
  4. 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 value
  • Error - expected typed failure
  • Requirements - 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.tryPromise at foreign boundaries.
  • Normalize foreign exceptions into typed errors as early as possible.
  • Do not wrap already-effectful code in tryPromise.
  • Do not use Effect.runPromise as 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.map for success-value transformation.
  • Use Effect.flatMap when the next step returns an Effect.
  • Use Effect.tap for effectful observation without changing the value.
  • Use Effect.all for independent effects; set concurrency when work is unbounded or expensive.
  • Use Effect.partition when 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.gen blocks 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.tags for _tag unions.
  • 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 patternTheseus v4 pattern
Effect.catchAllEffect.catch
Effect.catchAllDefectEffect.catchDefect
Context.Tag for servicesContext.Service<Service, Shape>()("Name")
uncurried Layer.effect(Tag, effect)Layer.effect(Tag)(effect)
ServiceMap.Serviceremoved; use Context.Service
Schema.TaggedErrorSchema.TaggedErrorClass for schema-backed failures
bare millisecond numbers for timeDuration.millis, Duration.seconds, or accepted duration strings when verified
throw in Effect.genreturn 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.runPromise or Effect.runSync inside services or domain functions.
  • throw for expected failures inside Effect.gen.
  • try/catch around yield* expecting to catch typed Effect failures.
  • console.log in runtime code.
  • direct process.env access outside config boundaries.
  • Option.getOrThrow in 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 typecheck after 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.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.