Glasses bring-up — handoff
Everything for the JQ-on-Mentra-Live glasses was built + merged the day the
hardware arrived (2026-07-17). This doc is the pick-up point: what's done, what's
left, and the one real unknown. Task: product_tasks "Glasses on-device bring-up
- multi-user validation" (P1).
State: built + merged (nothing left to code for v1)
| PR | What | Migration |
|---|---|---|
| earlier | AisleAsk glasses: scan aisle, scan recipe, add-item, recap, next, got-it | — |
| #1338 | Durable device token (hjg_…, never expires) | 511 |
| #1339/#1340 | Mantras: dashboard + "play my mantras" in-ear | 512 |
| #1342 | Multi-user device links: backend + dashboard | 513 |
| #1343 | Multi-user runtime: resolve-per-session + "link my glasses" | 513 |
| 2026-07-23 | SecondSet: job binding (companion/QR "scan job") + owner→tech async voice notes — INERT unless SECONDSET_ENABLED. Backend services/glasses-secondset.ts + glasses-qr.ts (sharp→jsqr) + routes/glasses.ts; glasses secondset.ts. Consent gate = active binding. Details: docs/ai/INDEX.md → "SecondSet". | 563 (human-provisioned) |
All migrations 500–513 applied to prod (owner did this). App builds clean
(apps/glasses, npm run build → dist/index.js). Tests: npm test → 39/39.
Voice command set (single app = JQ): nudges ("what's my next step"), levers ("log a 20-minute walk for energy"), soundtrack ("play some music"), AisleAsk (add / next / got-it / recap / scan this aisle 📷 / scan this recipe 📷), Mantras ("play my mantras"), linking ("link my glasses <code>"). Full table: apps/glasses/README.md.
Remaining: on-device only (no more coding for v1)
1. Finish the Railway service
hivejournal-glasses is created + GitHub-connected (CLI), with the 2 non-secret
vars set. In the Railway dashboard, finish:
- Settings → Config-as-code path =
apps/glasses/railway.json(root stays/). - Variables → add
MENTRA_API_KEY+HIVEJOURNAL_TOKEN(the durable token, below). For multi-user, alsoGLASSES_APP_SECRET. - Networking → Generate Domain → copy the URL. Guide: docs/setup-guides/GLASSES_RAILWAY_DEPLOY.md. (The first auto-build fails until the config path is set — expected.)
2. Mentra console
console.mentra.glass → the app (fm.graphene.jq) → set server URL to the
Railway domain; confirm Microphone + Camera permissions (camera = the scans).
3. Single-user smoke
Mint a durable token at /dashboard/glasses → "🔑 Connect this device" → set as
HIVEJOURNAL_TOKEN. Prereqs or you'll hear fallbacks: a JQ voice clone
(/dashboard/lovio/voice-setup) + an AisleAsk list (/dashboard/aisleask).
Button-press → nudge. Then run every voice command.
4. Multi-user validation (the real test)
Set GLASSES_APP_SECRET (same value on the glasses server + HJ). Link a second
wearer: owner mints a code at /dashboard/glasses → "👥 Link someone else's
glasses", the wearer says "link my glasses, code …". Then verify:
- Wearer A ≠ Wearer B — each hits their OWN account (nudges from their dream, their AisleAsk list, their mantras).
- ⚠️ THE ONE UNKNOWN: that
AsyncLocalStoragecontext propagates through Mentra's event handlers on real hardware. The per-session token lives in an ALS context wrapped around the button/transcription handler bodies (session.tsrunWithSessionToken); the client reads it inauthHeaders(hivejournal.ts). I'm confident ALS carries through the promise/timer chains, but "does it survive the SDK's event dispatch on-device" is unverified.- If it does NOT propagate: the fix is small — capture the session token
explicitly in the handler closures and pass it to the client calls (add an
optional
token?param to the client fns) instead of relying on ALS. The ALS indirection is the only thing that would need replacing; everything else (resolve/link/token) is unaffected.
- If it does NOT propagate: the fix is small — capture the session token
explicitly in the handler closures and pass it to the client calls (add an
optional
Single-user is byte-for-byte unchanged whether or not multi-user works
(unset GLASSES_APP_SECRET → static-token path).
5. On-device tuning (all built blind)
- Intent misfires/misses → tighten the regexes in
intent.ts(or enable Sophia). Note what you said that mis-routed. - Photo —
requestPhoto({size, compress})values (mediumsigns /largerecipes) are untested on the real camera; adjust for legibility vs transfer. - Transcription finality — we act on
isFinalonly; if laggy/double-firing, that's the spot. - Soundtrack ducking — duck-under-nudge timing is untested live.
Pointers
- App + architecture: apps/glasses/README.md.
- Multi-user design + the ALS caveat: GLASSES_MULTIUSER_AUTH.md.
- Deploy: GLASSES_RAILWAY_DEPLOY.md.
- Store strategy (why multi-user was the gate):
product_tasks"Mentra store distribution".