Skip to content

Local Development Workflow

The 30-minute merge β†’ deploy β†’ rebuild cycle is a release pipeline, not the edit-test loop. Everything below runs entirely on your machine. You should only deploy to ship a change that already passed locally.

Golden rule: never merge to main to test. Merge is the last step, after local tests pass on your branch.

The loop, start to finish

1. Start Claude

Terminal window
cd ~/Projects/accessible
claude

Or let the dev script launch it for you (it starts the stack first β€” see below).

2. Start the emulators (the local stack)

One command brings up Supabase (Kong on :8000), MinIO, LocalStack, the API + batch worker, and the Python sidecars (docx2acroform, flyer-tagger, veraPDF, WeasyPrint, …):

Terminal window
docker compose -f docker-compose.yml -f docker-compose.dev.yml --profile local up -d

Or use the wrapper, which also stops any stray CLI supabase stack, parks the second API replica, streams logs, and (in a real terminal) launches Claude:

Terminal window
accessible-dev # web only
accessible-dev --all # every app
accessible-dev --apps=web,forms

Supabase split-brain gotcha: use the compose Supabase (Kong :8000), NOT the CLI supabase start stack. If both run they fight over 54322/3/4, the API points at nothing, and the web app silently talks to prod. setup-local.sh / accessible-dev stop the CLI stack for you.

3. Work in the main checkout, on a branch

Use one checkout (~/Projects/accessible) and a feature branch inside it:

Terminal window
git checkout -b feature/<short-description>

The stack serves whatever source is on disk in the checkout you launched docker compose from. One checkout + one branch = the stack always serves the code you’re editing. (Do not run the active task from a separate git worktree β€” the stack would keep mounting the other directory’s source and your changes would appear to β€œnot run locally”. Worktrees are for parallel work and for subagents; see below.)

4. Refresh AWS creds if the feature calls Bedrock or Textract

The AI paths (PDF vision, the forms AI rebuild, Textract image lane) call AWS. Inject short-lived SSO creds once per session:

Terminal window
dev-aws-creds # logs into SSO (browser), writes creds, recreates sidecars

Re-run when the session expires (1–12h). Without creds these paths fall back to their deterministic non-AI behaviour rather than erroring.

5. Edit β†’ hot-reload β†’ test

ServiceReload
API / batch worker (TypeScript)tsx watch β†’ ~2s, automatic
docx2acroform (Python/Flask)gunicorn --reload β†’ ~1s, automatic
flyer-tagger (Lambda RIE)rebuild the image β€” no in-process watcher (bind-mount + docker restart once it lands in base compose)
Next.js appsFast Refresh β†’ instant

Code edits are free. A new dependency (npm or pip) needs one rebuild:

Terminal window
dev-rebuild # TS services (api / batch worker)
docker compose -f docker-compose.yml -f docker-compose.dev.yml \
--profile local build docx2acroform # a Python sidecar

Exercise the change directly — hit the endpoint or click through the app on localhost (see dev-ports for the app→port map; dev-proxy gives HTTPS *.localhost names mirroring prod).

6. Run the branch’s tests

Terminal window
npm run test # or the workspace-scoped equivalent

Python tools: cd tools/<tool> && python3 -m pytest -q.

7. PR when ready β†’ push β†’ merge

Only now does the branch leave your machine:

Terminal window
git push -u origin feature/<short-description>
gh pr create --base main --fill
# CI green + review β†’ merge
gh pr merge --squash

Deploy happens off main as the release step β€” it is never how you test.

Delegating to isolated subagents

For parallel or well-scoped work, hand a task to a subagent that owns the full branch β†’ PR β†’ merge lifecycle itself, and runs in its own git worktree so it never touches your main checkout’s working tree or the running stack:

  • Give the subagent a single, verifiable goal (β€œadd X, with tests, open a PR to main”).
  • It creates its own branch in an isolated worktree, implements, runs tests, pushes, and opens the PR β€” merging to main when green.
  • Because it is isolated, several can run at once without clobbering each other or your local stack. Your main checkout keeps serving your branch to the emulators the whole time.

This is the one case where worktrees are correct: isolation for background work, not for the branch you are actively hot-reloading in the foreground.

Quick reference

Terminal window
accessible-dev [--all|--apps=…] # start stack (+ Claude), hot-reload
dev-aws-creds # refresh Bedrock/Textract SSO creds
dev-rebuild [--api|--batch|--all] # rebuild TS service images (new deps)
dev-ports [<app|port>] # app ↔ port ↔ prod-domain map
dev-proxy [up|reload|down] # HTTPS *.localhost names
dev-tail [api|web|both] # tail dev-server logs

See also: the project-local-dev-loop note in Claude memory for the deeper gotchas (baked-image staleness, Kong readiness probe, seed/auto-login fixes).