7.3 KiB
Phase 03 — Device-Matrix Exit Gate (ROADMAP Success Criterion 5)
Deployed origin (staging): https://araharah-bot.araharah-bot.workers.dev/
Deployed version: 2c1336e3-58f9-4b0b-86a8-b25de66e327c (via npm run deploy — theme → web → wrangler deploy with web/dist as static assets, D-13/D-14/D-15)
Status: PENDING REAL-DEVICE VERIFICATION — the phase does not close until every leg has an explicit verdict.
Automated half (already green)
npm test -w web— 33 passed, 0 failed (includes BA1–BA6 build-artifact gates overweb/dist, HTML included)npm test -w bot— 48 passed, 0 failednpm run capture -w web— CAPTURE OK, 7/7 non-blank captures offweb/dist-harness, including the Desktop denied card, the?pin=1chat-pin relay, light + dark live shots, and the DSGN-02 side-by-side. GEO-03 runtime probe: localStorage / sessionStorage / cookies / IndexedDB all empty after a real boot-to-live cycle.- Live canaries at the deployed origin:
GET /→ 200 text/html from the asset binding;POST /api/session→ 401 for unsigned bodies (the HMAC gate is live);getWebhookInfoURL ends/telegram/webhookwith nolast_error;getChatMenuButtontype =commands.
Bot API 10.2 origin prerequisite (hard, blocking)
wrangler.jsonc's run_worker_first key drew an "Unexpected fields" warning from wrangler
4.151.3, but the deployed behavior was verified request-by-request: assets serve non-API
paths directly, /api/* and /telegram/* are answered by the Worker code (401/500, not
asset fallback) — D-13 routing holds in practice; the warnings are cosmetic at this wrangler
version.
- BotFather Mini App URL == the deployed origin above. Bot API 10.2 rejects Mini App
calls from any other origin silently — a mismatch means
Telegram.WebApp.*no-ops with no error. Check menu button (/setmenubutton) / WebApp URL in @BotFather (arasarah-bot.araharah-bot.workers.dev), not a localhost or Pages URL. Observed / Symptom: ______________________________________
The six device legs (each: an explicit verdict + observation)
1. Android — real phone, Telegram Android (GEO-01)
- Boot: BOOT-01 (validating session) → BOOT-02 (acquiring location) → LIVE; the position marker renders on the Rams basemap.
- Permission persistence: close the app fully, reopen — no second permission prompt. The grant lives in Telegram's per-bot store on the device, outside the WebView. Observed / Symptom: ______________________________________
2. Android stale-fix check (retires or confirms assumption A1)
- With the app LIVE, walk several metres and re-check. If the pin does not move, toggle
the phone's Location setting once (off → on) and record whether
navigator.geolocationthen delivered a fresh fix, and whether the FIRST Android verification needed that toggle. Result decides: A1 stands (toggle required) or A1 retired (no toggle needed). Observed / Symptom (needed toggle? y/n): ______________________________________
3. iOS — real phone, Telegram iOS (if available)
- Boot to LIVE; marker renders; permission grant persists across a reopen.
Observed / Symptom: ______________________________________
(If no iOS device is available: record
UNVERIFIED — no device; Success Criterion 5 stays open. Do not close the phase anyway.)
4. Desktop Telegram (GEO-02 end-to-end)
- Geolocation is absent by design (issue #77): the app shows the denied card with identical copy across all clients (D-06, RESEARCH Pitfall 4) — no client-specific variant.
- Tap ASK VIA CHAT → POSTs
/api/location-fallback; the location keyboard appears in the user's own chat; tap it, share a live pin; the app picks it up viaGET /api/location(within its poll window) and reaches LIVE. This is GEO-02 end-to-end. Observed / Symptom: ______________________________________
5. Plain browser (no Telegram context)
- Open the deployed URL directly in a desktop browser. Expected: the denied card
renders over the Rams shell (no
initData→ the HMAC session gate 401s), NOT a blank page, NOT the live branch. A blank page is a FAIL. (Optional extra observation: if a browser geolocation prompt leads to LIVE, note it — but the pass condition for this leg is the deliberate denied state.) Observed / Symptom: ______________________________________
6. Theme flip mid-session (PX-02 — manual-only verification)
- With the app LIVE on a real client, switch the Telegram theme light ↔ dark mid-session without closing the app. Confirm: (a) shell chrome re-slots (document background, shell ink swap); (b) the status strip stays matte-black; (c) the map is unchanged — same style, no flash, no camera jump (D-10 / RESEARCH Pitfall 7). Record a before/after screenshot in each direction and name the client used (this leg can be folded into any of legs 1/3/4). Verdict (chrome re-slotted? strip stayed black? map unchanged?): ______________________________________
How to report back
- Type "device matrix passed" if all six legs reached LIVE or the documented denied-and-recovered state (the theme-flip leg counts as passed when the chrome re-slotted mid-session and the map was visibly unchanged).
- Otherwise report which leg failed and the exact symptom — a
/gsd-debuggets opened for it. - If no physical Android/iOS device is available: report that. The honest outcome is Success Criterion 5 = UNVERIFIED; the phase is held open with the blocker recorded and either a device acquired or the matrix explicitly deferred to Phase 7 hardening with GEO-01/GEO-02 noted as unverified on real hardware. Never close the phase claiming device verification that did not happen.
Local-dev note (D-12 / assumption A3)
localhost:8787 (via root npm run dev → wrangler dev --port 8787 --local) is a secure
context, so navigator.geolocation works under wrangler dev while a LAN-IP dev URL would
not. Remote staging (the deployed origin above) is used sparingly, immediately before a
real-device verification session, for exactly that reason.
2026-10-23 debug round 1 (mid-gate failure fixes)
Reported failures + fixes (commit b1d8768, deploy a21c0816):
- No webview entry from the bot — root cause: phase 2 armed the menu button as
type
commandsand phase 3 never armed a web_app entry. Fix: newbot/scripts/set-menu-button.mjs→setChatMenuButtontypeweb_app, url = deployed origin (verified live viagetChatMenuButton). ⚠ Client-cached: the menu surface needs an app restart //-autocomplete to refresh (same quirk as phase 2). - TRY AGAIN / ASK VIA CHAT dead on one card — root cause: only the first
denied-card instance had listeners; the
awaiting-pinvariant is a secondbuildDeniedCardinstance (03-03 recorded stub). Fix:wireDeniedActionswires BOTH instances inweb/src/main.ts. - Post-fix canaries:
GET /200, unsigned/api/session401, webhook armed (/telegram/webhook, no last_error), menu button verifiedweb_app.
Notes for the re-run: on plain Chrome (no Telegram), TRY AGAIN legitimately re-renders the denied card — session can never validate without initData there; the pass condition for leg 5 is the deliberate denied state itself. If Android still shows dead/absent retry interplay after OS location is enabled, open a follow-up.