Devtools Console Refine: intuitive help + mobile access โ
Status: APPROVED + BUILT (2026-08-03). Operator approved Option B; placement confirmed as a dedicated in-app page at #/devtools. Release held until EOD on operator's mark.
Decisions taken during build (operator can veto any at review):
- Section 2 gating move: implemented. Gated on
import.meta.env.VITE_APP_ENV !== 'production'(statically replaced, so prod builds still dead-code-eliminate the block; verified by building both ways and greppingdist/). Deployed dev + previews now expose 43 commands, up from 18. Isolated in its own commit if it needs to be dropped. - Panel entry: direct hash only. The 7-tap-on-version entry has no anchor: no screen currently displays a version line. Adding one to ProfileSettings would be new user-visible UI, so it was left out; say the word if you want it.
- Hands-on findings folded in: the only boot hints pointed at SUB-helps (
lantern.spoof.venue.help()), neverlantern.help()itself;debug.auth.*summaries were just the legacy binding names (now real descriptions); PR preview builds registered zero commands because'preview'was missing from the env allowlists (now included).
Heritage: builds on #367 (closed), which consolidated the scattered window.* debug helpers into the window.lantern.* registry (apps/web/src/lib/lanternDevtools/index.js). This spec is about making that registry pleasant to discover and usable from a phone.
Problem โ
Verified via headless chromium against https://dev.ourlantern.app (2026-08-03):
- Nothing announces the tooling.
lantern.help()is mentioned only in code comments. A dev opening the console sees scattered boot logs, none of which say "typelantern.help()". You have to already know. help()buries its own output. It prints a clean grouped list, then returns the raw registry array, so the console immediately dumps[{...}, {...}, ...]noise right under the pretty text.- No argument hints. Help shows
lantern.spoof.location.setStatus()with a one-line summary but no clue what arguments it takes. Usage examples live only in scattered one-off boot logs and per-namespacehelp()subcommands. - Deployed dev only has 18 of ~51 commands. Everything registered inside
if (import.meta.env.DEV)inmain.jsx(seeders, cache utils, venue create/enrich/cleanup, area staleness) exists only on a local Vite server.import.meta.env.DEVis false in the deployed dev build, even though the registry's own env allowlist (DEFAULT_ENVS) was designed to make exactly this distinction. On-the-go testing happens againstdev.ourlantern.app, where the most useful helpers are absent. - Mobile browsers have no console at all. The entire
lantern.*surface is unreachable from a phone, which is where venue/location testing actually happens.
Goals โ
- A first-time dev (or the operator on a new machine) discovers
lantern.help()without being told. - Help output shows what each command takes, not just what it does.
- Every registered command is runnable from a mobile browser on
dev.ourlantern.app, no cable or remote-debugging session required. - Production stays locked out exactly as today (registry env allowlist; prod exposure remains explicit opt-in per command).
Non-goals โ
- No generic remote-debugging bridge (no chrome://inspect replacement, no network tab). If we ever want raw logs on device, an eruda toggle is a small later add (see matrix).
- No changes to what the commands do, and no new commands beyond what the panel needs.
- No prod-facing UI. The panel ships dev-env-only.
Recommendation โ
Option B: polish help() + a registry-driven in-app dev panel. The registry already knows every command, its grouping, its summary, and its env gating; rendering it as a tap-to-run screen is the natural payoff of the #367 consolidation, and it solves mobile properly (typing lantern.spoof.location.set(37.77, -122.41) on a phone keyboard is not a solution). Eruda would bolt on a generic console without solving tap-to-run, so it stays a possible later add inside the panel, not the foundation.
| Option | What it does | What it affects | Risks / implications |
|---|---|---|---|
| A: help polish only | Boot banner, arg signatures, clean return, help('spoof') filter | lanternDevtools/index.js + register call sites | Cheap, but mobile stays unsolved; desktop-only win |
| B: A + registry-driven dev panel (recommended) | Everything in A, plus a dev-only #/devtools screen listing commands by group, tap-to-run, arg inputs, output display | Same as A + one new screen, route, story, lazy chunk | Most build effort of the three; needs arg metadata on ~51 registrations; payoff is on-the-go testing on any device |
| C: B + embedded eruda toggle | B, plus lazy-load eruda from the panel for a full console/network/DOM inspector on device | B + one lazy dev-only dependency | Third-party dep (~100KB, lazy). Real diagnostic value (network tab on phone), but generic UI; fine as a later increment |
Design โ
1. Help polish (lanternDevtools/index.js) โ
- Boot banner: one concise line at init in dev envs:
๐ฎ lantern devtools ready: N commands. Type lantern.help(). Replaces the need to already know; the scattered per-moduleโ Dev mode:lines get consolidated or demoted. - Clean return:
help()prints and returnsundefined. Programmatic access stays viagetRegistry()(and powers the panel). - Arg signatures:
register()gains an optionalusagestring (e.g.'set(lat, lng)') and optionalexample. Help renderslantern.spoof.location.set(lat, lng)instead of bare(). Registrations withoutusagerender as today, so this rolls out incrementally; the ~10 arg-taking commands getusagein this pass. - Filter:
lantern.help('spoof')prints just that group. No-arg behavior unchanged. - Alignment: compute pad width from the longest rendered path instead of the hardcoded
padEnd(44).
2. Close the deployed-dev gap (decision needed) โ
Move the main.jsx dev-only registrations from if (import.meta.env.DEV) to the registry's env allowlist (hostname/VITE_APP_ENV based, same detection the registry already inlines). Result: seeders, cache utils, and venue tools register on dev.ourlantern.app (and locally, as today) and stay locked out of prod via DEFAULT_ENVS. These commands mutate dev Firestore, which is what dev is for, but it widens who can invoke them from a browser on the dev site (dev is invite-gated and the URL is unlisted; commands still require the site to be open in your browser). Operator call: without this, the mobile panel on deployed dev shows only the 18 currently-registered commands and the panel loses most of its point.
3. Mobile dev panel (apps/web) โ
- Route:
#/devtools, added toApp.jsxhash routing. Direct navigation works (typeable on a phone). Secondary entry: 7 taps on the version line in the profile/settings screen (the Android developer-mode homage), which just navigates to the hash. - Gating: the screen component lazy-loads (
import()) only when the route matches AND the registry env check passes (sameappEnvresolution as the registry). In prod the route renders nothing and the chunk never loads. Prod-safe commands (forceRefresh,version) are deliberately NOT enough to mount the panel in prod; prod keeps the existing documentedwindow.lanternForceRefresh()path. - UI: groups as collapsible sections mirroring
help()grouping; each command row shows dotted path + summary; tap runs it. Commands withusagemetadata render labeled text inputs (comma-split args, JSON-ish coercion: numbers, booleans, quoted strings). Output area shows the command's return value (pretty-printed JSON) and anything itconsole.logs during execution (temporary console tee while a command runs). Design-system tokens throughout (design skill before styling); colocated Storybook story (enforced). - Safety valve: rows for commands whose summary starts with a destructive verb (clear/cleanup/strip/force) get a confirm tap (tap once to arm, tap again to run). Cheap, prevents fat-finger seed-wipes on a phone.
4. Testing โ
- Unit:
usage/exampleregistration + help rendering (extendlanternDevtools.test.js), filter behavior, clean return. - Component: panel renders registry groups, runs a mock command, arg coercion cases, destructive-confirm arming.
- Manual: headless chromium against a local build for the panel route; operator on-device pass on Android for the tap-to-run flow (headless signed-in flows on deployed dev stay blocked by App Check, so the on-device pass is the real verification).
Rollout โ
Single PR (one session, one theme): help polish + gating move + panel, logical commits, draft until EOD mark. File one tracking issue on approval; PR closes it. No prod implications (dev-env-only surface).
Open questions (operator) โ
- Approve Option B as scoped? (Or trim to A / extend to C.)
- Section 2 gating move: expose the
import.meta.env.DEV-only helpers on deployed dev, yes or no? - Panel entry points: is
#/devtools+ 7-tap on version both, or direct hash only?