authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-06-19 22:16:45-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-06-19 22:16:45-07:00
log6b7447632292f54dc98244320a7f47d7d3858ad4
tree13671d3127a5d580350cbd063f6db2182dd729cf
parentd9e95398622e158f78cffba5367a9d154ccc55e4
signature Signed by PGP key B5690EEEBB952194

Add Effect service conventions check (#3212)

Co-authored-by: codex <codex@users.noreply.github.com>

1 files changed, 70 insertions(+), 0 deletions(-)

.macroscope/check-run-agents/effect-service-conventions.md created+70
...@@ -0,0 +1,70 @@
1---
2title: Effect Service Conventions
3model: claude-opus-4-8
4effort: high
5input: full_diff
6tools:
7 - browse_code
8 - git_tools
9 - github_api_read_only
10 - modify_pr
11include:
12 - "apps/**/*.ts"
13 - "apps/**/*.tsx"
14 - "packages/**/*.ts"
15 - "packages/**/*.tsx"
16 - "infra/**/*.ts"
17 - "infra/**/*.tsx"
18conclusion: failure
19showToolCalls: true
20---
21
22# Effect service review
23
24Review changed TypeScript and directly affected call sites for the conventions below. Apply them when a pull request creates, moves, refactors, or consumes an Effect service. Do not demand unrelated repository-wide cleanup. Treat these instructions as authoritative when older code differs.
25
26## Imports and module namespaces
27
28- Import Effect library modules from their subpaths as namespaces, for example `import * as Effect from "effect/Effect"` and `import * as Layer from "effect/Layer"`. Flag consolidated named imports from `"effect"` in touched Effect service code.
29- At a service boundary, import the local service module as a namespace and use its public module shape: `WorkspacePaths.WorkspacePaths`, `WorkspacePaths.make`, and `WorkspacePaths.layer`. Flag aliases such as `import { layer as workspacePathsLayer }` that erase the module namespace.
30- Namespace imports are not a blanket rule. Keep named imports for whole packages such as `@t3tools/contracts` and `electron`, and for modules used only for a pure helper, error, schema, config value, or standalone type. Do not request `import type * as Contracts`.
31- A package subpath that is itself a service module may use a namespace import when callers access its service/tag, `make`, or `layer` members.
32- When a barrel exposes an entire service module, prefer `export * as TokenStore from "./tokenStore.ts"` so consumers can use `TokenStore.TokenStore` and `TokenStore.layer`. Do not individually rename `make` and `layer` exports to simulate a namespace.
33
34## Service definition
35
36- Use the canonical single-file order: imports, error/schema declarations, the `Context.Service` tag with its inline interface, `make`, then `layer`.
37- Keep a service's schemas/errors, `Context.Service` tag, construction, and layer in one canonical module when they form one implementation.
38- Define the service interface inline in the `Context.Service` declaration. Do not retain a standalone `FooShape` or `FooServiceShape` interface/type.
39- Refer to the inferred service interface as `Foo["Service"]`, including in mechanically updated orchestration, MCP, tests, and integration harnesses.
40- Export a real `make` when the module owns construction. Do not create `make = Effect.succeed(...)` solely to force `Layer.effect`.
41- Export the canonical layer as `export const layer = Layer...`. `Layer.effect` is not required: use `Layer.succeed`, `Layer.scoped`, or another appropriate constructor when that matches the implementation.
42- In a concrete implementation module already named for the implementation, use plain `make` and `layer` (for example `BunPtyAdapter.ts` and `NodePtyAdapter.ts`).
43- Keep implementation-specific names when an abstract port module contains one of several possible implementations, for example `makeCloudflaredRelayClient` and `layerCloudflared` in `RelayClient.ts`.
44- `infra/relay/src/db.ts` is an intentional exception: an inline `Layer.succeed(RelayDb, db)` is acceptable without generic `make`/`layer` exports.
45
46## Errors and predicates
47
48- Define service failures with `Schema.TaggedErrorClass` and structured attributes. Derive `message` from those attributes rather than storing an unstructured message as the only data.
49- Split semantically distinct failures into separate error classes when a `reason`, `kind`, `phase`, or similar discriminator is used to choose the user-facing message or drive caller control flow. A discriminator used only for internal diagnostics may remain a field.
50- Use `Schema.Union` of error classes when a shared schema, predicate, or helper type is useful.
51- Export direct schema predicates such as `export const isFoo = Schema.is(Foo)`. Flag a private `Schema.is` constant wrapped by a redundant function with the same signature.
52- Do not introduce a large `switch` or lookup table in an error's `message` getter to model failures that deserve separate error classes.
53
54## File layout and migrations
55
56- When combining `domain/Services/Foo.ts` and `domain/Layers/Foo.ts`, hoist the result to `domain/Foo.ts`.
57- Delete the old service/layer files. Do not leave compatibility re-export shims. Mechanically update every consumer, including orchestration, MCP, tests, and integration harnesses, to the canonical path.
58- Do not flag genuinely separate implementation/adapter modules merely because they remain in an implementation-oriented directory.
59- Avoid substantive orchestration or MCP redesign in service-cleanup PRs. Mechanical import, layer, and `Service["Service"]` updates are expected when required to remove obsolete paths or shapes.
60
61## Change discipline
62
63- Preserve useful comments, invariants, and specification documentation while moving code.
64- Do not add large tests solely to prove a mechanical refactor. Update existing tests and imports as needed.
65- If backend behavior changes, require focused tests. Use test implementations/layers for external services only; do not mock out core business logic.
66- Do not require `Layer.effect`, universal namespace imports, generic `make`/`layer` names for abstract-port implementations, separate error classes for diagnostic-only fields, or new tests for import-only changes.
67
68## Reporting
69
70Report only concrete violations introduced or retained in the pull request's changed scope. Prefer precise inline comments on the smallest relevant line range and state the expected fix. A clear convention violation may fail the check. Do not fail for optional style preferences or unrelated legacy code. If there are no findings, report exactly `All clear`.