Effect + Alchemy peer patterns for rat-stack
These peer patterns guide the web client, Alchemy resources, database, Better Auth16, Worker tests, and boundary enforcement. The peer roster lists the candidates and links each studied repo here. See the layer-startups decision for the choices they inform.
Pin reality
The original study base, 1c2a738, pinned Effect 4.0.0-rc.117, Alchemy 2.0.0-beta.79, and XState 6.0.0-alpha.59. Those are historical versions. packages/core/test/docs-pins.test.ts guards the version text in AGENTS.md and README.md. The Effect reference note records the versions on its 2026-09-18 study date.
At that study date, only alchemy-run/alchemy at 9802260db8f8b5a083c4b85eb7ea31ae1ff8f176 matched both Effect and Alchemy lines: its package is beta.79 and its workspace requires Effect rc.117. Maple is one Effect RC behind at rc.116 / Alchemy beta.79; T3Code is rc.115 / beta.79. OpenSEO is Effect beta.93 / Alchemy beta.61, SolStatus beta.107 / beta.72, Smithers rc.115 / beta.76, Pivot beta.103 / beta.65, and Elianiva beta.101 / beta.64. The roster includes repos on shared stack lines with different exact pins. Use older source to study shapes; check APIs before copying code.
The Oct 2 refresh found stable Effect 4 in Alchemy, effect-cf, and vitesakuga. Alchemy also moved to beta.80.
The Oct 9 refresh checks all 29 existing peers and adds 21 verified public projects. The roster records their commits, manifests, and tiers. Alchemy moves to beta.81. Several peers use stable Effect 4.0.1 or 4.0.2. rat-stack keeps Effect 4.0.0 to satisfy Foldkit16’s exact peers. Patterns below keep their original source snapshots and study dates.
Patterns
Browser RPC behind a private Worker binding — adopt
Source: Alchemy example README, RPC schema, browser client, proxy route, Stack.
One RpcGroup declares the wire contract; the backend serves it and AtomRpc.Service turns it into module-scope20 query and mutation atoms for React. The browser calls a same-origin TanStack /rpc route, which forwards to a private BACKEND service binding23; the backend has no public Workers URL.
Adopt this for the web bin. Keep capability schemas in packages/core, the toRpc projection in packages/capability, and UI atoms in the web app.
One schema-derived contract carries payloads and typed failures36 across the wire. The binding23 keeps the backend private.
All browser operations go through toRpc and the same /rpc transport. Keep hand-written domain routes out.
PlanetScale resources and migrations — adapt
Source: T3Code relay database at f5ef0ddb90a8c36584e181b1913e7b8a5df30ffc (Effect rc.115, Alchemy beta.79).
T3Code makes Drizzle.Schema the migration source, then provisions a PlanetScale database or stage branch, a runtime role, and a Hyperdrive connection from that role’s origin. Production retains the shared database; non-production stages use branches. This is the closest inspected resource graph to rat-stack’s planned PlanetScale Postgres Layer.
Adapt this for the database cartridge36. The vendor Layer should own its schema, migration path, role, and Hyperdrive binding.
Use Alchemy Outputs and typed bindings for connection strings. Keep provider-specific Drizzle details behind the database service tag.
Choose D1 or PlanetScale once with Layer.provide. Keep T3Code’s stage-mode branch out of domain code.
Effect-native Drizzle and local Postgres tests — adapt
Source: Maple’s packages/db/src/client.ts, pglite.ts, and migrate.ts at e7bc2d97bea87c99c8b9b33f377956e923d04373 (Effect rc.116, Alchemy beta.79).
Maple builds Drizzle over @effect/sql-pg, opens the client inside an Effect Scope, and uses PGlite plus the same bundled migrations for local and test paths. Its exported MapleDb remains Postgres-specific. Use the driver implementation and test technique behind a provider-neutral database contract.
Adapt this for the database cartridge. Keep the public repository-shaped database service small and let each vendor Layer implement it. Scope clients to one invocation. Use a real embedded database for migrations and query tests. Keep Drizzle’s driver type behind the cross-provider API. Share migration fixtures and the service contract. D1 and Postgres keep their own schemas and implementations, as Joel decided.
Better Auth’s database seam — adopt
Source: Alchemy’s Database.ts, CloudflareD1.ts, CloudflareHyperdrive.ts, BetterAuth.ts, and example Worker.
@alchemy.run/better-auth@2.0.0-beta.79 makes Database a service supplied by provider-specific Layers, and BetterAuth consumes that service. D1 uses a runtime Worker binding and a separate deploy-time migration connection; Hyperdrive accepts the origin URL for migrations because its runtime connection is minted per invocation, and the integration scopes auth/database work to that execution.
Adopt the official Better Auth integration. It is a separately published Alchemy package with provider subpaths, so rat-stack can use it without forking an adapter.
Its API and migration failures are typed. Database clients are scoped, and the caller supplies one database Layer.
Use one BetterAuth service with the database cartridge’s selected Layer. Keep runtime D1/Postgres switching out of auth code.
Runtime database-provider switching — reject
Source: OpenSEO’s auth.ts, provider.ts, pg/client.ts, and alchemy.run.ts at 0ffff93101043aad7600a3b6a499a0cd2887ef49 (Effect beta.93, Alchemy beta.61).
OpenSEO constructs Better Auth directly and selects either a D1 Drizzle adapter or a Postgres adapter by reading DATABASE_PROVIDER at runtime. Its Postgres client is created inside an AsyncLocalStorage request scope and reaches the database through Hyperdrive, but the auth and schema code still branch on provider.
Reject the selector and retain the request-scope invariant. The branch ties auth to both database implementations; either database Layer should plug into one contract.
An environment string chooses the driver at runtime, outside the Schema/Layer boundary.
Keep the per-request Hyperdrive lifetime. Move provider choice to one Layer.provide at composition.
Fast Worker and Durable Object tests — adopt
Source: Maple’s worker-bridge.test.ts, ChatSessionObject.test.ts, and fake-do-state.ts, same Maple commit as above.
The bridge test invokes Alchemy’s Cloudflare.Workers.makeRequestHandler and checks the request scope, Worker environment, and response. Durable Object tests give the class a narrow fake state backed by real node:sqlite, including SQL storage, alarms, and waitUntil; they exercise object behavior without pretending to be a full workerd runtime.
Adopt this for fast tests alongside real-runtime tests. Keep fixtures beside the Worker or database cartridge that owns them. Real SQL catches query and storage drift. The narrow fake fails when tests ask for an unsupported state operation. Use the Alchemy bridge for deterministic Worker tests. Keep a separate integration test for real service bindings and deployed behavior.
D1 fixture backed by SQLite — adapt
Source: Smithers’ worker/test/d1.ts and action-cache.test.ts at 38846c64dd322d61e67efafadac1fbd9d34df64f (Effect rc.115, Alchemy beta.76).
The test adapter runs shipped SQL migrations against node:sqlite, implements only the D1 calls the code uses, and throws on every unsupported method. That makes it a useful D1 unit-test boundary, but it does not prove Cloudflare’s D1 binding or runtime behavior.
Adapt this for D1 cartridge tests. Expose a test Layer or helper from the D1 package. Keep it local to D1 rather than creating a repo-wide mock database. Unsupported calls fail loudly. Use this for local SQL behavior and the Alchemy integration gate for binding behavior.
Deployed Alchemy integration tests — adopt as a separate gate
Source: Alchemy’s test/integ.test.ts at 9802260db8f8b5a083c4b85eb7ea31ae1ff8f176.
The example’s alchemy/Test/Bun harness deploys the Stack before tests, retries readiness, sends CRUD calls through the /rpc proxy into the database, verifies a tagged failure crosses the wire, and destroys the Stack afterward. This is the closest inspected example of the real-resource integration path called for in VISION.md, but its Bun runner is not a fit for rat-stack’s pnpm test stack.
Adapt this as a separate credentialed integration gate. Keep fast unit tests uncredentialed.
The integration suite belongs to the Stack and can be removed with it. It proves the full binding, wire, and database path.
Port the deploy/test/destroy lifecycle into a pnpm-compatible harness. Keep alchemy/Test/Bun out.
Boundary tests and agent guidance — adapt
Source: Maple’s vendor-isolation.test.ts and CLAUDE.md, same Maple commit.
The test scans explicitly named generic roots and files to keep chat-vendor names inside connector directories, and checks that guarded roots are non-empty and guarded files still exist. The guidance makes app, product-package, and vendor-neutral-library ownership explicit; it also says services capture implementation dependencies and keep request context invocation-scoped.
Adapt the proof. Maple’s lexical vendor-name scanner is product-specific; rat-stack needs checks for its own package ownership and adapter isolation. Encode the capability-only surface23 rule in a test or type-aware lint rule. Check that guarded paths exist and contain files. Extend the current capability fence39 rather than adding a second generic source-scanning framework.
Scoped actor inspection and generated model commands — adapt
Source: beep-effect at a7644af785843bb42546109272970f2f7a82b1d7: catalog, driver manifest, inspector service, test bridge.
Checked Oct 2. Its catalog selects XState alpha.63 and bridge alpha.5. Effect comes from a pkg.pr.new build at b5a2d4c1d62c9620a68d72b7f20248c69ef7663b. That source manifest declares 4.0.0; the installed build’s semver was not verified. It shares a stack line; an exact release match remains unverified.
The inspector has a disabled production Layer. Its enabled Layer owns the inspector, and attach acquires an actor subscription whose finalizer unsubscribes. The test bridge derives event arbitraries from schemas and projects a snapshot’s value and context for model comparisons. Its actor SUT opens a scope and closes it on disposal.
Adapt the lifetime checks. Keep watchActor and ActorWatch in devtools7 as our single inspection path. A second Stately service would bypass that fence. The helper runs tests through ManagedRuntime, which our test fence forbids. Use it.effect.prop and generated command sequences instead of copying its runtime bridge. No production code was adopted.
Verify Foldkit’s exact Effect peers: adopt
Source: Hazel at 2504baa1ab85d7966152a615a08dc0d4af4c1f69: catalogs, Foldkit client, lockfile.
The upstream manifests at 49dc36ef672d1caee23f153f88c09d98c8241670 confirm the boundary: Foldkit and Vite plugin.
Checked Oct 9. Does the shared catalog satisfy Foldkit’s exact Effect peers?
- The catalog selects Effect 4.0.1 and Alchemy beta.80.
- The client selects Foldkit 0.167.0 and uses that Effect catalog.
- The lockfile records Foldkit’s exact Effect 4.0.0 peer.
- The Vite plugin also requires Effect 4.0.0 and depends on platform-node 4.0.0.
Adopt the manifest and lockfile check. Keep Effect 4.0.0 until Foldkit publishes compatible peers and platform dependencies. Do not copy the newer Effect catalog. This study changes no runtime adapter or UI code.
Template lifetime boundaries: adopt
Source: architecture and manifest at 04851db8649501bfaf8d2712429074ba0290a10f.
The source assigns each resource to an application, request, tenant, session, or execution. It acquires request capabilities at the request boundary.
rat-stack already adapts this guidance in cache completed values7, UI tests, and opt-in tracing. PROVENANCE.md records the source and license confirmation.
Keep those lifetime boundaries. The template uses Effect 4.0.0 and Alchemy beta.81, with Foldkit 0.165.0. Its package-manager defaults do not replace pnpm here. This refresh records the existing adoption. It copies no new implementation.
Other screened projects
jonbeckman/solstatus at 83b3e2f4400aa7a2f2f4b738334e0cf91dbf15f5 has Alchemy Cloudflare.Website.Vite, D1, and direct Better Auth, but pins Effect beta.107 / Alchemy beta.72; its runtime is too old to copy. elianiva/elianiva.com at 5799fcb6b5ddc48e1df0223f23a9a15da8d097ae also has a Website.Vite Stack, but uses Effect beta.101 / Alchemy beta.64 and adds no stronger pattern than the current Alchemy example. 13kparkin/Pivot at 1887824d1f96ea6b601fffbc283b836967d32a5c shows PlanetScale/Hyperdrive but uses Effect beta.103 / Alchemy beta.65; T3Code’s newer PlanetScale graph supersedes it for this decision.
Symbol-screened without a relevant client/resource pattern: Dhravya/radish, zonko-ai/harbor-sdk, dmmulroy/overseer, instructa/planr, and statelyai/agent. cameronapak/dotflowy has Better Auth but no Alchemy integration and uses Effect rc.115 / XState alpha.12, so it is not a useful match for this lane. The earlier Effect reference note already covers the XState bridge; this note does not repeat it.
Recommendations from the September study
These recommendations record the original study. The current web bin uses Foldkit; current pins come from manifests.
- TanStack web bin: generate the browser client from capability Schemas as one
RpcGroup, create module-scopeAtomRpcatoms, and forward same-origin/rpcto a privateRpcWorkerbinding. - Better Auth: use
@alchemy.run/better-auth@2.0.0-beta.79; provide the database cartridge’s Layer and keep auth API/migration failures typed. - Database cartridge: expose one repository-shaped service tag; let D1 and PlanetScale/Hyperdrive Layers own schemas, migrations, and bindings; choose with one
Layer.provide. - Worker tests: add
makeRequestHandlerand SQLite-backed DO/D1 fixtures for fast tests, plus one credentialed Alchemy deploy/test/destroy gate using a pnpm-compatible harness. - Capability fence: test or lint that every surface, including
toRpc, originates from the capability registry21; make the test prove its guarded paths exist and contain files.
Last changed 2026-10-09 in 3bab7b3. Source on GitHub. Change log.
Linked from
- find-peers. Refresh public peers, exact stack versions, and usefulness tiers every week or two and after each shared dependency update. Study source before adopting patterns.
Record adoptions and rejections, with the reason, in .brain/resources/same-version-repos.svx, and link that entry from the peer’s Studied column.¶