Skip to content

Latest commit

 

History

History
164 lines (121 loc) · 32.4 KB

File metadata and controls

164 lines (121 loc) · 32.4 KB

AI Development Guide – Metaboost

Quick reference for AI assistants working on the Metaboost repo (HTTP API + Next.js app).

Authoritative AI rules: .cursor/, .cursorrules. Contributor policy and plans: docs/development/llm/DOCS-DEVELOPMENT-LLM.md and llm-cursor-source.

Stack

  • Node.js 24+ (see .nvmrc)
  • npm workspaces: apps/api, apps/web, apps/web/sidecar
  • TypeScript strict, ESM

Commands

npm install
npm run build
npm run lint
npm run dev:api          # API only (default port 4000)
npm run dev:web          # Next.js only
npm run dev:web-sidecar  # Build sidecar + run web with sidecar

Nix / terminal (agent sandbox)

Node and npm are provided by the repo's Nix flake, not a global install. When running terminal commands (e.g. in Cursor's agent), use the wrapper so the correct environment is available:

  • Wrapper: ./scripts/nix/with-env <command> [args...]
  • Examples: ./scripts/nix/with-env npm run build, ./scripts/nix/with-env npm run dev:api
  • Run from repo root. Full explanation and setup-in-other-repos: docs/CURSOR-NIX-WITH-ENV.md.
  • Agent runs that use the wrapper may need full permissions so Nix can write to its cache.

Structure

  • apps/api/ – Standalone Express HTTP API
  • apps/web/ – Next.js app (fetches runtime config from sidecar when RUNTIME_CONFIG_URL is set; optional npm run validate-env -w @metaboost/web / -w @metaboost/management-web for a Podverse-style env check)
  • apps/web/sidecar/ – Runtime-config sidecar (serves env-derived config for the Next.js app)
  • packages/ui/ – Shared UI: design tokens and mixins in packages/ui/src/styles/ (variables, mixins); component groups (form, layout, modal, navigation, table, feedback, bucket) and hooks (useDeleteModal, useTableFilterState, useAuthValidation) documented in packages/ui/PACKAGES-UI.md. Form controls live under packages/ui/src/components/form/ (see that directory’s PACKAGES-UI-SRC-COMPONENTS-FORM.md).
  • Validation schemas: Joi validation lives in apps/api/src/schemas/ and apps/management-api/src/schemas/. Controllers and routes import from these directories only; no ad-hoc schema definitions in controllers or routes. Shared request/response types may live in packages/helpers-requests or packages/helpers. See each app’s schemas/APPS-API-SRC-SCHEMAS.md and schemas/APPS-MANAGEMENT-API-SRC-SCHEMAS.md for the file layout.

Local env

Secrets (JWT, DB, Valkey, etc.) are auto-generated by make local_env_setup via scripts/env-setup-secrets.sh. Canonical contributor-facing env templates/examples are apps/*/.env.example, apps/*/sidecar/.env.example, and infra/config/env-templates/*.env.example (see docs/development/env/ENV-REFERENCE.md). scripts/local-env/setup.sh generates app and infra/config/local/*.env files from those templates when missing. Optional override files under dev/env-overrides/local/*.env (symlinked from ~/.config/metaboost/local-env-overrides/ via prepare/link) are merged by setup.sh when present — info.env, user-agent.env, mailer.env, auth.env, locale.env, notifications.env (see docs/development/env/LOCAL-ENV-OVERRIDES.md). API_CORS_ORIGINS and MANAGEMENT_API_CORS_ORIGINS should match local web base URLs for local dev. WEB_BASE_URL / WEB_URL stay as local dev defaults in generated app env (not in home override stubs).

  • Prepare: make local_env_prepare — ensures ~/.config/metaboost/local-env-overrides/ exists and creates missing override .env files with keys/defaults seeded from canonical .env.example templates (write-home-override-stubs.rb; never overwrites existing KEY= lines; appends missing keys with defaults); edit for non-default values
  • Link: make local_env_link — symlinks dev/env-overrides/local/*.env to existing files in the home overrides directory
  • Clean: make local_env_clean — removes generated repo env and dev/env-overrides/local/*.env symlinks; does not remove home files under ~/.config/metaboost/local-env-overrides/. Run local_env_link before local_env_setup after a clean if you use home overrides.
  • Setup: make local_env_setup — seed env files from canonical templates/examples, then apply auto-generated secrets and overrides (info, user-agent, mailer, auth, locale) when those override files exist (via repo paths under dev/env-overrides/local/ after link).
  • One-shot: make local_setuplocal_env_setup + local_infra_up

See docs/development/env/LOCAL-ENV-OVERRIDES.md.

Optional .llm/history/ notes

See .llm/LLM.md if your team keeps optional markdown notes under .llm/history/.

Security review

For PRs touching auth, CORS, outbound HTTP, redirects, SQL query builders, or proxy headers, use docs/development/security/SECURITY-REVIEW-CHECKLIST.md. Finding-level traceability lives in docs/development/security/SECURITY-FINDINGS-CLOSURE-MATRIX.md. Run npm run security:check locally (also part of make validate_ci).

Testing

When implementing features or executing plans that touch api or management-api, include integration tests (see api-testing). When they touch web or management-web, include E2E tests (see e2e-page-tests). If an API change affects UI in web or management-web, add or update the relevant E2E specs as well.

AI agents: write tests, do not run them

  • During agent or plan implementation: do not run test, lint, or verification commands (npm run test*, make e2e_*, or npm run lint as verification gates).
  • Do add or update tests when the change requires them (see paragraph above and feature-implementation-testing).
  • After each implementation response: tell the operator which command(s) to run in a fenced bash block — see response-ending-make-verify skill, end-with-targeted-make-report-verify rule, and .cursorrules § Agent/plan work.
  • Final COPY-PASTA step: when completing the last prompt in a plan set, list all cumulative verification commands for the whole set (assume the operator ran all COPY-PASTA prompts without testing until the end). See plan-files-convention and plan-execution-completion-tracking rule.
  • Default E2E verification commands for the operator without E2E_API_GATE_MODE unless API or integration code changed across the set.
  • The maintainer merge checklist below applies to operators and humans, not to in-session agent runs.

Root npm scripts

Script What it runs Services needed
npm run test:unit scripts/ci/run-workspaces.mjs — Vitest in every workspace with a test script except apps/api and apps/management-api (packages + apps/web, etc.) None (pure Node; no DB for unit tier)
npm run test:e2e:api check-test-requirements then Vitest in apps/api + apps/management-api Postgres 5632, Valkey 6579 (make test_deps)
npm run test:e2e:web make e2e_test (Playwright: web + management-web) Full E2E stack (make test_deps + seeds)
npm run test:e2e:web:reports make e2e_test_report (HTML step-screenshot reports) Same as test:e2e:web
npm run test:reports test:unittest:e2e:apitest:e2e:web:reports All tiers
npm test test:unittest:e2e:apitest:e2e:web All tiers (slow — runs locally, not in CI)

Full npm test including Playwright is intentionally slow and meant for developer-local runs. CI skips the full suite; maintainers run make test_deps and npm test targets locally before merge. That describes operator/human verification, not in-session agent runs.

API integration tests

One setup file (apps/api/src/test/setup.ts) provides smart-default env for all tests. Tests that need different env (e.g. signup/mailer) override only those vars at the top of the file and load app/config via dynamic import in beforeAll so overrides apply. The first step is a requirements check: Postgres and Valkey must be reachable at the test ports (defaults 5632, 6579 — distinct from Metaboost dev Docker on 5532/6479). If not, the script exits with instructions (e.g. make test_deps). In Nix/agent environments use ./scripts/nix/with-env npm run test:e2e:api.

  • Test requirements (Makefile): Test-related commands live in makefiles/local/Makefile.local.test.mk. From repo root: make test_deps starts Postgres on 5632 and Valkey on 6579, creates two test databases: metaboost_app_test (main app; infra/k8s/base/ops/source/database/linear-migrations/app/0001_app_schema.sql) and metaboost_management_test (management-api; infra/k8s/base/ops/source/database/linear-migrations/management/0001_management_schema.sql), and creates app/management read and read_write roles (metaboost_app_read / metaboost_app_read_write, metaboost_management_read / metaboost_management_read_write). make help_test prints instructions.
  • Test databases: Tests use dedicated DBs on the same Postgres instance. Main: metaboost_app_test (api and management-api main-user CRUD). Management: metaboost_management_test (management identities, permissions, events). Default test ports are 5632 (Postgres) and 6579 (Valkey). Each test run starts with a clean slate: globalSetup truncates main and management tables once before any test file runs (api: apps/api/src/test/global-setup.mjs; management-api: apps/management-api/src/test/global-setup.mjs).
  • Database naming (dev/Docker/K8s): Two databases in one Postgres instance: app DB metaboost_app, management DB metaboost_management. Canonical env templates define DB_APP_NAME and DB_MANAGEMENT_NAME; owner credentials are DB_APP_OWNER_USER (default metaboost_app_owner) and DB_APP_OWNER_PASSWORD in db.env (with DB_HOST / DB_PORT for clients). Migrator credentials are DB_APP_MIGRATOR_* and DB_MANAGEMENT_MIGRATOR_*. The official Postgres Docker image still reads POSTGRES_USER, POSTGRES_PASSWORD, and POSTGRES_DB; local Compose maps those from DB_APP_OWNER_USER, DB_APP_OWNER_PASSWORD, and DB_APP_NAME. Apps use DB_APP_NAME (synced by local_env_setup). Management-api uses the same DB_HOST / DB_PORT plus DB_MANAGEMENT_NAME and DB_MANAGEMENT_READ_WRITE_* (inherited from env group db; no separate MANAGEMENT_DB_* vars). Role names: metaboost_app_read / metaboost_app_read_write, metaboost_management_read / metaboost_management_read_write; keys DB_APP_READ_* and DB_MANAGEMENT_READ_*.
  • Mailer: No local mailer service is required. Tests that cover verification flows use a Vitest mock of the mailer module to capture tokens and call verification endpoints; see apps/api/src/test/*.test.ts.

Auth and PII

  • Credentials: The system stores only passwordHash (no plaintext password). Never return passwordHash or any credential field in API responses. Use userToJson (or a similar safe serializer) for user data; never serialize req.user or user.credentials directly.
  • Verification tokens: Token hashes and raw tokens are never returned or listed by the API; they are only consumed server-side (verify-email, reset-password, confirm-email-change). Do not add endpoints that expose verification token entities.
  • User in responses: For the authenticated user only (login, me, signup, refresh, confirm-email-change, update-profile), use PublicUser (id, idText, email, username, displayName) via userToJson. Email is PII and must not be returned for other users. For any response that describes another user (e.g. bucket admins list/detail), use PublicUserSummary (id, idText, username, displayName) via userToPublicSummary from apps/api/src/lib/userToJson.ts.
  • API messages and locale: API structural messages (e.g. "Invalid or expired link", "Authentication required") are American English. Password validation text and email content (verification, password reset, email-change) are localized: resolved from Accept-Language when the value is supported (en-US, es), else app default from DEFAULT_LOCALE env. Do not localize the message field for structural errors; user-facing content such as email subject/body and password validation messages is localized.

Dependencies

  • Upgrade policy: Apply patch and minor updates routinely (e.g. npm outdated then bump versions and npm install). Major upgrades (e.g. @faker-js/faker, dotenv, eslint-plugin-perfectionist, express-rate-limit, lint-staged, nanoid, @types/node) are done separately with migration and testing. After any dependency change, run npm run build and npm run lint; commit the updated package-lock.json so CI and Docker use the same versions.
  • Linux-canonical lockfile: CI runs on Linux and needs Linux optional deps (e.g. @parcel/watcher, @next/swc-linux-x64-gnu, next-intl's @swc/core) in the lockfile. Generate or refresh the lockfile under Linux x64 so it stays correct for CI: run ./scripts/development/update-lockfile-linux.sh (requires Docker). The bump-version script runs this automatically before committing. When you add or update dependencies from a Mac, run that script and commit the updated package-lock.json.

Code Quality

Import order

Imports must follow a consistent hierarchy and be alphabetized within groups: (1) Node built-ins, (2) external packages, (3) workspace packages (@metaboost/*), (4) relative imports (parent, sibling, index), (5) style imports (SCSS/CSS) last. Type-only imports use a separate line (import type { X } from '...') and are ordered with their group. Enforced by ESLint rule perfectionist/sort-imports; run npm run lint:fix to auto-fix.

Skills and Rules

  • .cursor/skills/ – Project skills (one SKILL.md per directory). Use when the task matches the skill’s “when to use” so the agent follows the right patterns.
  • .cursor/rules/ – Glob-triggered or always-applied rules (e.g. eqeqeq, avoid-type-assertions, path-casing). Rules apply automatically when editing matching files.

All configuration is project-scoped (no home-directory skills or rules).

When to use which skill

Task or area Skill(s) to use
LLM / Cursor guidance llm-cursor-source when adding or editing skills, rules, .cursorrules, or .cursorignore. github for gh CLI read patterns and repo URLs. operator-only-git-operations rule for agent git/gh policy. See docs/development/llm/DOCS-DEVELOPMENT-LLM.md.
Plans plan-files-convention when creating or saving plan sets. parallel-plan-execution when decomposing large work into COPY-PASTA phases. plan-completion and plan-execution-completion-tracking rule when archiving finished plans. single-readme when adding index/overview docs (repo has only one README at root). Plans live in .llm/plans/active/; split if over 300 lines (see plan-creation rule).
Engineering ops logging for LOG*DIR / console-only policy. observability when adding OTLP/tracing (future). git-worktree-sibling for sibling metaboost*\* worktrees. github for issues/PRs/Dependabot. response-ending-make-verify for operator verification blocks.
API api for Express API patterns. api-testing when adding or changing API routes, auth, or env-dependent behavior (integration tests). swagger-openapi skill and openapi-sync rule when changing API surface. api-no-pii-credentials-in-responses rule applies to responses. config-type-safety rule and startup-validation-env-order skill for env/config. Operator runs: npm run test:e2e:api (integration), npm run test:unit (unit tier; excludes API apps).
Web / management-web web for Next.js app patterns. prefer-shared-ui-web-management and shared-ui-i18n rules. styles-source-of-truth, styles-import-last, modal-layout-contract, ui-component-promotion, reusable-components for shared UI. ui-e2e-screenshot-report for targeted screenshot reports. time-format-local, rate-limit-message, routing-url-params for display and URL conventions. e2e-page-tests when layout or behavior changes in web/management-web. test-timeout-budget rule for E2E/unit timeouts. e2e-seed-nano-id-limits when adding E2E fixture id_text values. management-post-save-navigation when adding/changing dedicated create or edit pages that save one row (redirect to list via ROUTES after success, except invite-link / password-only / detail flows). response-ending-make-verify (and end-with-targeted-make-report-verify rule) for E2E verification commands. Use make E2E targets only (e2e-run-with-make-only). Operator runs: make e2e_test_web, make e2e_test_report.
Testing (general) feature-implementation-testing — tests are required for features touching api/management-api/web/management-web. unit-test-priority-confident and unit-tests-risk-first for prioritizing unit tests. unit-test-design-no-overgranularity and unit-tests-confident-granularity for test depth. unit-test-new-code-gate when adding critical logic. unit-tests-security-authz-template for auth/permission tests. e2e-seed-nano-id-limits and e2e-seed-id-text-limits rule when changing E2E seed id_text values. e2e-permission-actor-matrix for management-web authz E2E. Operator runs: npm test (full), npm run test:unit (no DB), npm run test:e2e:api (DB needed), npm run test:reports (full with HTML).
i18n i18n when adding/editing translation keys, locales, or generating translations. time-format-local for client date/time display.
Forms / UI use-form-component, button-loading-async, password-strength-on-set-update. modal-layout-contract for dialog footers. avoid-type-assertions, eqeqeq-strict-equality rules apply.
DB / ORM database-schema-naming, typeorm-orderby-property-names, generate-data-sync when schema or entities change. nested-resource-prefix-naming for nested resources.
K8s / Argo CD k8s skill and infra-k8s rule. github-actions-yaml rule for workflow edits. Remote cluster runbook: docs/development/k8s/REMOTE-K8S-GITOPS.md. argocd-gitops-push when changing files under infra/k8s/ or sync targets for k8s (add push-to-Git reminder in response so Argo CD can sync).
Env / templates env-file-formatting, env-expiration-naming, env-defaults-match-code when adding env vars or templates. startup-validation-env-order when editing validation.ts. Keep env work anchored to canonical templates/examples (scripts/local-env/setup.sh, scripts/env-overrides/prepare-home-env-overrides.sh). config-type-safety rule for app config modules. For remote K8s, maintain env/manifests directly in your GitOps repository.
Imports / paths path-casing-imports when adding or changing relative imports (rule also applies on .ts/.tsx). import-specifiers-tiered when editing imports across Tier A vs Next src or considering codemods. type-imports-separate-line rule for type-only imports. app-internal-import-aliases rule for @api/* and @management-api/* in Node apps.
Build / lockfile / Docker build-order and build-order-doc-sync when changing root build scripts. native-deps-platform-mismatch when diagnosing missing platform binaries after lockfile refresh. docker-runtime-workspace-parity when adding packages/*, changing @metaboost/* app dependencies, or editing infra/docker/local/** Dockerfiles. linear-baseline-gz-sync when linear migration SQL changes.

For large or multi-step plans: save under .llm/plans/active/ and use plan-files-convention; execute via COPY-PASTA prompts one plan at a time.