| name | architecture |
| description | Use when implementing anything on this repo's slice architecture including creating a slice or package, adding a concept or schema, wiring a use-case end to end, sharing code across slices, translating errors, writing tests, and writing Effect code. |
Implementing on this architecture
Normative tables (families, roles, suffixes, ceilings, exports) live in ARCHITECTURE.md. tools/architecture/policy.ts enforces them via bun run lint — run it after every structural step; it is fast and its diagnostics tell you the correct shape.
packages/notes + apps/server are the compiled, tested, runnable worked example of everything below — when a playbook is ambiguous, imitate the example.
Route your task
| Task | Playbook |
|---|
| New slice or new package in a slice | references/add-a-slice.md |
| New domain concept, schema, model, domain error | references/add-a-concept.md |
| Command/query, port, adapter, handler, client, UI — the vertical chain | references/wire-a-use-case.md |
| Two slices need the same type, event, or contract | references/share-across-slices.md |
| Tests for any layer | references/testing.md |
| Writing Effect code (imports, errors, Options, Match, Layers) | references/effect-code.md |
The path is the spec
Every product file matches packages/<slice>/<role>/src/<domain-kind>/<Concept>/<Concept>.<role-suffix>.ts(x). Before creating a file, decode where it goes:
- Which slice owns the meaning? Product behavior goes in its slice. External SDK/engine wrapper →
drivers/. Domain-agnostic substrate → foundation/. Repo tooling → tooling/. Deliberately shared product language → shared/ (promotion required, see share-across-slices.md).
- Which role owns the action? Pure meaning →
domain. Application intent and ports → use-cases. Adapters and live Layers → server. Browser state/adapters → client. Persistence projection → tables. React → ui. Typed config contracts → config.
- Which suffix owns the file? Look it up in
ARCHITECTURE.md § Concept grammar. If no canonical suffix fits, you are probably putting the code in the wrong role.
Iron rules (never violate, in any playbook)
- Never import another slice's packages — integrate via events or a promoted
shared contract.
- Never import deep private paths — only declared subpath exports (
/public, /server, /secrets, /layer, /test, /browser); never wildcard exports.
domain imports nothing but its own slice's domain, shared domain, and foundation primitive/modeling. No config, no drivers, no Config/ConfigProvider, no side effects.
use-cases declares ports and contracts; it never implements adapters or exports live Layers.
- Schema-first: if data can be a
Schema, define the schema and derive the type. Finite cases are discriminated unions, never optional-field bags.
- Errors translate at every boundary: driver errors die in the adapter, port errors die in the use-case, action errors die in the handler.
- Create only what has a current concrete responsibility: no packages for symmetry, no placeholder exports, no empty role files, no
utils/common/helpers folders.
- Effect APIs: verify against the pinned Effect source via
opensrc (see CLAUDE.md) before using a nontrivial API.