Route-Share Local Devstack
The reset needs one local loop an agent can run before a product branch is merge-ready. This command makes that loop explicit: service paths, ports, commands, fake Garmin, fake chat, local infra, and smoke evidence all live in one machine-readable plan.
Commands
imperfect-cli route-share-devstack check
imperfect-cli route-share-devstack up --smoke
imperfect-cli route-share-devstack up --smoke --exit-after-smoke
imperfect-cli route-share-devstack smoke
check does not start services or mutate local infra. It prints the full plan,
optionally writes route-share-devstack-check.json with --artifact-dir, and
exits 2 with actionable missing inputs when a sibling repo, command, compose
file, or local env is missing.
Preflight expects docker, uv, node, and npm on PATH; sibling repos
alice, cheshire, footman, and looking-glass under --repos-root /
ROUTE_SHARE_REPOS_ROOT; and either an imperfect-api .env file or
ANTHROPIC_API_KEY in the shell for local imports.
up starts local infra from docker-compose.route-share.yml, then starts the
hot-reload service processes. Use --exit-after-smoke for a bounded agent/CI
run that boots, validates, then cleans up instead of staying attached:
| service | URL | command |
|---|---|---|
| imperfect-api | http://127.0.0.1:8000 |
uv run imperfect-cli serve --port 8000 |
| Cheshire | http://127.0.0.1:8767 |
uv run uvicorn cheshire.server:app --host 127.0.0.1 --port 8767 |
| Alice | http://127.0.0.1:8766 |
docker compose up --build -d alice |
| fake Garmin | http://127.0.0.1:8010 |
uv run uvicorn alice.vendors.garmin.fake_server:create_fake_garmin_app --factory --host 127.0.0.1 --port 8010 |
| Footman fake chat | http://127.0.0.1:4510 |
node src/cli.js whatsapp route-share-sim --host 127.0.0.1 --port 4510 |
| Looking Glass | http://127.0.0.1:3000 |
npm run dev -- --hostname 127.0.0.1 --port 3000 |
For agent runs, pass --artifact-dir tmp/route-share-devstack to persist
route-share-devstack-check.json and route-share-devstack-smoke.json.
Host-process logs are appended under
tmp/route-share-devstack/logs/{service}.log. If health checks time out, retry
with --timeout-seconds 90 before treating it as a product failure.
The default Cheshire path is the normal sibling cheshire checkout. #980 is
merged, so the devstack no longer prefers the old #977 issue worktree; using
main by default avoids stale WIP winning silently. Override sibling discovery
with:
When a service runs from a linked worktree, the devstack also checks the
canonical sibling checkout for a local .env file and passes those values to
the child process. The printed plan includes hard-coded local dev env, env-file
paths, and required key names; dotenv secret values are not printed. Cheshire
currently requires MAPTILER_API_KEY before it can boot because its settings
validate raster tile configuration at startup.
Local Infra
docker-compose.route-share.yml starts the infra the route-share harness owns:
- MongoDB on host port
27018, databasecuishe_route_share - MinIO on
9000with bucketimperfect-route-shares-local
Alice's own compose file still owns Postgres + Valkey on 6379, under the
route-share-specific Compose project route-share-e2e-alice. Cheshire's own
compose file starts Postgres on 5433 and Valkey on 6380 under
route-share-e2e-cheshire, while the Cheshire ASGI process stays host-side for
fast local iteration. The route-share stack injects fake Garmin endpoint env
into Alice and local R2 env into Cheshire. The fake Garmin OAuth callback is
allowlisted for
http://127.0.0.1:3000/garmin/route-send/callback, and imperfect-api sends
WhatsApp bridge deliveries to the fake chat adapter on 4510. No production
Garmin credentials are required for the local path.
Alice points FIREBASE_CREDENTIALS_PATH at an intentionally missing file in
this stack. Alice treats missing Firebase credentials as degraded Apple Health
auth, while the Garmin internal routes used by route-share e2e continue to
boot. This avoids Docker's missing-file bind mount behavior, where
FIREBASE_CREDENTIALS.json can appear as a directory and crash startup.
Detached services are readiness-gated before dependent host processes start:
Alice must answer /health before the devstack launches Cheshire, because
Cheshire introspects Alice's migrated vendor schemas during startup.
The backing stores are intentionally ephemeral: cleanup runs docker compose
down --volumes --remove-orphans for the route-share, Alice, and Cheshire
Compose projects. That keeps local e2e runs deterministic and avoids stale
schema/data from previous route-share experiments.
Smoke
smoke validates the running fake services and the fake-chat contract payload:
GET /api/contractandGET /api/fixturefrom the Footman fake chatGET /fake-garmin/statefrom Alice's fake Garmin- the fake-chat payload through
assert_route_share_reset_route_options_payload()
The route-option smoke rejects:
- media attachments / HD PNG delivery
- text baked into preview images
- oversized, unowned, or non-JPG preview assets
- legacy picker copy from the retired two-turn flow
- replies that cannot bind back to a single route option message
This proves the services boot, local fake chat exposes the route-option contract
shape, and fake Garmin is reachable. It does not prove live route generation.
To run live mode, start imperfect-cli route-share-devstack up --smoke in one
terminal and leave it running. Then run the live harness from a second terminal:
imperfect-cli route-share-e2e --live \
--prompt "Create three scenic trail running routes from the Conservatory of Flowers in Golden Gate Park, San Francisco, about 5 miles, starting and ending there." \
--artifact-dir tmp/route-share-e2e-live
That live command is wired through fake chat, the BFF webhook, channel workers, route-page HTML rendering, manifest checks, fake Garmin auth/callback, and duplicate send idempotency. Generated local runs should produce artifacts that point at the current product contract instead of forcing manual WhatsApp or Garmin testing. Browser screenshots and a real WhatsApp unfurl smoke remain the next layer above this local loop.