Skip to content

Latest commit

 

History

History
109 lines (84 loc) · 10.7 KB

File metadata and controls

109 lines (84 loc) · 10.7 KB

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.4 with xhigh reasoning effort or Claude Opus 4.6 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.