# AGENTS.md ## One Rule To Remember "Can we do more with less code?" Proposed implementation should target minimal code surface without sacrificing correctness, performance, and security. If you have an idea to refactor and making the code more reusable/maintainable/production-grade, propose it as an option, not as the default. ## Naming The product name is "anvil", with lowercase "a". Use this consistently in your response, code, comments, etc. ## Repository status This repository is in v1, not blank-slate prototyping. Core v1 flows are implemented, and the repo now has worker-focused Vitest coverage, a narrow queue integration path, and a small Playwright frontend e2e suite. This repository is still under active development, so expect surrounding code, adjacent files, and in-flight changes to move while you work. Manual verification is still expected for volatile paths, especially around sandbox/container behavior and other platform-sensitive execution flows. ## Working rules - Multiple agents may be implementing changes in parallel. - Prefer using multiple agents when the work can be split into independent parallel tasks. Plan work to be parallelizable. - If a task involves comprehending or modifying `src/worker`, always use `GPT-5.5` with `xhigh` reasoning effort or `Claude Opus 4.7` with `max` reasoning effort. - Keep edits scoped to the task at hand. - Prefer focused, minimal changes over broad refactors or unrelated cleanup. - Prefer clarity over cleverness. Favor explicit names and intermediate types over dense inline expressions. Small duplication is acceptable when it materially improves readability and maintenance. - Avoid touching unrelated files unless a change is required for correctness. - Do not assume automated tests are complete or authoritative for all behavior. - When validation is needed, prefer the existing worker harness for worker/backend changes, use the frontend e2e suite for client flows, then leave volatile-path verification for the user. - Keep shared client/server validation aligned when changing invite acceptance or password policy. `src/contracts/auth.ts` is the shared source of truth for invite/password constants. - `src/contracts/` is the shared client/server API surface: types and codecs that cross the HTTP boundary (API request/response shapes, branded IDs, public enums like `RunStatus`, `DispatchMode`). If the frontend never imports it, it does not belong here. - `src/worker/contracts/` holds worker-internal coordination types: DO RPC inputs/results, execution state machines (`ProjectRunStatus`, `D1SyncStatus`), dispatch/queue messages, trusted serialization helpers, and repo-config validation. These cross the Workers↔DO boundary but never reach the client. - Frontend code in `src/client/` must use proper accessibility (a11y) practices: semantic HTML elements, ARIA attributes where needed, keyboard navigability, sufficient color contrast, and screen-reader-friendly labels. ## User rules - Do not start the dev server as part of routine agent work. Assume I already have it running in another terminal unless proven otherwise. - Do not modify `drizzle/` files directly: they are generated by `npm run db:generate`. Modify schema in `src/worker/db/` first, then generate the migration. - Prioritize lookup on `cloudflare-docs` mcp for up-to-date Cloudflare developer docs (if available). Fallback to searching the web if you cannot find the relevant information on this mcp - For confirming the function's exported signature, it is often faster to read the module's `.d.ts` in `node_modules/` instead of searching the web. For documented/published behaviors, searching the web/mcp on the latest information/guideline is appropriate. ### `chrome-devtools` mcp: - Restrict the use of `chrome-devtools` to browse the local dev server. - Before trying to use this mcp tool (if available), confirm with `curl http://127.0.0.1:9222/json/version` that Chrome is running on _Windows_, not in WSL2. - Browser the local dev server for validation when you are working on frontend code in `src/client`, or when I asked/implied you to check. If the intention is unclear, ask for confirmation/clarification. ### No transitive boundary crossings - Keep cross-runtime boundaries to a single hop from the caller. Workers -> DO and Workers -> D1 are allowed, but do not chain boundaries transitively. In particular, Workers -> DO -> D1 is not allowed. - DO -> DO is allowed (e.g. ProjectDO -> RunDO) if and only if it is single hop (not part of a transitive chain), and not reentrant. - See "Transaction/Mutation/Saga" below ### Transaction/Mutation/Saga: - Retain strict mutation boundaries between D1, Workers, and Durable Object: Durable Objects can hold a transaction against its object (e.g. Project, Run, etc). Do not attempt to propose a design that resembles a distributed transaction (e.g. Workers reads a row, decide on resolutions, then invoke Durable Object RPC to mutate) - If there needs to be resolution, Durable Object can resolve that conflict in a transaction and return a tagged union for Workers to decide - If there needs to be reconciliation with D1, do not query/modify D1 directly in Durable Object RPC- use `alarm()` for reconciliation. A similar pattern already exists. - If you need to reach for `blockConcurrencyWhile` in an Durable Object RPC, your design is probably wrong. Try again. - In general: Workers stays stateless, no mutations; Durable Objects are stateful, transactional. ### Error handling - Durable Objects do not throw `HttpError`. Use tagged union between Workers and Durable Objects to communicate outcome. - Reserve `throw` in Durable Object for FUBAR ## Testing - Available commands: `npm test`, `npm run test:integration:queue`, `npm run test:integration:workflows`, `npm run test:e2e`, `npx tsc -p tests/tsconfig.json --noEmit` - `tests/worker/` remains the fast worker-first harness for routes, D1/DO invariants, dispatch edge cases, and shared worker utilities - For `tests/worker/` setup that is not explicitly asserting alarm delivery or alarm persistence, prefer the `tests/helpers/project-do.ts` `*WithoutAlarm` helpers so state-invariant tests do not race background reconciliation. - Do not persist real alarms in tests unless the test is specifically about alarm scheduling or alarm delivery; record or stub the write instead when the assertion only cares that an alarm would be armed. - For route and ownership/atomicity tests that do not care about workflow dispatch, prefer projects with `dispatchMode: "queue"` to avoid background Workflow work leaking into teardown. - `tests/e2e/` is the live frontend Playwright suite for auth, route guards, profile flows, and basic project CRUD against a locally started app - `tests/integration/queue-runner-happy-path.test.ts` is the narrow end-to-end queue integration check: it applies local D1 migrations, seeds a bootstrap invite, starts the local app, accepts the invite, logs in, creates a project, triggers queued runs, verifies a passing run's steps/logs, and checks that back-to-back runs serialize correctly - `tests/integration/workflows-runner-happy-path.test.ts` is the narrow end-to-end Workflow-backed integration check: it applies local D1 migrations, seeds a bootstrap invite, starts the local app, accepts the invite, logs in, creates a Workflow-backed project, triggers a run, and verifies a passing run's steps/logs - Treat live run-execution coverage as expensive. Do not duplicate it across `tests/integration/queue-runner-happy-path.test.ts` and Playwright run-trigger flows. - Prefer the queue integration test for queue-backed execution and the workflows integration test for Workflow-backed execution. Keep Playwright focused on browser/UI coverage unless there is a clear local-development reason to exercise real run execution there too. - If the queue integration test, workflows integration test, and browser e2e coverage that triggers real runs need to be run on a local dev machine, run them sequentially, not in parallel. - Cloudflare Workers Vitest still runs with containers disabled; container-related workerd exceptions and sourcemap noise can appear in `tests/worker/` output without failing the file - Use the worker harness for fast feedback, use the Playwright suite for frontend validation, and treat the integration tests as targeted coverage for the local run path rather than exhaustive sandbox validation - Watch out for queue/alarm side effects, long-running integration or e2e timeouts, and preserved temp-state logs when run-dispatch or browser coverage fails ## Quick references - Before adding/changing behavior: `reference/anvil-spec.md`, `reference/anvil-spec-resolutions.md`, `reference/spec-drift.md`, `reference/frontend-spec.md` - Frontend UI work: `src/client/main.tsx`, `src/client/app.tsx`, `src/client/pages/`, `src/client/components/`, `src/client/styles/` - Client-side auth and API calls: `src/client/auth/`, `src/client/lib/`, `src/client/hooks/`, `src/client/toast/` - Shared client/server contracts: `src/contracts/`, `src/lib/` - Worker-internal contracts (DO RPC, dispatch coordination, execution state): `src/worker/contracts/` - API and routing work: `src/worker/index.ts`, `src/worker/router.ts`, `src/worker/http.ts`, `src/worker/hono.ts`, `src/worker/api/public/`, `src/worker/api/private/` - Auth and session flow: `src/worker/auth/`, `src/worker/db/d1/sessions.ts`, `scripts/seed-bootstrap-invite.ts`, `src/client/auth/` - Security headers and CSP: `src/worker/router.ts`, `src/worker/security/`, `index.html` The current CSP allows Google Fonts via `fonts.googleapis.com` and `fonts.gstatic.com`. - Data model and persistence: `src/worker/db/d1/`, `src/worker/db/durable/`, `drizzle/d1/`, `drizzle/project-do/`, `drizzle/run-do/` - Run orchestration and sandboxing: `src/worker/durable/project-do/`, `src/worker/durable/run-do/`, `src/worker/dispatch/queue/`, `src/worker/dispatch/workflows/`, `src/worker/dispatch/shared/`, `src/worker/sandbox/` - Validation and codecs: `reference/en-garde.README.md`, `src/worker/validation.ts`, `src/lib/codec-errors.ts` - Testing and tooling: `tests/worker/`, `tests/e2e/`, `tests/integration/`, `tests/helpers/`, `package.json`, `wrangler.jsonc`, `vite.config.ts`, `vitest.config.ts`, `vitest.integration.config.ts`, `tsconfig.json`, `tsconfig.scripts.json`, `tests/tsconfig.json`, `worker-configuration.d.ts` ## Cloudflare platform runtime invariants quick references: - "Alarms" under Durable Objects - "Rules of Durable Objects" - "Durable Object Stub" - "Error handling" under Durable Objects - "SQLite-backed Durable Object Storage" - specifically `.transaction` section - "Remote-procedure call (RPC)" under Workers - "application-defined classes (or objects with custom prototypes) cannot be passed over RPC" - Exception: `util-en-garde` defined types are plain objects. Those can be safely passed over RPC.