Start Surge locally using the dev shell function from ~/.zshrc.
Launch
You must run this as a background agent terminal. A foreground terminal blocks your turn and forces the user to manually background it. Do not wait for Phoenix to finish starting before backgrounding - launch in the background immediately, then verify in a separate command.
Run from the repo root:
cd "$(git rev-parse --show-toplevel)" && tail -f /dev/null | zsh -ic 'dev'
Shell tool settings:
block_until_ms:0(background immediately - required)required_permissions:["all"](port binding, killing stale listeners, Livebook)
The tail -f /dev/null | pipe keeps stdin open so IEx stays alive in a background terminal. Do not run bare zsh -ic 'dev' without the pipe - agent shells close stdin immediately and IEx exits within seconds.
Verify
lsof -nP -tiTCP:4000 -sTCP:LISTEN
Expect:
- HTTP on 4000, HTTPS on 4001
- IEx node
surge_dev@127.0.0.1(cookiesurge_dev_local) - Livebook on 8080 if
livebookis installed
Notes
devrunsmix deps.get, stops any existing listener on port 4000, theniex -S mix phx.server.- Use
devoinstead ofdevwhen the user wants Livebook to open a new attached notebook after Phoenix is up.
Log in
Use playwright-cli with --headed so the user can see the browser. Credentials come from priv/repo/seeds.exs.
Default: log in as dennis@surge.app unless the user asks for admin access.
Before opening the browser, set both seeded users to system theme. Older local databases may still have light from the theme migration backfill. Use psql when the dev server is already running (mix run will fail on the PromEx port):
psql postgresql://postgres:postgres@localhost/surge_dev -c "UPDATE dashboard_users SET theme = 'system' WHERE email IN ('dennis@surge.app', 'admin@surge.app');"
Playwright's Chrome does not inherit macOS dark mode - prefers-color-scheme stays light unless emulated. Run this immediately after playwright-cli open:
playwright-cli run-code "async page => await page.emulateMedia({ colorScheme: 'dark' })"
playwright-cli open https://hq.jason.surge-eng.com/ --headed
playwright-cli fill e19 "dennis@surge.app"
playwright-cli fill e23 "Abcd1234!"
playwright-cli click e32
Dennis is a regular dashboard user (platform admin on RiseUp Bakery Solutions). After sign-in, expect the platform accounts page - not /admin/*.
To start a fresh session, clear cookies before navigating to the login page:
playwright-cli cookie-clear
playwright-cli goto https://hq.jason.surge-eng.com/users/log_in
Admin access
Use only when the user explicitly asks for admin or HQ admin access:
admin@surge.app/Abcd1234!- system super-admin; lands on/admin/platformswith access to accounts, dashboard users, campaign registration, etc.
Browser debugging
After logging in, reproduce UI bugs the way the user did - form changes, clicks, and push_patch navigation - not only by goto on the final URL. A bug may appear after filtering or selecting a row but disappear on full page reload; that pattern points to a LiveView patch/morph issue.
When content looks missing after an in-app update, check whether the DOM has text or only LiveView comment placeholders:
playwright-cli eval "({ textLen: document.querySelector('#some-id')?.textContent?.trim().length, hasComments: document.querySelector('#some-id')?.innerHTML?.includes('<!--') })"
Empty textLen with hasComments: true means the server sent data but the client patch did not render it.
Dennis's seeded platform is RiseUp Bakery Solutions (plt_01j9a40ea2fh48x5fmdxq1n0p6). Example deep link after login:
playwright-cli goto https://hq.jason.surge-eng.com/platforms/plt_01j9a40ea2fh48x5fmdxq1n0p6/api_logs
With the dev server already running, one-off mix run -e '...' often fails because PromEx owns port 9568. Use psql for simple local DB tweaks instead.
Before reporting back to the user, remove debugging artifacts but keep the browser open so the user can verify:
rm -rf .playwright-cli
Do not leave snapshot YAML, console logs, or other throwaway files in the repo.
Migrations debugging
When the dev server is up but requests fail with Phoenix.Ecto.PendingMigrationError, the local database is behind the repo's migration files.
Rules
- Do not add new migration files to fix local dev state.
- Never reset the database without explicit user approval - do not run
mix ecto.reset,mix ecto.drop, or equivalent even as a last resort. - If you get stuck, stop and tell the user - do not keep trying destructive fixes.
Diagnose
cd "$(git rev-parse --show-toplevel)" && mix ecto.migrations
Look for rows with status down. Those are pending.
Fix
Run pending migrations in a separate terminal (dev server can stay running):
cd "$(git rev-parse --show-toplevel)" && mix ecto.migrate
Re-check with mix ecto.migrations - all rows should be up. Confirm the app responds:
curl -sf -o /dev/null -w "%{http_code}" http://127.0.0.1:4000/
A redirect (302) or 200 means the pending-migration blocker is cleared.
When migrate fails
- Read the error output - common causes: renamed table/column already applied manually, constraint conflict, or stale DB from an old branch.
- Compare the failing migration file in
priv/repo/migrations/with actual schema (mix ecto.dumpor\d table_namein psql). - Stop and report to the user with the exact error - do not invent a new migration or reset the database.
Database reset
Never run this unless the user explicitly asks. If a reset might help, explain why and wait for approval:
mix ecto.reset
This runs ecto.drop, ecto.create, ecto.migrate, and seeds.