You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 72097f7
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: .macroscope/check-run-agents/effect-service-conventions.md
+13Lines changed: 13 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -44,6 +44,18 @@ Review changed TypeScript for the conventions below. They apply when a pull requ
44
44
- Use plain `make` and `layer` in a module named for its implementation (`BunPtyAdapter.ts`). Keep implementation-specific names when one abstract port module holds several implementations (`makeCloudflaredRelayClient`, `layerCloudflared` in `RelayClient.ts`). `infra/relay/src/db.ts` may keep its inline `Layer.succeed(RelayDb, db)`.
45
45
- When a service moves, delete the old files and update every consumer, including orchestration, MCP, tests, and integration harnesses. Do not leave compatibility re-export shims.
46
46
47
+
## Effect functions and tracing
48
+
49
+
- Reusable functions use named `Effect.fn("Service.operation")`, not functions wrapping
50
+
`Effect.gen`. Inline programs, callbacks, and layer construction can keep `Effect.gen`.
51
+
- Named `Effect.fn` creates a span; the unnamed form does not. Default to named spans for service
52
+
operations and I/O. `Effect.fnUntraced` is deliberate for per-chunk/per-event work, tight loops,
53
+
or helpers duplicating an existing tracing boundary; require a nearby reason for new uses.
54
+
- Preserve existing span names/options and avoid adding duplicate spans. Pass combinators after
55
+
the generator body; transforms receive the original function arguments after the effect.
56
+
- Preserve generic signatures, laziness, resource scope, and interruption behavior. An explicit
57
+
generator return type is `Effect.fn.Return<A, E, R>`.
58
+
47
59
## Dependency acquisition and runtime boundaries
48
60
49
61
- Production service construction acquires its Effect dependencies from the environment with `yield* Foo.Foo`, and `make`/`layer` types expose those requirements. Flag a factory that takes `Foo["Service"]` (or an object of Effect-returning methods) as a parameter when that value is a service dependency. Passing service instances explicitly in tests is fine; passing pure configuration, immutable domain values, or deliberate callback strategies is not service injection.
@@ -57,6 +69,7 @@ Review changed TypeScript for the conventions below. They apply when a pull requ
57
69
- Define service failures with `Schema.TaggedError` and structured attributes: operation or stage, resource path or entity identifier, normalized category or status. Derive `message` from those attributes only. Never derive it from `cause`, `cause.message`, or a stringified defect, and do not add a `detail` field that copies `cause.message`.
58
70
- When wrapping a real failure, keep the immediate underlying error as `cause` so the chain and stack survive. Make `cause` required if every construction wraps a failure. Pure validation or domain errors created without an underlying failure need no cause.
59
71
- Keep attributes and log annotations safe and bounded: no raw wire payloads, command arguments or output, signed URLs, credentials, query strings, or arbitrary defect text. Preserve the exact value only as `cause`; expose normalized categories, lengths, counts, and safe URL protocol or hostname where useful.
72
+
The documented exception is `DeviceBootError.diagnostics`: device hub launch commands and failure text are intentionally exposed to authorized device users and agents, capped at 2,000 characters and included in the error message. Do not apply this exception to other service errors.
60
73
- At a translation boundary, pass through an already structured domain error when it is part of the target error channel; wrap only unknown or lower-level failures. Map failures where the context is known instead of wrapping a whole multi-step pipeline in one generic error.
61
74
- Do not encode the same distinction twice with both a specific error tag and a single-value `operation`, `reason`, `kind`, or `phase` literal. Split into separate error classes when a discriminator drives caller control flow or the user-facing message; a discriminator used only for diagnostics may stay a field. Caller-visible messages exposed through HTTP, RPC, persisted state, or UI are behavior and must survive a structural refactor.
62
75
- Do not add a helper whose only behavior is `(...args) => new SomeError({ ...args })`. Construct the error at the failure boundary. Keep a mapper only when it performs real normalization, passes through domain errors, or adds reusable context; when such a mapper belongs to the target error type, prefer a static factory on that class.
Copy file name to clipboardExpand all lines: AGENTS.md
+6Lines changed: 6 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -153,6 +153,12 @@ Architecture and its constraints: `docs/internals/overview.md`. Glossary: `docs/
153
153
- Client mutations use `createEnvironmentRpcCommand`. Add protected methods to `CLIENT_GUARDED_RPC_SCOPES` in contracts and use the command's `permissionAtom` for UI availability. Grants are checked at execution against the destination environment; the server remains authoritative. Keep raw RPC clients inside `rpc/`, and extend the permission behavior tests when adding a protected method.
154
154
- Server features are services; transports stay thin. A `ws.ts` RPC handler, HTTP route, or MCP tool decodes input, calls one service method, and maps errors. See [Effect services](docs/internals/effect-services.md).
155
155
-`apps/web/src/components/ui` exports own their look. Pick a `variant` or `size`; do not restyle one with `className`. If none fits and the look is a generic concept, add a variant to the component; a look that belongs to one feature stays in that feature's own component, not in `components/ui`. Layout classes (width, flex, margin, position) belong on the parent. `shadcn/no-restyle` fails lint on violations. See [Web UI](docs/internals/web-ui.md).
156
+
- Reusable Effect functions use `Effect.fn("Service.operation")` with a stable span name, not a
157
+
function wrapping `Effect.gen`. Keep `Effect.gen` for inline programs and layer construction.
158
+
Use `Effect.fnUntraced` only when spans would be too frequent (stream chunks, per-event
159
+
projection, tight loops) or duplicate an existing tracing boundary; explain that choice nearby.
160
+
Named `Effect.fn` creates a span; the unnamed form does not. Preserve existing span names and
161
+
options, and pass combinators as additional function-builder arguments.
156
162
- Inferred types over annotations. `any` is the enemy.
157
163
- Comments say why: a constraint, an external quirk, or an invariant the code can't show. Don't restate names or signatures, narrate steps, or record history. `t3code/no-low-value-comments` catches the mechanical cases.
158
164
- Our users drive agents all day and notice a dropped frame, a lying spinner, and a stale label. No continuously repainting animations; they peg the GPU on high-refresh displays.
0 commit comments