MeshiTrack/ANTIGRAVITY.md

9.7 KiB

ANTIGRAVITY.md

This file provides guidance to Antigravity (the Gemini-based coding assistant) when working with code in this repository.

Project Overview

MeshiTrack is a self-hosted medicine & nutrition management platform. It is a TypeScript monorepo with three packages: packages/api (Fastify backend), packages/web (Next.js frontend), and packages/shared (types + Zod schemas used by both). Medicine tracking is implemented first (Phases 1-4), followed by food tracking (Phases 5-9).

Commands

Root (all packages via Turborepo)

npm run dev          # Start all services in dev mode
npm run build        # Build all packages (shared → api/web)
npm run test         # Run all tests
npm run test:cov     # Run all tests with coverage
npm run lint         # Lint all packages
npm run lint-fix     # Auto-fix lint issues
npm run typecheck    # Type-check all packages
npm run clean        # Remove build artifacts
npm run seed         # Seed the database (delegates to packages/api)

Single package

npm run test -w packages/api           # Run API tests
npm run test:cov -w packages/api       # API tests with coverage
npm run test -- --watch -w packages/api  # Watch mode
npm run dev -w packages/api            # API dev server only

API package (packages/api)

npm run dev      # tsx watch src/main.ts
npm run build    # tsc
npm run seed     # tsx src/scripts/seed.ts

Docker

docker compose -f docker/docker-compose.yml up -d    # Start all services
docker compose -f docker/docker-compose.yml down     # Stop all services

Implementation Workflow (TDD-First)

Every feature or phase implementation MUST start with a rigorous planning and design phase:

  1. Mandatory Planning (Grilling): Before starting any feature or bug fix, you MUST invoke the grill-with-docs skill.

    • Run the skill using: view_file on .agents/skills/grill-with-docs/SKILL.md and follow its instructions.
    • Stress-test your plan against the existing domain model, terminology, and documentation (CONTEXT.md, ADRs, docs/tdd-transition-plan.md).
    • Decisions must be crystallized and documentation updated before moving to implementation.
  2. Strict TDD Cycle: All code changes must strictly follow the Red-Green-Refactor loop as detailed in TDD Guide. Do not write any production code before writing its failing test.

  3. Vertical Slice TDD Order: Build each vertical slice following this exact test-first sequence:

    • Shared Layer: Write validation schema tests first in packages/shared/tests/ -> Implement schemas in packages/shared/src/.
    • Database Layer: Write integration tests in packages/api/tests/ using mongodb-memory-server -> Implement Mongoose schemas and Repository in packages/api/src/. All reads must use .lean().exec().
    • Service Layer: Write unit tests in packages/api/tests/ using createMockRepository -> Implement business logic Service class in packages/api/src/ with Awilix constructor injection.
    • Route Layer: Write route tests in packages/api/tests/ using app.inject -> Implement Fastify route plugin in packages/api/src/.
    • Web API Client: Write service unit tests in packages/web/tests/ using MSW mocking -> Implement the service client in packages/web/src/.
    • Web UI: Write component tests in packages/web/tests/ using React Testing Library -> Implement Next.js pages and components in packages/web/src/. Use Server Components by default.

Verification Gate (Mandatory): Before considering a task complete, run:

  • npm run build
  • npm run test:cov (must meet 100/100/100/90 for API/Shared and 90/85/75/85 for Web)
  • npm run lint

Architecture

Monorepo Structure

  • packages/shared — Single source of truth for all domain types, enums, and Zod v4 schemas. Consumed by both api and web. Must be pure TypeScript with no Node.js, browser, or framework dependencies.
  • packages/api — Fastify 5 backend. ESM-only, TypeScript strict. Uses Awilix for DI, Mongoose 9 for MongoDB, jose 6 for JWT verification.
  • packages/web — Next.js 16 (React 19) frontend. App Router, Tailwind CSS 4.

API Layer Architecture (Fastify + Awilix)

The API follows a routes → services → repositories pattern with Awilix constructor-injection DI:

  • Each domain feature lives in src/modules/<feature>/ with files: *.routes.ts, *.service.ts, *.repository.ts
  • Route plugins use fastify-plugin (fp()) to export routes and register Awilix dependencies
  • Services receive dependencies via destructured constructor: constructor({ productsRepository }: { productsRepository: ProductsRepository })
  • Resolve services per-request via request.diScope.resolve<T>('serviceName')
  • All Mongoose read queries must use .lean().exec()

Plugin registration order in main.ts: security → compression → swagger → DI container → database → auth → household guard → route modules.

Domain Modules

Medicine domain (Phases 1-4): medicines/, medicine-products/, cabinet/, regimens/, organizer/, medicine-prices/, purchases/, refills/ Food domain (Phases 5-9): products/, recipes/, pantry/, meal-plans/, grocery/ Shared: health/, users/, households/, stores/, llm/

Multi-tenancy

Every domain document is scoped to a householdId. A Fastify preHandler hook validates the householdId from the URI against the user's householdIds[] JWT claim. Every data query must filter by householdId.

Routes can opt out with config: { public: true } (skips auth) or config: { skipHousehold: true } (skips household validation).

Auth

Keycloak is the OIDC provider. The API verifies JWTs via jose. The custom Keycloak protocol mapper injects householdIds[] into the JWT claims.

Shared Package Rules

  • All domain types and Zod schemas live here — never duplicate types across packages
  • Import from 'zod/v4' (not 'zod')
  • Use z.enum() for enums, z.email() / z.url() as top-level calls
  • Every directory has a barrel index.ts
  • Use import type for type-only imports

Pagination

All list endpoints use cursor-based pagination. Never use skip() on MongoDB queries. Response shape:

{ data: T[], pagination: { cursor: string | null, hasMore: boolean, total?: number } }

Error Handling

Services throw AppError subclasses (NotFoundError, ConflictError, ForbiddenError, etc.). The global Fastify error handler maps them to the standard ApiError response shape (statusCode, error, message, timestamp, path).

Key Rules

  1. No any types — use unknown + Zod validation at boundaries
  2. ESM everywhere"type": "module", .js extensions on all imports, import type for type-only
  3. Cursor-based pagination only — never skip() for large collections
  4. Zod v4 — import from 'zod/v4'
  5. .lean().exec() on all Mongoose read queries
  6. householdId filter on every domain query — this is the multi-tenancy boundary
  7. No emojis — never use emoji characters in source code, UI text, console output, or documentation
  8. npx is banned — never run npx for any reason. Use npm run <script> for all test, lint, build, and tool invocations. No exceptions.
  9. Never pipe or redirect npm run commands — run npm run <script> exactly as written; never append 2>&1, |, Select-Object, Select-String, or any other shell constructs to it.
  10. Never use shell commands to read files — always use view_file tool. Commands like Get-Content, cat, head, tail will be denied.
  11. Never use shell commands to search — always use grep_search or file_search. Commands like Select-String, grep, rg, find will be denied.
  12. Never call tools in parallel — always wait for one tool call to complete before calling the next. This applies to all tools: file reads, searches, and terminal commands.
  13. Every implementation task must end with npm run build, npm run test:cov, and npm run lint all passing. Always use test:cov (not test) to enforce coverage thresholds. If coverage fails, write additional tests before considering the task done.
  14. No .js extensions on @/ imports in packages/web — Next.js resolves TypeScript files directly; .js extensions on @/ path-alias imports break Turbopack and webpack. Only use .js extensions in packages/api and packages/shared (Node ESM).

Testing

  • Unit tests: Vitest, co-located with source files as *.routes.test.ts / *.service.test.ts
  • Integration tests: Vitest + mongodb-memory-server (*.integration.test.ts)
  • Component tests: React Testing Library (__tests__/*.test.tsx)
  • E2E: Playwright (root-level e2e/)
  • Use ClassName.name for describe labels (not string literals)
  • Coverage targets:
    • packages/api & packages/shared: 100% lines/functions/statements, 90% branches
    • packages/web: 90% lines, 85% functions, 75% branches, 85% statements
  • Mark untestable lines with /* v8 ignore */

Documentation

Before writing code, consult the relevant docs:

  • docs/instructions/ — coding conventions, Fastify patterns, Next.js patterns, MongoDB, Zod/TypeScript, testing, Docker, Keycloak, Turborepo
  • docs/phases/ — per-phase specs with schemas, endpoints, and business logic
  • docs/architecture.md — ADRs explaining key technology choices
  • docs/cross-cutting.md — API versioning, pagination, security, audit trail

Git Conventions

Branches: feature/MESH-001-description, fix/MESH-042-description, chore/description

Commits follow Conventional Commits: feat(products): add barcode lookup, fix(pantry): correct freshness calc