Files
convex-monorepo-payload/AGENTS.md
T
Gabriel Brown 7d1884be41
Build and Deploy Next App / quality (push) Successful in 1m24s
Build and Deploy Next App / convex-deploy (push) Successful in 1m58s
Build and Deploy Next App / build-next (push) Successful in 5m32s
Deploy from CI with Infisical and ship Payload changes as migrations
CI reads Infisical staging as the read-only convexmonorepo-ci machine identity, deploys the Convex functions, builds and pushes the image, and asks the VPS to redeploy. Payload schema changes now ship as migrations that production runs on startup; the baseline matches the production schema and is recorded as applied there. The local stack moves to its own ports and syncs the Convex runtime env, and the scripts pick up Infisical account switching, dev-kill and a cache-safe CI install.
2026-10-01 14:42:55 -04:00

5.7 KiB

AGENTS.md

Architecture

  • apps/next: Next.js 16 frontend plus embedded Payload CMS.
  • apps/expo: Expo scaffold; only work here when explicitly requested.
  • packages/backend/convex: self-hosted Convex functions, schema, and auth.
  • packages/ui: shared shadcn-based UI components.
  • tools: shared ESLint, Prettier, Tailwind, TypeScript, and Vitest config.
  • Local development uses host-run apps, local Convex on ports 3240/3241, its own data volume, and Payload Postgres on port 5436. Convex does not use Postgres unless a cloned project explicitly enables the commented option.

Protected and generated files

  • Never edit packages/backend/convex/_generated/** or packages/backend/convex/http.ts.
  • Do not rename apps/next/next.config.js or apps/next/src/proxy.ts.
  • Preserve withPayload(...) and typescript.ignoreBuildErrors in Next config.
  • Do not edit Payload-generated routes, layouts, import maps, or apps/next/payload-types.ts manually.
  • Do not modify Sentry config or tools/tailwind/theme.css unless requested.
  • Generated .cache, .turbo, .local, and environment files are ignored.

Environment rules

  • Local dev and staging come only from Infisical via scripts/with-env; it never falls back to .env*.
  • Run infisical login and infisical init before local development.
  • Machine-generated values belong in .local/<env>.generated.env; never put the generated Convex admin key in shared Infisical.
  • CI reads staging through scripts/ci-export-env.mjs, logged in as the read-only convexmonorepo-ci machine identity (Gitea secrets INFISICAL_CLIENT_ID and INFISICAL_CLIENT_SECRET). scripts/export-env refuses to run in CI.
  • staging holds the production values that CI builds and deploys with.
  • If you are logged in to more than one Infisical account, put INFISICAL_EMAIL=... in scripts/flags/infisical.env (gitignored) and the scripts switch the CLI to that account before exporting.
  • Server code imports validated variables from @/env. Client components and instrumentation-client.ts import from @/publicEnv, which keeps zod out of the browser bundle. Never read process.env elsewhere.
  • Add cache-relevant variables to turbo.json globalEnv.

Code and dependency rules

  • Use const arrow functions instead of function declarations.
  • Add .js extensions to Convex generated-file imports.
  • Put custom frontend routes in apps/next/src/app/(frontend).
  • Extend the Convex users table for application user data; do not add a profile table by default. Authenticate protected functions.
  • Manage shared versions in root catalogs. Never run bun update in a package.
  • Run root bun install and verify with bun lint:ws.
  • Update this guide when architecture or workflows change.

Local stack

bun db:up          # start Payload Postgres, Convex, and dashboard
bun dev:next       # host Next + deploy/watch local Convex functions
bun db:down        # stop and preserve both data volumes
bun db:down:wipe   # remove both volumes, generated admin key, and seed marker
bun db:sync:payload # refresh and apply the local Payload seed snapshot
bun db:sync-env    # re-push Infisical dev values into the local Convex env
bun dev:kill       # stop dev servers that survived a Ctrl-C

Local URLs: app http://localhost:3000, Convex http://localhost:3240, dashboard http://localhost:6794, Payload Postgres localhost:5436. Use INFISICAL_ENV=staging bun dev:next for staging services.

db:sync:payload is an explicit, destructive local operation. It validates a remote staging source and localhost destination, stores the sensitive snapshot only under .local/, and never changes Convex or production data. Normal db:up never connects to staging; it only applies .local/payload-staging.dump when .local/payload-seed-state.env is absent or stale. Do not store seed markers in Payload Postgres; schema push treats unmanaged tables as drift.

Payload schema changes

Payload schema changes ship as migrations in apps/next/src/migrations/. Locally, Payload pushes schema edits straight into the localhost database. When a change is ready, run bun with-env bunx payload migrate:create <name> from apps/next and commit the generated files. Production runs pending migrations on startup (prodMigrations in payload.config.ts). Push only runs against a database on localhost: a push leaves a dev row in payload_migrations, and Payload's startup migration stops on a confirmation prompt when it finds one. For the same reason, a local production build against the local dev database hangs; point it at a database without that row.

Analytics and bundle size

  • Plausible (NEXT_PUBLIC_PLAUSIBLE_SRC, the per-site script URL) loads through a same-origin proxy under /pa, clear of Payload's /api routes. PostHog (NEXT_PUBLIC_POSTHOG_KEY, NEXT_PUBLIC_POSTHOG_HOST) loads after the page is idle through /ingest. Both stay off when their env is unset.
  • Session replay comes from PostHog. Sentry carries errors and traces only.
  • packages/ui/src/index.tsx holds re-exports only (cn and ccn live in utils.ts). With optimizePackageImports in next.config.js, each page loads only the UI modules it imports. Declaring anything in the index breaks that.

Deployment

A push to main runs .gitea/workflows/build-next.yml: the quality gate, then convex deploy to production, then the image build and push, then ssh [email protected] convexmonorepo (the forced-command deploy on the VPS). A push to main is a production deploy.

Validation

Use bun typecheck, never a production build for routine validation. The full gate is SKIP_E2E=1 bun run ci:check; local-stack smoke e2e is bun test:e2e. Pre-commit runs lint-staged serially and pre-push runs the bounded full gate.