Skip to main content

local-dev-stack

Why It Exists

local-dev-stack is the operational counterpart to runtime-env-config: that skill is the app-side loader chain (@cogs/configprocess.envEnvProvider), and this skill is everything that loader depends on being already running — the stage files on disk, the process orchestration, and the database it talks to. Both trace back to the same origin, docs/sessions/2026-08-02-env-stack-migration/ARCHITECTURE.md, which documents workloom as "repo 1 of ~12" — the concrete instantiation every invariant in this skill was extracted from, with its diff serving as the literal migration template for the other 11 repos in the rollout.

The skill's tenth invariant — verify a repo's own onboarding docs against process-compose.yml directly, rather than trusting them — was added later, in commit e3f255f, after a 13-repo rollout turned up something concrete: more than one repo's README/CLAUDE.md/SETUP.md still described a completely different, superseded local-dev flow (Postgres plus a generic process supervisor, predating the migration to this skill's process-compose.yml pattern) that a new developer would have followed to stand up the wrong stack entirely. That same rollout also upgraded the port-block invariant from a theoretical concern to a documented, live-confirmed one: two real repos independently defaulted to the identical 3010/4010 dev-server ports, a collision that had nothing to do with Supabase's own 54N2x port block and needed its own separate allocation table.

What It Does

Lays out the four things that have to work together before an app can boot locally: which .env files are committed vs. gitignored (split by the env registry's type), the .gitignore negation block that keeps stage files tracked without leaking secrets, a process-compose.yml that boots Supabase then runs the database and seed steps in strict dependency order, and a per-repo port-block convention — for both Supabase's own ports and the app's own dev-server ports — so multiple stacks can run on one machine without colliding.

How To Use It

Trigger phrases: "process-compose", "Procfile to process-compose", "local Supabase ports", "port is already allocated", "supabase start fails / migrations before tables", "db:post", "apply-post-schema", "RLS out of migrations", "commit .env.dev but not the secrets", ".gitignore .env negation", "ENV stage secret split", "two local Supabase stacks at once", or standing up a local dev stack.

skills add git@github.com:catesandrew/next-starters.git --skill skills/local-dev-stack -g
npm install @next-starters/skill-local-dev-stack
/plugin marketplace add catesandrew/next-starters
/plugin install local-dev-stack@next-starters

Gotchas & Invariants

  • ENV is never local.env.local is the personal-override slot, and an ENV=local stage file would collide with it.
  • Only env-registry entries with type:'public' are safe to auto-commit; type:'connection' values are treated as potentially credential-bearing and flagged for manual review rather than committed automatically.
  • Anything that depends on app tables existing — RLS ENABLE, grants, an auth hook — cannot live in supabase/migrations, because supabase start runs migrations at boot, before db:push creates those tables. It moves to an idempotent db:post step that runs after the schema exists, both locally and in prod.
  • Local Supabase's service_role key and JWT secret go in .env.<stage>.local only — never printed, never committed, never even present in the committed .env.<stage> file.
  • Seeded accounts must be created via supabase.auth.admin.createUser(...), not a plain Drizzle seed insert — a plain seed writes app rows with random UUIDs that map to no real auth.users row, so those accounts can neither log in nor resolve an org/role. This step is guarded to refuse running against anything but a localhost/127.0.0.1 Supabase URL.
  • Even with the membership correctly keyed to a real auth UID, RLS on organization_memberships can silently block the access-token hook unless supabase_auth_admin has an explicit permissive SELECT policy — a bare GRANT is not enough, and the failure mode is a login that "succeeds" but resolves an empty org/default role.
  • runtime-env-config — the app-side loader and EnvProvider chain that reads the stage files this skill lays out.
  • prod-deploy — the production counterpart: the same db:post step re-run as db:deploy against Supabase Cloud, and every local-only convenience (captcha off, auto-confirm) reversed for prod.

Sourced from: skills/local-dev-stack/metadata.json, skills/local-dev-stack/SKILL.md, docs/sessions/2026-08-02-env-stack-migration/ARCHITECTURE.md, docs/sessions/2026-08-05-fleet-migration-progress/SUMMARY.md, git commit e3f255f