Agent instructions
For a non-trivial task, start with skills/rat-stack-mode.
This file is the repo law for agents and contributors. It holds commands, validation rules, architecture constraints, and project stop rules. Read VISION.md for intent before planning substantial work. VISION.md does not override this file. Pi sessions also load .pi/APPEND_SYSTEM.md and the repo-local extension .pi/extensions/project.ts.
Stack contract
Workspace package.json files declare the pinned stack. README.md summarizes it. Keep dependencies exact. Repo-local config wins. Record drift instead of silently migrating the project.
- pnpm workspaces + Turborepo (
apps/*,packages/*) - Node
>=24.18.0and pnpm11.3.0; do not replace pnpm with Bun or npm for installs - Effect
4.0.7and@effect/platform-node4.0.7. Foldkit210.169.0peers on exact4.0.0;peerDependencyRules.allowedVersionsadmits4.0.7, and the@foldkit/vite-pluginpatch invendor/README.mdtravels with it. Every Effect package resolves to one runtime. - XState
6.0.0-alpha.71for finite lifecycles, retries, cancellation, and resumability @xstate/effect1.0.0-alpha.10bridges the two: machines run as scoped Effects viacreateEffectActor, side effects are declaredcreateEffectLogicactors.vendor/README.mdkeeps the rules for the next unpublished pin- Foldkit
0.169.0with@foldkit/uiand@foldkit/devtools0.169.0and@foldkit/vite-plugin0.28.0for the web UI, server-rendered and prerendered by default - Alchemy
2.0.0-beta.84with the@alchemy.run/cloudflare,@alchemy.run/drizzle, and@alchemy.run/sqlproviders (Infrastructure as Effects10) for every cloud resource; declared inapps/infra/alchemy.run.ts, authenticated through Alchemy profiles, never through env vars in this repo - TypeScript
7.0.2in strict mode, patched by@effect/tsgo0.55.0inprepareso the Effect language service diagnostics intsconfig.base.jsonfailtsc, not just the editor. Escape hatch for a real boundary:// @effect-diagnostics-next-line <rule>:offwith a reason @yielded/effect-tsrs0.2.3runstypecheckandcheckwithdiagnostics --project tsconfig.json --strict --type-check.--type-checkincludes ordinary TypeScript diagnostics;--strictmakes Effect warnings fail. It bundles tsrs revision8f135f106a63, contained in plain tsrs0.20.0. KeepignoreEffectWarningsInTscExitCodefalse. Patched TypeScript7.0.2with@effect/tsgo0.55.0runs throughtypecheck:tscandtypecheck:parityat the switch and on checker bumps (pnpm bump:checkers), not on every CI run. Report any checker disagreement.- Plain tsrs
0.20.1-ts7.1.0-betastays intypecheck:fast, targeting TypeScript7.1.0-beta. It still has no Effect diagnostics; usetypecheckorcheckfor those. @effect/vitest4.0.7for every Effect test:it.effectandit.layer(layer);Effect.run*andManagedRuntime.makein test files are a lint error@oxlint/plugins1.88.0for the two typed lint rules inscripts/oxlint-plugin-*.tsand the vendored anti-slop rules intools/oxlint/anti-slop/(all generic rules plus the Effect group, aterror). The copy is ours;UPSTREAM.mdthere says where it came from and how to take upstream fixes- Oxlint
1.88.0with Ultracite8.0.1, Oxfmt0.73.0, and Turborepo2.11.9 - varlock
1.23.0: declare every env var in.env.schema, never read.env.localdirectly, runpnpm env:checkafter schema edits
The base Effect plugin owns diagnostic severity. Child configs may add exact allowedUnstableApis only for modules warned about in that scope34. Do not use subtree prefixes, copy severity maps, or set ignoreEffectWarningsInTscExitCode to true.
Packages
@rat-stack/capability:- Path:
packages/capability - Role:
defineContract,implement, and thetoCommand,toHttpApi,toToolkit,toRpc, andtoCodeModeprojections
- Path:
@rat-stack/core:- Path:
packages/core - Role: Shared
inspectFile,search, andreadcontracts;inspectFilehandler, lifecycle machine26, andFileInspector
- Path:
@rat-stack/database:- Path:
packages/database - Role: Database cartridge38: the
RunLogservice and theDatabaseVendorchoice (D1 or Hyperdrive Postgres), with each vendor’s schema, migrations, and resources
- Path:
@rat-stack/auth:- Path:
packages/auth - Role: Better Auth16 cartridge over the chosen
DatabaseVendor:Auth,CurrentPerson, and the RPC middleware that provides it; onlyUnauthenticatedcrosses the wire.@rat-stack/auth/devtoolsaddsrunAsPerson, the dev-onlyrat_test_personcapability, andtestPersonLayer
- Path:
@rat-stack/flags:- Path:
packages/flags - Role: Typed flags cartridge: scoped evidence ladders, Config and memory adapters, cartridge-owned Alchemy KV, and a default Durable Object ledger. The DO commits values, receipts, and projection markers together; alarms project to KV. Readers never call the DO. Empty KV uses Config or declared defaults; read failures follow the catalog’s
failurePolicy.EVENTS_ENABLEDstays deploy-time Config. Core owns declarations and theflip_feature_flag,record_flag_evidence, andread_flag_ledgercontracts. Climbs use capability approval;offbypasses approval but stays audited. Only the CLI mounts operator capabilities; its default composition has no live ledger. Public readers and the sandbox stay read-only. Replace both exports inapps/site/src/flags.tswithFlags.defaults(eventFlags)to remove the adapter package.
- Path:
@rat-stack/events:- Path:
packages/events - Role: Events cartridge: capture-first request analytics18.
withEventCapturerecords onerequestevent per HTTP request into core’sEventSinkport and owns visitor ids, the visitor salt, identity modes, and query and geography redaction.src/basin.tsprovides the Basin-backed sink and salt;src/memory.tsis the in-memory sink for tests.@rat-stack/events/siteis the one seam:apps/site/src/events.tsis the only file outside the package that imports it, andrat-stack-boundaries/no-cross-layer-importsenforces that. Removal is one line in that file plus deleting the package and itsapps/site/package.jsonline;packages/events/README.mdhas the steps. Core owns theEventSinkport (no-op default), theRawEventwire schemas,@rat-stack/core/events-off. Tests are property and model based (it.prop,it.effect.prop).EVENTS_ENABLED(default false) turns capture and the catalog, sink, and pipeline on, and readsEVENTS_SINK_TOKENat deploy time only; off still declares the bucket, stream, and salt so their state survives.EVENTS_IDENTITY_MODEpicksdailyorpersistentvisitor ids
- Path:
@rat-stack/crash:- Path:
packages/crash - Role: Private crash-trace archive: trace schemas,
captureCrashes, and the R2 archive layer (@rat-stack/crash/r2). Mischief’s tail consumer provides it inapps/site/src/crash-tail.ts
- Path:
@rat-stack/subscriber-delivery:- Path:
packages/subscriber-delivery - Role: External subscriber intake and confirmation adapter; implements core’s job-shaped
SubscriberIntakeandSubscriberConfirmports, provided by the Worker, never imported by core
- Path:
@rat-stack/learn:- Path:
packages/learn - Role: Source-derived public concept deck10 and the learning lifecycle. Stateless capabilities return compact progress;
@rat-stack/learn/localowns the local JSONL adapter and capabilities. The CLI provides the filesystem platform for core’sLearnerProgressport.
- Path:
@rat-stack/lore:- Path:
packages/lore - Role: Public content graph and traversal service
- Path:
@rat-stack/devtools:- Path:
packages/devtools - Role: 🐀 devtools cartridge:
CallLog,record, and therat_*capabilities that list, read, dispatch, replay, and diff capability calls
- Path:
@rat-stack/web:- Path:
apps/web - Role: Default UI bin: Foldkit on Vite, deployed through Alchemy’s
Cloudflare.Website.FoldkitasWebsite(apps/web/src/website.ts). Server rendering9 and prerendering are the default: onefoldkit({ ssr: { serverEntry, clientEntry, build: { prerender } } })call invite.config.tsbuilds the client bundle, the server fetch handler, and an HTML file for every path insrc/entry.server.ts’sprerenderPaths. The Website serves those files, sends every other request to the handler, and the browser hydrates. System page:.brain/areas/web.svx9. Browserclient/Commands call an EffectRpcClientbuilt from the contracts’toRpcGroup. The reader program mounts the document browser (features/app.ts,client/docs) as a Submodel at/search, where it callssearchandreadover that client. In production, the Worker that@foldkit/vite-plugingenerates fromsrc/entry.server.tsforwards/rpcthroughsrc/server/website-bindings.tsto the privateRpcBackendinapps/site/src/rpc-worker.ts.pnpm --filter @rat-stack/web devloads the#backendimport, which resolves only under thedevelopmentcondition inapps/web/package.json, and serves the content capabilities in process fromsrc/dev/backend.ts. In dev,src/dev/backend.tsruns the content capabilities under devtools:/rpccalls are recorded,/__rat/rpcserves the 🐀 overlay (#devtools-overlay,src/dev/features/overlay, ⌘K), and/__rat/mcpserves everyrat_*tool to agents, with memory-Auth test people.src/dev/clientandsrc/dev/featuresfollow the browser rules.test/production-bundle.test.tsproves nosrc/devmodule, no devtools code, and no test people reach a production build. Include it in the Stack withconst website = yield* Website;
- Path:
@rat-stack/cli:- Path:
apps/cli - Role: Composition root17:
stats,catalog,openapi,serve [--devtools],mcp [--code-mode] [--devtools]commands. Also builds the publishedratstacknpm package:src/ratstack.tsbundles the local learn commands and a learn-onlymcpintodist/npmwith Vite.test/ratstack.e2e.test.tsproves the package holds no devtools and serves only the learn tools
- Path:
@rat-stack/infra:- Path:
apps/infra - Role: Alchemy Stack: the project’s cloud footprint as one Effect program
- Path:
@rat-stack/site:- Path:
apps/site - Role: Cloudflare Worker: the public site, agent discovery, and sandboxed execute surface25
- Startup fence45:
scripts/startup-budget.tsmeasures Alchemy’s actual upload. Follow static imports from each entry; count shared chunks once and exclude dynamic-only chunks. Hard startup ceilings are shrink-only: Mischief 1,250,000 bytes, LearnFeedbackAuth 1,600,000 bytes. Never raise them to accept growth. Entry bytes are evidence only; total JavaScript above 2,500,000 bytes warns without blocking. Growing page data stays in static assets outside the startup code. Measurement and profiling:apps/site/STARTUP.md.
- Path:
Nouns
- Contract:
- Home:
packages/core/src/contracts.ts - Runtime job: Shared name, schemas, failure, annotations, and approval setting.
- Home:
- Capability:
- Home:
packages/core/src/inspect-file.tsorapps/site/src/capabilities/<name>.ts - Runtime job: Binds one contract to its server-side handler with
implement.
- Home:
- Projection:
- Home:
packages/capability/src/to-<surface>.ts - Runtime job: Expose implemented capabilities on one runtime surface.
- Home:
- Cartridge:
- Home:
packages/<name>/src/ - Runtime job: One Layer with its own infrastructure; it must pass the cartridge test in
VISION.md.
- Home:
- Machine:
- Home:
packages/core/src/<name>-machine.ts - Runtime job: Own one finite domain lifecycle.
- Home:
- Feature:
- Home:
apps/web/src/features/<name>/ - Runtime job: Thin route and view that read the Model and send Messages.
- Home:
- System page:
- Home:
.brain/areas/<name>.svx - Runtime job: Public page at
/systems/<name>for one system rat-stack ships: what it does, the standard it keeps, and how to check it. The content build rejects a system page without those three sections or outside.brain/areas/.
- Home:
- Client:
- Home:
apps/web/src/client/<name>/ - Runtime job: Own the Foldkit program’s Model, Messages, update, and named Commands; Commands call an
RpcClientover the contract group.
- Home:
Commands
pnpm install: Install workspace dependenciespnpm sources:fetch: Import missing pinned code objects once before the first gate; generation stays local-onlypnpm dev: Runapps/webundervite devthrough portless athttp://rat-stack.localhost:1355(worktrees get their own prefix);portless listshows the routepnpm check: Typecheck, verify formatting, and run type-aware lintingpnpm lint: Type-aware lint plus format check only;turbo run checkruns this once at the root as//#lintpnpm fix: Apply Oxfmt and safe Oxlint fixespnpm test: Build and run the Vitest suite oncepnpm build: Compile package outputspnpm typecheck: Strict effect-tsrs checks for workspaces and root scriptspnpm typecheck:tsc: Patchedtscfor workspaces and root scripts; runs at checker bumps, not in CIpnpm typecheck:fast: Fast feedback in seconds; no Effect diagnostics;pnpm checkis still the gatepnpm typecheck:parity: Compare effect-tsrs and patchedtscerrors and exit verdicts, includingTS377xxx; also compare plain tsrs with Effect-only codes excludedpnpm bump:checkers: Bump both checker pins to npm latest, install without scripts, report bundled revision alignment, and run both parity pairs; never commit or pushpnpm vendor:agent-sources: Shallow-clone Effect, effect-solutions, xstate, alchemy, better-auth, and Foldkit mirrorspnpm infra:plan: Preview the Alchemy Stack diff without applyingpnpm infra:deploy/pnpm infra:destroy: Apply or tear down the Stack (asks for approval)pnpm exec lefthook install: Install git hooks (also viaprepare)pnpm turbo run check test build: Required validation before claiming a change is ready
Run pnpm fix only when you intend to rewrite files. Finish with pnpm turbo run check test build.
Deploy
Follow ship and skills/ship/references/project.md for deployment commands, approval, checks, and recovery.
Release the ratstack package
.github/workflows/ratstack-publish.ymlpublishesratstackto npm with trusted publishing (OIDC). No npm token exists in the repo or its secrets.- Bump
apps/cli/npm/ratstack/package.jsonandapps/cli/src/version.tstogether. The e2e test fails when they differ. - Merge to
main, then push the tagratstack-v<version>on that merged commit. The workflow refuses a tag that does not match the version or is not onmain. - The workflow runs the full gate, then
npm publishfromapps/cli/dist/npm. npm adds provenance. - The npm package settings name this repo,
ratstack-publish.yml, and thenpmenvironment as the trusted publisher. Changing the workflow filename breaks publishing.
Fence (cheating is uncomfortable)
Why lives in AGENTS.md / VISION.md. The stack and hooks are the enforceable fence. Fence wins over prose.
- Lefthook pre-commit runs
pnpm checkandpnpm test.checkruns strict effect-tsrs; floating Effects and warning-level Effect diagnostics fail. CI type-checks throughcheckonly. - Agents must not use
git … --no-verify(or equivalent hook bypass) - Blocked by: Pi
.pi/extensions/git-interceptor, Cursor.cursor/hooks.json, Claude Code.claude/settings.json - Policy source:
scripts/vcs-command-policy.js
If a hook fails, fix the failure. Do not disable the fence.
Source-first Effect / XState work
Before writing, reviewing, or refactoring Effect or XState code, read the installed Effect package’s AGENTS.md first, for example packages/core/node_modules/effect/AGENTS.md. It ships with the installed version, so it is never stale. Then inspect vendored source for the pinned versions. Populate mirrors:
pnpm vendor:agent-sources
# or
./scripts/vendor-agent-sources.sh --refresh Inventory: .agent_sources/README.md is generated in the local source mirror and is not part of this repository.
- Effect Schema, Context.Service, CLI:
.agent_sources/github.com/Effect-TS/effect/ - Idiomatic Effect:
.agent_sources/github.com/kitlangton/effect-solutions/ - XState:
.agent_sources/github.com/statelyai/xstate/ @xstate/effect(v6 Effect bridge):.agent_sources/github.com/statelyai/xstate/packages/xstate-effect/(README.md,docs/,src/*.test.ts)- Alchemy resources, Cloudflare, AWS:
.agent_sources/github.com/alchemy-run/alchemy/and https://alchemy.run/llms.txt
Refs are derived from the workspace pins, so bumping a package and re-running the script keeps them matched. The pi-effect tool reads the Effect mirror in place; it needs no link.
Mirrors are reference material, not runtime dependencies. Exclude them from typecheck, test, lint, and format. Do not vendor product-specific corpora in this template.
Source control
Preserve existing work. Inspect status before editing, stage only files changed for the current task, and do not commit generated output, secrets, or populated .agent_sources/github.com/ trees. Use the repository’s existing source-control tool; do not initialize or migrate one without approval. Never pass --no-verify to git.
Project law
A child project replaces this section on day one with its own product rules. These rules describe the rat-stack template, not the product in a clone. Keep durable product intent in VISION.md.
- One contract, every surface. Define schemas and metadata with
defineContract; bind a server-side handler withimplementand add the capability tocapabilities27. CLI, HTTP, MCP, RPC, and code mode are projections inpackages/capability. Do not add a command, route, or tool handler that bypasses a capability. - Schemas are the contract. Input, output, and failure are Effect
Schema; JSON Schema, OpenAPI, and the code-mode declarations are derived from them, never hand-written. - Lifecycles are machines. Finite modes, retries, and cancellation live in XState machines started with
createEffectActor; side effects live in declaredcreateEffectLogicactors, never inline. - The sandbox is a surface, not a bypass. Anything reachable from a code-mode program must be a capability and goes through that capability’s schemas and handler.
- Gardener rule: add a lint rule before cleaning up a bad pattern; lint baselines only shrink. The routine is
skills/gardener; before simplifying or replacing a design, runskills/uncomplect. - Refresh peers, versions, and tiers after every bump of Effect, Alchemy, XState, or
@xstate/effect. On every gardener pass, refresh peers whose checked date is more than 14 days old. Report stale dates; they never block other gardener work. The repo-nativeskills/gardenerpass owns the peers refresh throughskills/find-peers: check default-branch manifests or lockfiles, record exact versions and checked dates in.brain/data/peers.json, and explain tiers with evidence. The content build reads our pins from manifests and filters/sorts that dataset into.brain/resources/peers.svx; never hand-edit generated entries. Drift is expected. Preserve every peer; show S through B in the main list and C through F in a collapsed Also seen list. Put the rubric and full ranking at the top of the report for feedback after shipping; no separate approval round is required. - Tautological tests considered harmful.
- Change-detector tests considered harmful.
- Do not create regression tests for bug fixes without a genuine gap in behavior testing.
- Prefer property and model tests over example tests. A domain rule is an
it.propover EffectArbitraryinputs; anything with history (sessions, retries, lifecycles) is a model checked over generated command sequences. Keep example tests for seams. Plant a bug before trusting a new property: it must fail. Reference:packages/events/test/. - Errors are part of the interface. Name the failure, keep its source context, say how to fix it, and fail at the boundary. Build-time code references accumulate every independent failure before emitting assets; drift is a warning, never a fallback.
packages/code-snippetsowns the build-only excerpt pipeline and ports;/gitand/shikiare adapters. Worker graphs must contain neither code-snippets nor Shiki.- No comments in code.
no-comments/no-comments(scripts/oxlint-plugin-no-comments.ts) bans them, after Lauren Tan’s Dune rule: agents copy comments, and a comment that explains a workaround spreads the workaround. Say it in a name, a type, a test, a commit message, or the Brain. Two kinds survive: tool directives, with the reason inline after--, and a one-lineSAFETY:invariant above a type assertion. TypeScript’s@ts-expect-error,@ts-ignore, and@ts-nocheckcount as directives here;typescript/ban-ts-commentdecides which may stay, so write// @ts-expect-error -- <reason>.--fixdeletes prose, so move anything a comment knows into a name, a test, the commit, or the Brain before running it. Plain JS files keep JSDoc blocks made only of type tags (@param {string} command), because that is their type syntax; Ultracite’s JSDoc description rules are off for the same reason. Credit for borrowed code lives inPROVENANCE.md. - Diagnostic overrides are targeted and explained:
// @effect-diagnostics-next-line <rule>:off -- <reason>. File-level overrides exist only at projection boundaries (packages/capability/src/to-toolkit.ts,packages/capability/src/to-code-mode.ts,packages/capability/src/to-rpc.ts) and in process-spawning test files. - Lint overrides follow the same rule:
// oxlint-disable-next-line <rule> -- <reason>. Every type assertion carries a one-lineSAFETY:comment naming the invariant it relies on.unicorn/throw-new-erroris off becauseSchema.TaggedError(...)looks like a throw to it. Anti-slop overrides exist only where a type is erased on purpose (the projections inpackages/capability, the sandbox RPC boundary, the lint plugin’s AST walker); anywhere else, parse the value at its boundary with EffectSchema. no-comments/directive-reason(scripts/oxlint-plugin-no-comments.ts) enforces the reason: everyoxlint-disable*oreslint-disable*comment without text after--fails lint.- Hot pink is the only accent.
--accent(#ff1493) marks borders, rules and focus rings;--accent-text(#c8006e, 5.7:1 on white) is the only pink text. Use it for at most one thing per screen, plus focus. News chrome uses no pink. The allowed selectors cover:focus-visibleand copy controls; links, headings, tables and tier cells never use it.apps/site/test/accent.test.tsholds the allowed selectors. - Pins stay exact. A vendored dependency carries a matching
minimumReleaseAgeExcludeentry and a removal rule invendor/README.md.
Architecture
A child project replaces this section on day one with its own architecture. Record module boundaries, dependency direction, data ownership, and state-machine seams here. Link deeper docs instead of duplicating them.
rat-stack-boundaries/no-core-adapterskeeps provider adapters out of core. Core owns job-shaped ports; adapter packages implement them, and apps provide them at composition.packages/subscriber-deliveryimplements subscriber intake and confirmation;packages/core/test/boundaries-rule.test.tschecks the boundary.rat-stack-boundaries/no-cross-layer-importsenforces dependency direction:apps/cli→packages/core→packages/capability→ Effect/XState. Packages do not import apps;packages/capabilitydoes not import domain code;apps/infraowns its Stack wiring.coreowns shared contracts and theinspectFilehandler; each capability’s owner keeps its handler beside its data or infrastructure. Core depends oncapabilityfordefineContractandimplement.rat-stack-boundaries/no-browser-server-importsapplies toapps/*/src/features/**andapps/*/src/client/**. Browser modules may import only@rat-stack/core/contractsand@rat-stack/capability/rpc-groupfrom these packages; they cannot import handler modules.rat-stack-boundaries/no-feature-transportapplies toapps/*/src/features/**.rat-stack-boundaries/no-hand-rolled-surfacekeepsRpc.make,RpcGroup.make,HttpApi*,Tool.make, andToolkit.makeinsidepackages/capability; everything else projects contracts.rat-stack-boundaries/no-browser-globals-on-serverkeepswindow,document,navigator, and web storage inapps/*/src/client/**andapps/*/src/features/**.rat-stack-patterns(scripts/oxlint-plugin-patterns.ts):contract-binding-matches-name(const searchContract = defineContract("search", …)),no-module-level-mutable-state(modulelet/varinapps/*/srcandpackages/*/srcleaks across Worker requests), andacquire-release-constructs-in-acquire-body(build pools inside acquire, not before).packages/core/src/file-inspector.tsis the reference service shape: aContext.Serviceclass whosemakecaptures its dependencies so its methods carry no requirements, withstatic layerbeside it.packages/core/src/config-service.tsderives a service from EffectConfig(productionlayerreads the ConfigProvider,configLayertakes parsed values for tests);AppConfigis the instance and mirrors.env.schema.packages/core/src/inspect-machine.tsis the reference shape for a lifecycle: the machine owns states, declaredcreateEffectLogicactors own side effects and typed failures,joinplusEffect.orDiehands the outcome back to Effect.packages/core/src/contracts.tsownsinspectFileContract;packages/core/src/inspect-file.tsimplements it and registers the capability.- Effect-backed machines start only under
createEffectActor, nevercreateActor. Only actions and actors declared insetupEffectcontribute to the actor’s requirements; thexstate-effect/no-inline-effectlint rule enforces the inline cases. packages/capability/src:contract.tsdefines shared contracts;implement.tsbinds typed handlers and adds approval requirements;to-command.ts,to-http-api.ts,to-toolkit.ts,to-rpc.ts, andto-code-mode.tsproject implemented capabilities.to-rpc-group.tsbuilds the contract-only client group;to-rpc.tsderives the server group from those same contracts.catalog.ts,sandbox-service.ts, andsandbox-subprocess.tssupport code mode.to-code-mode.tsimports fromto-toolkit.ts, so MCP is a prerequisite for code mode.- Devtools are capabilities:
devtools(capabilities)adds therat_*capabilities, and a dev composition provides the recording layer fromdevtoolsLayer()throughCallWatch(@rat-stack/capability/call-watch).implementreads that ambient reference once per call and surrounds the approval-gated handler, so denied calls are recorded too; the default is a no-op.CallWatchobserves calls across projections without changing their capability lists.aroundHandlersstays as the explicit wrapper over one capability list: drovr uses it forwriteGateto serialize and audit approval-gated writes. Both compose becauseimplementreadsCallWatchinside the handler thataroundHandlerswraps; the gate runs outside the recorder.rat_calldispatches throughinvokerFor, the same path code mode uses. Machines report throughwatchActor(@rat-stack/capability/actor-watch), a no-opActorWatchreference that the recorder overrides;rat-stack-patterns/watch-effect-actorsrequires the call.devtools(capabilities, { runAs })records each call’s identity and letsrat_calltakeas; the composition root passesrunAsPersonfrom@rat-stack/auth/devtools, so devtools never imports auth.rat-stack-boundaries/no-devtools-in-productionkeeps@rat-stack/devtoolsand@rat-stack/auth/devtoolsinapps/*/src/dev/**,apps/cli, and tests, and keeps everything outside an app’sdev/folder from importing it, so a deployed entry cannot reachrat_callor test people.--devtoolsbinds127.0.0.1only. Plan and tool list:.brain/projects/rat-devtools.svx. apps/cli/src/surfaces.tsandapps/cli/src/learn-surfaces.tsare the only places projections are instantiated;learn-surfaces.tsstays devtools-free because the publishedratstackbin imports it.apps/cli/src/command.tsmaps them to subcommands;apps/cli/src/cli.tsis the composition root that providesFileInspectorandNodeServices, andapps/cli/src/ratstack.tsis the composition root for theratstackbin. Both sharelearn-layer.ts.- The README’s Keep or cut section lists what to delete per surface.
Wiki writing
Follow write-a-wiki-page for prose, visuals, code excerpts, and content-build diagnostics.
Web feature blueprint
apps/web/src/features/<name>/ holds a thin route and view. It reads the Model and sends Messages; components never handle transport, retries, or process startup. apps/web/src/client/<name>/ owns the Foldkit program: Model, Messages, update, and named Command.define definitions. Remote state lives in the Model, as Foldkit AsyncData or a foldkit/experimental Query. The capability in packages/core is the shared typed edge, imported by both sides. A cartridge owns durable behavior.
One send follows one path: feature → Message → update → named Command → RpcClient over the contract’s toRpcGroup → shared capability → server-side Durable Object or database cartridge. The server stays authoritative. The Command returns a result Message, and update folds it into the Model.
Worker autonomy ladder
A worker acts at the lowest rung that applies. Climb only when the rung below does not cover the step.
- 0 — Do it: Fix your own red CI, failing hook, or stale git snapshot. Arm a wait watch. Read, edit, and test inside your write scope.
- 1 — Do it and report: Commit on your branch. Tighten a test. Edit docs or the Brain. Report what changed and which checks ran.
- 2 — Ask the desk or owner: A choice that changes scope, a guarantee, or the design, where the repo does not decide.
- 3 — Owner sign-off: Everything in “Needs owner sign-off” below.
- Never — Do not do this:
--no-verifyor any hook bypass. Merging or deploying as a worker. Force-pushing. Secrets or customer data in a PR.
At rung 2 or 3, ask one question with a recommendation. Keep doing independent work while you wait. Red CI is work, not a question: reproduce it, fix it, and push.
Never end a turn “waiting” without a wake. Procedure: skills/rat-stack-mode/principles/own-your-wait.md.
Boundaries and sign-off
A child project replaces this section on day one with its own boundaries. This section names changes agents may make directly and changes that need owner approval.
- Safe by default: adding a capability in
packages/coreand wiring it intocapabilities27; new or tightened tests; a targeted diagnostic override with a written reason; README, AGENTS.md, and.brain/edits; tightening a lint rule; removing a surface by following Keep or cut; any change underapps/infra, andpnpm infra:deployoncepnpm infra:planshows only the intended diff and the gate passes, followed bypnpm site:smoke. - Needs owner sign-off: adding or changing a dependency version (pins are exact and CI installs cold); any edit to
oxlint.config.ts, the diagnostics map intsconfig.base.json,lefthook.yml, orscripts/vcs-command-policy.jsthat loosens the fence;pnpm infra:destroy, and a deploy whose plan replaces or deletes a resource; widening sandbox permissions or adding a runtime that reaches the network; exposingserveormcpbeyond localhost; vendoring a package as afile:tarball; deleting.agent_sources/orvendor/.
Last changed 2026-11-02 in 7ff40bf. Source on GitHub. Change log.
Linked from
- A clean playground. rat-stack does real work with packages that stay easy to add and remove.
- add-a-lifecycle-machine. Learn how XState owns a lifecycle while Effect owns its work, errors, and services.
- add-a-store. Add persistence through a service named for an application job, with validation, vendor Layers, migrations, bounded reads, and shared backend tests.
- Build and run rat-stack (README.md). What is in the repo, how the example works, and how to run it.
- CAFE names the Cloudflare, Alchemy, Foldkit and Effect stack. CAFE is a named stack: Cloudflare runs the app, Alchemy declares it, Foldkit draws the UI, and Effect does the typed work.
- Effect + Alchemy peer patterns. Source-grounded patterns from repos on nearby Effect and Alchemy pins.
- Effect 4 study: September 2026. Dated Effect 4 source studies from September 2026; current versions live in pins.md.
- gardener. Keep rat-stack current and clean. Update exact dependency versions, learn from repos on the same versions, and add lint rules before cleanup.
- investigate-a-failure. Find the cause of a crash, wrong output, flaky test, or slow run with evidence, fix it at its source, and leave a notebook a person can learn from. Use when something fails intermittently, when the first fix idea is "raise the timeout" or "swap the version", or when asked "why does this break" or "make this stable".
- keep-or-cut. Learn which pieces depend on each other, then keep only the ones your project needs.
- learn-alchemy. Learn how Alchemy 2 turns an Effect program into a planned Cloudflare deployment.
- learn-rat-stack. Trace a capability through Effect, XState, five projections, the CLI, and Alchemy. Try hosted search and read.
- Purpose and boundaries of rat-stack (VISION.md). What this starter is for and what a useful copy should keep.
- ship. Learn how to ship a change through merge, CI, release, stage deployment, rollout, verification, rollback and flags.
- uncomplect. Find responsibilities that a rat-stack design combines, separate them into capabilities, cartridges, machines, features, and clients, and enforce that separation through types and checks. Use when asked to simplify, review, or replace a design, "what would Rich Hickey do", "optimize for deletion", or "define this error out of existence".