Skip to content

Codebase patterns for agents

High-signal rules for where code belongs and how to keep types and boundaries clean in this monorepo.

Repository shape

PathRole
apps/web/React SPA (Vite), Cloudflare Pages–oriented
apps/worker/Hono API on Cloudflare Workers, Drizzle + Postgres
apps/docs/Documentation site (if present in workspace)
docs/Human and agent-oriented documentation (ADRs, frontend guides)

Shared npm/pnpm workspace rules live at the repo root; prefer workspace dependencies over ad-hoc relative imports across apps/.

Domain separation (frontend)

  • Route ownership is expressed under apps/web/src/domains/ — e.g. shared/, hod/, developer/, employee/, technician/, admin/, auth/.
  • Shared pages (profile, settings, system tools) usually live under domains/shared/pages/ and are wired through the router with permission-aware shells.
  • Role-specific dashboards and flows live under the matching domains/<role>/ tree; avoid importing role-only pages from another role’s tree.
  • Cross-cutting UI: apps/web/src/components/, layouts/, context/.

See also: ../frontend/architecture/domains.md and ../frontend/features/shared-vs-role-pages.md.

Backend (workers)

  • Routes under apps/worker/src/routes/ compose Hono handlers; keep them thin and delegate to services.
  • Services under apps/worker/src/lib/services/ own business logic, DB access (Drizzle), cache, and integration with Durable Objects where applicable.
  • Types for API and domain models belong in apps/worker/src/types/ (and friends), then reused or mirrored on the web side when the client needs them (apps/web/src/types/).
  • IDs — Prefer centralized generators in apps/worker/src/lib/services/id/ (e.g. forMaintenance()) instead of ad-hoc UUIDs when that is the project convention.

Type safety

  • Avoid any for persisted or API-bound data; extend interfaces and Zod schemas where validation exists.
  • When adding a new endpoint, define or extend response types and use them in the client service layer (apps/web/src/lib/services/).
  • i18n — Prefer typed or namespaced keys (see apps/web/src/i18n/keys.ts and locale JSON) over raw strings in UI.
  • SEO (web) — The SPA sets meta via apps/web/src/components/seo/RouteSeo.tsx using apps/web/src/lib/seo/routeRegistry.ts.
    • For a new page/route: add a route pattern + routeId in routeRegistry.ts, then add apps/web/locales/en.json strings under seo.routes.<routeId>.{title,description}.
    • If the title/description depends on runtime/dynamic data (e.g. user name): render apps/web/src/components/seo/PageSeo.tsx in that page and add seo.routes.<routeId>.dynamicTitle / dynamicDescription keys (or whatever keys the page uses).
    • After updating SEO strings: run pnpm locales:sync, then pnpm locales:validate:quality (parity / English-identical report).

Internal services and logging

  • Use the real public methods on InternalLoggerService (and related services) — e.g. logSecurityEvent, suspicious, logMaintenance — with the correct argument order and string requestId parameters. Do not call private cache methods from feature services unless that is the established pattern in that file.

Configuration and env

  • Workers use Wrangler and typed Env bindings; new bindings belong in wrangler config and Env types.
  • Do not commit secrets; use .dev.vars / platform secrets as documented in project guides.

Documentation

  • Substantial architectural choices should reference or add an ADR under docs/architecture/decisions/.
  • User-facing feature documentation may go under docs/frontend/ or product docs as appropriate.