Imported from Marcus100/grenmet (
AGENTS.md). Install upstream withnpx skills add Marcus100/grenmet. Copyright stays with the author.
AGENTS.md
Canonical instructions for every coding agent in this monorepo (Codex, Claude
Code, and others). AGENTS.md files are the default instruction files for all
agents; do not create or maintain CLAUDE.md files.
Project in one paragraph: Barrels Grenada is the software company. Grenada
Airports Authority (GAA) is a client organisation; Grenada Meteorological
Service (GMS) is its meteorological department. Neither is a Barrels product.
This pnpm/Turborepo monorepo holds Next.js 16 / React 19 web apps, a FastAPI
(Python 3.14) modular-monolith API, shared packages, and GMS operational
tooling. Start with docs/technical-overview.md.
Instruction map — read the nested file before editing its directory
Codex only loads AGENTS.md files from the repo root down to its start
directory, and Claude only loads nested files when it reads inside that
directory. Before editing under a path below, open its AGENTS.md.
| Path | Covers |
|---|---|
apps/api/fastapi/AGENTS.md |
FastAPI conventions, testing, OpenAPI contract |
apps/api/fastapi/src/<domain>/AGENTS.md |
Per-domain ownership, invariants, tests (auth, hr, cap, wxproducts, wxwatch, audit, notifications, storage, billing, worker, eregister, janitorial, transport, baseline) |
apps/web/<app>/AGENTS.md |
auth, cms, docs, elections, events, gaa-admin, gms, mbia, signal |
apps/web/barrels/AGENTS.md |
Static Barrels Grenada company homepage |
packages/<pkg>/AGENTS.md |
api-client, auth, email-templates, gms, theme, ui |
docs/playbooks/full-stack-feature.md |
End-to-end: model → migration → route → OpenAPI → client → UI → tests |
Commands
pnpm install # dependencies
pnpm start # Docker services (Postgres, Redis, FastAPI, worker) — HOST ONLY
pnpm dev:web:<app> # auth:3000 gaa-admin:3001 docs:3002 gms:3003 signal:3004 mbia:3005 cms:3006 elections:3007 events:3009 — HOST ONLY
pnpm fix:changed # Biome/Ultracite fix on this session's changed files
pnpm type-check # TypeScript across all packages
pnpm fix # repo-wide fix — only when deliberate (e.g. dependency bump)
turbo run test --filter=@barrelsgd/<package> # one package's tests
pnpm vitest run src/path/to/file.test.ts # one test file (from the app dir)
pnpm generate:api-client # Kubb client from apps/api/fastapi/openapi.json
pnpm check:drift # client/openapi drift (fails CI)
pnpm guardrails:staged # FastAPI contract pairing check on staged files
pnpm docs:check-links && pnpm docs:check-portfolio && pnpm test:docs # docs gates
pnpm design-system:check # token gate
FastAPI (from apps/api/fastapi; host stack uses docker compose exec api …):
uv run --frozen --package fast-back pytest [tests/<domain>/] # tests
uv run --frozen --package fast-back alembic upgrade head # main DB migrations
uv run --frozen --package fast-back alembic -c src/<domain>/alembic.ini upgrade head # separate-DB domains
./scripts/lint.sh # Ruff + format check + mypy
./scripts/format.sh # Ruff fix + format
PROJECT_NAME='Grenmet API' uv run --frozen --package fast-back python -c "from src.main import app; import json; json.dump(app.openapi(), open('openapi.json', 'w'), indent=2)" # regen openapi.json
Inside the agent dev container there is no docker CLI and Compose hostnames
(db, grenmet-postgres) don't resolve. Point tests at the host stack:
POSTGRES_SERVER=host.docker.internal REDIS_URL=redis://host.docker.internal:6379/0 uv run --frozen --package fast-back pytest
(full variable list in apps/api/fastapi/AGENTS.md → Testing).
Behavioral Tiers
Always (no confirmation needed)
- Stage, commit, push and open PRs for authorized work after required formatting, types, affected tests and blast-radius checks pass; review the diff, preserve unrelated changes and never bypass hooks or force-push.
- Run
pnpm fix:changedthenpnpm type-checkbefore marking any task done. Repo-widepnpm fixreformats unrelated in-progress files and can bust turbo's cache, surfacing pre-existing issues as if new - Treat GAA as the client organisation and GMS as its meteorological department; never describe either as a Barrels product
- Put backend logic in Python (FastAPI). The only TypeScript backend is the Payload CMS (
apps/web/cms). Next.js route handlers may only validate, proxy to FastAPI, or render (e.g. email HTML); never add database access, business rules, or delivery there - Use Biome/Ultracite through
pnpm fixfor linting and formatting; never invoke Prettier or ESLint - Before marking a task done, grep every importer/callsite of changed symbols and confirm the change is complete across all affected layers — see Blast-Radius Gate
- Follow existing patterns in the codebase before proposing new ones
- Include tests with every new feature or significant logic change
- Explain reasoning before proposing any new pattern, library, or abstraction
Ask First (stop before proceeding)
- Work outside the task or domain the user has authorized
- Adding any npm package not in
pnpm-workspace.yamlcatalog, or any Python dependency - Modifying
turbo.json,biome.jsonc, or anytsconfig*.json - Destructive schema/data changes, breaking API changes, or widening public access or permissions
- Cross-project architecture changes or business-policy decisions the repository and session do not resolve
Existing session authorization counts; do not request it again. Within an assigned task, proceed with related files, tests, documentation, generated clients, shared package changes, compatible API changes, and additive migrations; apply the Blast-Radius Gate and explain material tradeoffs.
Never
gh pr mergeor any deploy command without explicit user authorization.- Destructive Git operations remain blocked by
.claude/hooks/block-dangerous-git.mjs. - Write to
.env.*or.env.localfiles — blocked by.claude/hooks/protect-files.mjs - Manually edit
packages/api-client/src/gen/— blocked by the same hook - Implement a review-only or planning-only request before the user authorizes implementation
Behavioral Rules
Scope Gate
Treat the requested outcome as scope: related source, tests, contracts, generated artifacts, and documentation are authorized even when filenames are not listed. Respect explicit file limits; ask before expanding into unrelated work.
Host/Container Boundary
Run pnpm start and pnpm dev:web:* on the host, never inside the
devcontainer; use the devcontainer for editing, agents, linting,
type-checking, and tests.
Parallel Agents
Several agents often run at once: give each its own git worktree and branch
(Claude: claude --worktree <topic>, which creates .claude/worktrees/<topic>
from the current dev HEAD; Codex: its worktree mode). Run pnpm install in a
new worktree. Commit in the worktree, then land on dev with
git fetch && git rebase origin/dev, pnpm fix:changed, git push origin HEAD:dev;
if the push is rejected, rebase and retry — never force-push. Never hand-merge
openapi.json or packages/api-client/src/gen/: take either side, regenerate
(openapi command above → pnpm generate:api-client → pnpm check:drift).
Dev servers stay on the host in the main checkout.
Communication
Lead with the answer or the next step in plain language; keep responses short and offer deeper detail only when asked. When teaching, go one concept at a time with a hands-on command — never a comprehensive architecture dump.
Verify Environment Before Theorizing
Before acting on any setup/diagnosis theory, confirm the environment with a cheap check (host vs devcontainer, which Docker daemon, which port/config file) and state the assumption being tested. Never bundle a speculative environment change with a fix.
Blast-Radius Gate
A change is not done when the named file passes pnpm fix + pnpm type-check.
Run pnpm guardrails:staged (FastAPI route/schema changes must be paired with
openapi.json; same check CI runs), then grep for every remaining consumer of
the symbols you touched and verify each affected layer — the script covers one
case, not the general one. Use the api-change skill for FastAPI contract
changes and the gaa-admin-change skill before touching apps/web/gaa-admin
(five formerly separate apps: cap/hr/wxwatch/wxproducts/salesbus).
This gate finds impact; it does not override explicit file limits or authorize unrelated work. Complete affected layers within the authorized task.
| If you change… | Also verify… |
|---|---|
| A FastAPI route or schema | regen openapi.json → pnpm generate:api-client → pnpm check:drift; docs/api/contracts.md; every web consumer of the generated hook/type |
| A SQLAlchemy model | Alembic revision for the right database (main or src/<domain>/alembic.ini); seeds; openapi.json if it surfaces in a schema |
| A permission key | src/auth/permissions.py catalogue (enforced by tests/auth/test_permission_registry.py); UI gating in gaa-admin |
Auth behavior (packages/auth) |
all apps using it + delegating apps (docs, gms via AUTH_API_URL) |
A domain baseline (src/<domain>/migrations/drizzle-history.json) |
never edit it — it verifies adopted production history; new schema work is a new Alembic revision |
| A consolidated admin route | the other folded modules in gaa-admin |
A @barrelsgd/ui primitive |
every app importing it |
Reasoning Gate
Before introducing any new pattern, library, abstraction, or approach: state (1) the problem it solves, (2) why the existing approach is insufficient, (3) the tradeoffs. Routine choices within authorized work can proceed; wait only when an Ask First boundary applies.
Tests Alongside Features
Every new feature, component, server action, or significant logic change includes tests in the same task. If there is no clear test target, explain the verification used and its limits.
Correction Handling
Apply user clarifications to ongoing work immediately. Record durable domain
rules in domain docs and durable agent conventions in the relevant AGENTS.md;
ask only when the lasting rule or its scope is ambiguous.
AGENTS.md Update Protocol
- Behavioral rule → the right tier, or a named rule under Behavioral Rules
- Code convention → Code Conventions; lead with
**Name**, say what to do and not do - CI/CD fact → CI/CD Conventions
- Lookup pointer → Where to Look
- Directory-specific rule → that directory's
AGENTS.mdand add it to the Instruction map - Domain or operational rule → domain docs; add an instruction-file pointer only when useful
- One or two lines per entry; no narrative prose. Keep this file under 20 KB — Codex concatenates root + nested files against a byte budget.
Session Handoff
Claude Code and Codex share one working tree. A SessionStart hook tails
SESSION_LOG.md (main checkout root, gitignored; shared by every worktree) into context — read it before
assuming a task is untouched. After a meaningful chunk of work, append one
entry: timestamp, tool, one-line summary, files touched, next step. Newest at
the bottom. Don't log trivial single-file tweaks.
Tool Usage
- When the user wants to inspect a file, return full contents — not a summary.
- Before investigating a CI or build failure, list the top hypotheses with the fastest falsification command for each; test cheapest first and report after each.
- For multi-file changes, trace impact across types, config, and related files first.
- When delegating to a sub-agent, include the Blast-Radius Gate and the relevant
nested
AGENTS.mdpaths in its brief — it starts cold.
Playbooks and hooks
- Reusable playbooks:
.claude/skills/*/SKILL.mdand.claude/commands/*.md(plain markdown, usable by any agent)..agents/skillsis a symlink to.claude/skills; edit the canonical.claude/skills. Check for a playbook before improvising CI triage, pre-merge, release, API changes, or diagnosis. - Hooks: scripts in
.claude/hooks/, wired in.claude/settings.json(Claude) and.codex/config.toml(Codex). Edit scripts once; both tools pick them up.scripts/guardrails/*.test.mjsself-checks the wiring and this instruction layout.
Code Conventions
Frontend (TypeScript/React) — full style rules in .agents/rules/ultracite.mdc (Ultracite/Biome):
- No
any— useunknownand narrow. Biome enforces this. - No
forwardRef— React 19: passrefas a prop. - No
process.envin app code — use the app's typedsrc/env.ts. Exceptions:next.config.*,instrumentation.ts,sentry.*.config.ts. - Server Components by default —
"use client"only for interactivity or browser hooks. - No React Query for server-fetchable data — fetch in Server Components; React Query is for client-side mutations/polling (gaa-admin).
catalog:for shared deps — never hardcode a version for a catalogued dep.- Path aliases —
@/(maps tosrc/), not deep relative imports. - UI primitives —
@barrelsgd/ui/components/ui/<name>; utils from@barrelsgd/ui/lib/utils. - Class composition — use
cnfrom@barrelsgd/ui/lib/utilsfor conditional utilities and callerclassName; keep customtext-*size/color pairs intact whencnwould merge them. - Generated client — types, fetch clients, hooks, and Zod schemas come from
@barrelsgd/api-client; never hand-write a FastAPI response type. - Sentry everywhere — let unexpected errors throw to the app's
error.tsx/global-error.tsx(both report to Sentry). If you catch an error and show a fallback instead, callreportError(error, "<area>")from the app'ssrc/lib/report-error.ts(it skips expected 4xx via the sharedshouldReportErrorrule). Never import@sentry/nextjsin a shared package — it would resolve an uninitialised copy of the SDK. Onlyareaanddigesttags survive the privacy scrubber; never put user data in Sentry.
Backend (Python/FastAPI) — details in apps/api/fastapi/AGENTS.md:
- Two layers — SQLAlchemy models in
models.py,src.models.BaseModelschemas inschemas.py; never expose ORM models. - Thin routers — logic in
service.py;Annotateddependency aliases; typedAppExceptionsubclasses. - SQL first — filter/join/paginate in SQL; Pydantic at the HTTP seam only.
- Permissions —
require_permission(current_user=…, permission_key=…); every key insrc/auth/permissions.py. - Async I/O — no sync network calls in
async def; background work goes to the ARQ worker.
Other:
- geonetcast runs devcontainer-first — its
gdalpin tracks the devcontainer's libgdal; neveruv sync --package geonetcaston the host.
Claude Code
- Claude Code v2.1.277+ loads
AGENTS.mdnatively in its default Project instructions mode when noCLAUDE.mdorCLAUDE.local.mdexists in the working directory or an ancestor. - Invoke repo playbooks with the Skill tool or
/<name>(for example/api-change,/gaa-admin-change,/pre-merge,/ci-triage,/release,/commit,/ui-check,/design-critique,/tdd,/diagnosing-bugs, and/stack-doctor). - Only spawn sub-agents when the user asks; when doing so, pass the Blast-Radius Gate and relevant nested
AGENTS.mdpaths. - Claude hooks live in
.claude/settings.json;format-changed-file.mjsformats each edited file automatically.
CI/CD Conventions
-
Sentry: owner-approved shared projects for this repo are
grenmet-stagingandgrenmet-production; route only through the matching environment secret, never cross-environment fallback. Preserve existing reporting during migration. -
Telemetry rollout: verify dev → staging → production, with app/environment mappings in
packages/ui/src/lib/service-catalogue.json; missing mappings disable collection, never fall back to another owner’s project. -
Monitoring ownership: manage provider configuration, host probes and backup/restore jobs through reviewed CI/CD; activation and evidence gates are in
docs/operations/analytics-monitoring.md. -
Docker image names in GitHub workflows must be lowercase.
-
Pin all GitHub Actions to SHAs, not tags.
-
After modifying Biome config, verify both
assistandformatteroverride keys — Linux CI formatting can differ from macOS. -
outputFileTracingRootin Next.js config must be top-level, not insideexperimental. -
packages/api-client/src/gen/must stay in sync withapps/api/fastapi/openapi.json— drift fails CI.
Design
- Loop:
docs/design-workflow.md. Token contract:docs/design-system.md. Before building UI, read the lane specdocs/design/<gms|gaa-admin|mbia|signal|elections>.md(DESIGN.md format; drift-tested bypnpm test:docs). - Figma is not linked to this repo. Ignore Figma tools and node URLs; design intent arrives via Claude Design or a supplied screenshot. Never ask for a Figma frame URL.
- Style only with
--gm-*tokens / Tailwind aliases / shadcn semantics — never hardcode color/spacing/radius or add design values to Tailwind config. New or changed--gm-*tokens need approval and land inpackages/gms/src/styles/foundation.css, notpackages/ui. Runpnpm design-system:syncafter editing the canonical block inpackages/ui/src/styles/globals.css. - Brand: navy
#0b132b, blue#2878f5, sky#37a3ef, lime#b9ee63. Kit hues fail AA as small text — use--gm-*-inkfor text under 24px regular / 18.66px bold, icons under ~24px, and fills behind small white text. - Logo:
@barrelsgd/gms/components/logo; never hardcode a path or setwidth/height(useclassName). Retired:--gm-sun, the orange wordmark. - Token commands:
pnpm design-system:check(gate),:audit/:audit:full,:contrast,:sync. Dark mode is class-based; prefer semantic tokens overdark:*in shared primitives; printable "papers" stay light.
Where to Look
| I need to understand… | Read… |
|---|---|
| Portfolio, client programmes, repository ownership | docs/portfolio/ |
| Monorepo structure, auth flow, codebase architecture | docs/technical-overview.md |
| Barrels product, AI/data platform, IP strategy | docs/strategy/ |
| GMS service strategy (not codebase architecture) | docs/architecture.md |
| Architecture decisions | docs/adr/README.md |
| Building a feature end to end | docs/playbooks/full-stack-feature.md |
| Frontend development and testing | docs/web/development.md |
| FastAPI dev workflow and testing | docs/api/development.md |
| API contracts and public endpoints | docs/api/contracts.md |
| Auth package API | packages/auth/README.md |
| Environment variables | docs/env.md |
| Port allocation | docs/ports.md |
| Deployment | docs/deployment.md |
| Release promotion | docs/operations/release-runbook.md |
| Infrastructure, backups, incidents | docs/infrastructure.md |
| Security baseline | docs/security.md |
| Troubleshooting | docs/troubleshooting.md |
| Design system and workflow | docs/design-system.md |
| Data architecture and governance | docs/data-architecture.md |
| GMS programme / SOPs | docs/internal/ |
| HR forms and new form modules | docs/hr/adding-a-form-module.md |
| HR forms, roster, attendance and timesheet alignment | docs/hr/end-to-end-alignment.md |
| Agent configuration (how this layout works) | docs/agent-configuration-guide.md |
| Vendored ops apps (SURFACE, wis2box) | VENDORED.md |
Issue tracking and domain docs
- Issues: GitHub Issues for
Marcus100/grenmetviagh; external PRs are not a triage surface. Seedocs/agents/issue-tracker.md. - Triage labels:
needs-triage,needs-info,ready-for-agent,ready-for-human,wontfix. Seedocs/agents/triage-labels.md. - Domain language: root
CONTEXT.md(created lazily by/domain-modeling) plusdocs/adr/. Seedocs/agents/domain.md.
