Completion Report: ANU-6 - Add the containerized local CMS development stack
Implementation summary
ANU-6 adds the repository-owned local development stack without moving any production or hosted data. The tracked implementation contains:
- a pinned Node 22.5.1 development image and Compose
webservice - pinned PostgreSQL 17/pgvector 0.8.2 and pgAdmin 9.16 services
- loopback-only ports, health gates, hot reload, and persistent named volumes
- safe tracked environment examples and ignored machine-local secrets/artifacts
- explicit database export, loopback-only restore, parity, and volume-reset guards whose default modes do not connect or mutate
- a container smoke for Payload, Mastra storage, pgvector insert/query/delete, and the authenticated worker API
- bootstrap, architecture, environment, command, testing, and recovery docs
The real Supabase export, restore, parity acceptance, and cutover remain owned by the dependent migration issue. No source database was read or modified while implementing ANU-6.
Acceptance evidence
| Acceptance check | Result | Redacted evidence |
|---|---|---|
| Compose validates without embedded secrets | Passed | docker compose --env-file .env.compose config --quiet exited 0. Tracked Compose contains interpolated variable requirements, while .env.compose and .local-data/** are ignored. The guard test also rejects tracked placeholder/secret literals in compose.yaml. |
One command starts web, postgres, and pgadmin on approved ports |
Passed | After the Docker Desktop image-store change, pnpm stack:up pulled and built the images and reported all three services healthy. stack:status showed loopback-only mappings for 127.0.0.1:3000, 127.0.0.1:5432, and 127.0.0.1:5050; pgAdmin returned HTTP 200 and PostgreSQL accepted TCP connections. |
| PostgreSQL 17.10, pgvector 0.8.2, pgAdmin 9.16, and resolved digests | Passed | Running containers reported PostgreSQL 17.10, pgvector 0.8.2, pgAdmin 9.16, and Node 22.5.1. PostgreSQL and pgAdmin ran from the pinned repository digests; the final local web image ID was sha256:3d13dc30087e229ad0f5cb4d5a71fe6e49ae3f00ef8184f042135938a39e7b7e. |
| Restart preserves named-volume state and routine stops do not delete it | Passed | Exact temporary sentinels in PostgreSQL and the pgAdmin data directory survived both an all-service restart and stack:down followed by stack:up. The named volume identities remained unchanged, and the sentinels were removed after verification. |
Payload, Mastra, RAG vector operations, and worker API work from web against local Postgres |
Passed | pnpm stack:verify returned ok: true with Payload initialized, Mastra storage initialized, deterministic pgvector insert/query successful, and the authenticated worker API returning 200. The final smoke was repeated after the down/up persistence cycle. |
| Export/restore tooling has explicit inputs, ignored outputs, and destructive guards | Passed | The 10-test local-stack suite passed. Plan mode does not require or read a source URI; artifacts must be named descendants of .local-data/database; restore requires an exact redacted loopback fingerprint and a second exact confirmation for a non-empty target; reset also requires an exact project confirmation. No real export or restore ran. |
Verification commands and results
docker compose --env-file .env.compose config --quiet: passed.docker compose build --no-cache web: passed after replacing the failing bundled Corepack signature path with pinnedpnpm@10.7.0installation.- Final
docker compose --env-file .env.compose build web: passed with a 61.68 kB Docker context and local image IDsha256:3d13dc30087e229ad0f5cb4d5a71fe6e49ae3f00ef8184f042135938a39e7b7e. pnpm stack:up: passed after the Docker Desktop image-store change; all three health gates passed. A Docker Desktop restart repaired one transient Windows bind-mount bridge error encountered after changing the engine setting.- Runtime probes: PostgreSQL
17.10, pgvector0.8.2, pgAdmin9.16, Node22.5.1, pgAdmin HTTP 200, PostgreSQL TCP reachable, and authenticated web worker health requests returned 200. pnpm stack:restartplusdocker compose up --wait: passed. The web launcher detects an initialized Payload schema, disables repeat schema push, and waits for PostgreSQL readiness during simultaneous service restarts.pnpm stack:downfollowed bypnpm stack:up: passed; temporary PostgreSQL and pgAdmin sentinels persisted through container/network removal and were cleaned up afterward.pnpm stack:verify: passed twice, including after down/up, with Payload, Mastra storage, pgvector insert/query, and authenticated worker API results.pnpm lint -- <ANU-6 implementation files>: passed.pnpm lint: repository-wide check reached unrelated pre-existing findings in historical/generated CSS, the generated import map and Payload types, andsrc/mastra/tools/presentation/index.ts; ANU-6 files pass the targeted check.pnpm typecheck: passed.tests/unit/local-stack.test.ts: 10 tests passed.pnpm test:int: 95 tests passed; the one unrelated existing failure istests/unit/anuva-orchestration-service.test.ts, whose expectation still names the obsolete combined narrative-agent call after the repository split that orchestration into separate calls.pnpm test:e2e: the server and Chromium started, then all six existing presentation scenarios timed out on the same stalepresentation-usecase-product_explainerlocator. The captured page was already on the current Video Setup screen, which does not contain that old use-case control; server output also contained Better Auth 401 responses. This route-suite drift is outside the local-stack scope.git diff --check: passed.mkdocs build --strict: passed; only existing navigation and absolute-link informational warnings were reported.- Recursive documentation index validation and preview HTTP checks: passed.
Documentation and preview URLs
- Change overview:
https://web1.girishd.com/changes/2026-07-20-switch-to-local-postgres/ - Implementation plan:
https://web1.girishd.com/changes/2026-07-20-switch-to-local-postgres/ImplementationPlan/ - Implementation log:
https://web1.girishd.com/changes/2026-07-20-switch-to-local-postgres/ImplementationLog/ - Completion report:
https://web1.girishd.com/changes/2026-07-20-switch-to-local-postgres/CompletionReport/ - Local stack bootstrap:
https://web1.girishd.com/bootstrap/LocalDevelopmentStack/ - System architecture:
https://web1.girishd.com/core/SystemArchitecture/ - Environment and services:
https://web1.girishd.com/state/EnvAndServices/ - Scripts and commands:
https://web1.girishd.com/state/ScriptsAndCommands/ - Testing and verification:
https://web1.girishd.com/core/TestingAndVerification/ - Known gaps:
https://web1.girishd.com/state/KnownGaps/
Product Main synchronization
Product Main/ANU-5 must receive [ANU-6 draft PR
30](https://github.com/Shoonya-Game-Technologies/anuvax-cms/pull/30), this
plan/log/report, exact preview URLs, immutable version/digest evidence, and the completed local runtime acceptance evidence. The dependent migration issue must retain ownership of the real Supabase export, reviewed role/schema selection, local restore, parity proof, application acceptance, source protection, and cutover/rollback decision.
Canonical docs remain unpublished. The CMS URLs above are review previews only.
Remaining risks and skipped checks
- The repository-wide lint/integration/E2E failures described above are not caused by the local-stack files, but they remain visible acceptance debt and should be resolved by their owning work before release-level verification.
- External S3, AI, email, billing, Python, and Unity dependencies are deliberately outside this Compose stack and still require environment-specific testing.
Rollback and recovery
- Reverting the ANU-6 commit restores the prior host-run configuration; it does not touch the remote Supabase source.
pnpm stack:downis the normal diagnostic stop and preserves all named-volume state. Reviewpnpm stack:logsbefore retrying a failed start.pnpm stack:resetonly prints its exact target plan. Volume deletion requires--execute --confirm anuvax-cms-localand was not used during verification.- Restore execution accepts only a loopback target and refuses ambiguous or non-empty targets without exact confirmations. Those paths remain deferred to the dependent migration issue.