Files
arah2/.planning/phases/07-hardening-private-launch/07-01-SUMMARY.md
T
araharah-executor 99e28de6a1 docs(07-01): complete fullscreen capability layer plan — SUMMARY
- 4/4 tasks, 157/157 web tests, typecheck clean; PX-04 fullscreen + safe-area re-resolution shipped
2026-10-25 03:47:47 +08:00

104 lines
7.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
phase: 07-hardening-private-launch
plan: 01
subsystem: ui
tags: [telegram, fullscreen, bot-api-8.0, safe-area, capability-guard, px-04]
# Dependency graph
requires:
- phase: 03-shell-map-mini-app
provides: the TelegramWebAppLike contract + getWebApp() one-shot accessor; the boot() choreography with the boot-time applySafeArea(resolveSafeInsets(getWebApp())) line
- phase: 04-destination-search-kl-gazetteer
provides: the homescreen.ts capability-guard shape (BOTH halves: method presence AND isVersionAtLeast("8.0")) copied verbatim into fullscreen.ts
- phase: 06-turn-by-turn-guidance-live-driving
provides: activateGuidance()/teardownGuidance()/resume()/stopGuidanceArtifacts() edges in route.ts with engageCloseProtection/releaseCloseProtection anchoring the wiring sites; the PX-03 safe-area resolve/apply pair
provides:
- web/src/telegram/fullscreen.ts — requestGuidanceFullscreen / exitGuidanceFullscreen / watchFullscreenEvents behind the both-halves 8.0 guard, every platform call in try/catch
- TelegramWebAppLike grown with five OPTIONAL members: requestFullscreen/exitFullscreen/isFullscreen/lockOrientation/unlockOrientation (8.0+) — no existing member reshaped
- watchSafeAreaChanges in safe-area.ts — insets re-resolve through the union authority on safeAreaChanged/contentSafeAreaChanged
- route.ts wiring: request at both guidance-start edges (fresh + resumed), exit at both teardown edges (trip end + walk-back), watchFullscreenEvents registered exactly once at the guidance-start edge
- main.ts boot-site call: watchSafeAreaChanges(getWebApp()) directly after the boot-time apply line
- web/test/fullscreen.test.mjs — FS1..FS9 covering the guard, refusal path, old-client fallback, events, and re-resolution
affects: [07-02 (failure-state matrix), 07-03 (_headers), launch-gate verification, any future orientation/fullscreen work]
# Tech tracking
tech-stack:
added: []
patterns:
- "both-halves capability guard (method presence AND isVersionAtLeast(\"8.0\")) before every platform call, try/catch on each call, boolean best-effort return the caller ignores"
- "one-authority re-resolution: event handlers re-run the SAME resolveSafeInsets union instead of duplicating inset math"
- "observability-only event registration: no-op callback, try/catch-wrapped observer, category-only (no logging) per D-14"
key-files:
created:
- web/src/telegram/fullscreen.ts
- web/test/fullscreen.test.mjs
modified:
- web/src/telegram/webapp.ts
- web/src/telegram/safe-area.ts
- web/src/mock/telegram-mock.ts
- web/src/route/route.ts
- web/src/main.ts
key-decisions:
- "Fullscreen is requested on the SYNCHRONOUS guidance-start transition (activateGuidance / resume), never an async callback — the transient user-activation window is exactly there (RESEARCH Pitfall 1); the boolean result is deliberately ignored at every call site"
- "Orientation handling uses Telegram's own lockOrientation/unlockOrientation (locks the CURRENT orientation only); the DOM Fullscreen API and generic screen.orientation.lock are banned and pinned out by the FS9 source-scan"
- "watchFullscreenEvents registered exactly ONCE, in activateGuidance only — not in resume() — so a guidance session registers one watcher; the callback is a deliberate no-op (events are observability, never correctness)"
- "The safe-area watch call in main.ts uses the same one-shot getWebApp() as the adjacent apply line rather than the later const wa binding (avoids a forward reference; getWebApp() is idempotent)"
patterns-established:
- "Capability wrapper triad (request/exit/watch) mirrors the close-protection engage/release pairing on the same lifecycle edges"
- "Mutation-check discipline: the guard tests were verified to bite by temporarily neutering the 8.0 guard (FS2/FS5 failed under mutation, restored, green again)"
requirements-completed: [PX-04]
# Metrics
duration: ~25 min
completed: 2026-10-25
---
# Phase 7 Plan 1: Fullscreen Capability Layer Summary
**Telegram-native fullscreen (Bot API 8.0 capability-guarded) at the guidance-start edge with safe-area re-resolution on rotation/fullscreen — a refusal is a silent fallback, never a guidance failure.**
## Performance
- **Duration:** ~25 min
- **Tasks:** 4/4 completed
- **Files modified:** 7 (2 created, 5 modified)
## Accomplishments
- **Task 1** — `TelegramWebAppLike` grew with the five 8.0-optional fullscreen members (appended after `disableClosingConfirmation?`, before `onEvent?`; no existing member changed shape) and the new `web/src/telegram/fullscreen.ts` exports exactly `requestGuidanceFullscreen` / `exitGuidanceFullscreen` / `watchFullscreenEvents`, each behind the BOTH-halves guard (method presence AND `isVersionAtLeast("8.0")`), each platform call inside its own try/catch. Zero logging (T-07-09), no DOM-fullscreen and no generic orientation lock (T-07-10).
- **Task 2** — `watchSafeAreaChanges(wa)` in `safe-area.ts`: registers `safeAreaChanged` and `contentSafeAreaChanged` handlers that each re-run `applySafeArea(resolveSafeInsets(wa))` — the same union authority, never a duplicated computation — wrapped in try/catch (T-07-06b). `resolveSafeInsets`/`applySafeArea` behaviour untouched; no storage writes.
- **Task 3** — the harness mock satisfies the grown contract in the same change (counters + `__isFullscreen` state; version comes from the `?mock=` profile, never hard-coded), and `web/test/fullscreen.test.mjs` landed FS1–FS9: guard halves (FS1/FS2), exact-once call (FS3), throwing-platform refusal (FS4), the exit mirror (FS5), event categories + throwing-observer containment (FS6), the old-client fallback (FS7/SC2.e), safe-area re-resolution with a finally-restored document stub (FS8), and the comment-stripped source-scan ban (FS9) asserting the banned APIs are absent AND the 8.0 guard is present.
- **Task 4** — wiring: `requestGuidanceFullscreen` at both guidance-start edges (`activateGuidance` + `resume`, directly after `engageCloseProtection`), `exitGuidanceFullscreen` at both teardown edges (`teardownGuidance` + `stopGuidanceArtifacts`, directly after `releaseCloseProtection`), `watchFullscreenEvents` registered exactly once at the guidance-start edge with a deliberate no-op callback, and `watchSafeAreaChanges(getWebApp())` called at the `main.ts` boot site directly after the boot-time apply line. No new logging anywhere.
## Verification
- `npm run web:typecheck` — exit 0.
- `npm test -w web` — **157 passed, 0 failed** (148 baseline + 9 new FS cases); FS1–FS9 all PASS.
- All plan acceptance greps exact: `requestGuidanceFullscreen(` = 2, `exitGuidanceFullscreen(` = 2, `watchFullscreenEvents(` = 1, `engageCloseProtection(` = 2, `releaseCloseProtection(` = 2 in route.ts; `watchSafeAreaChanges(` = 1 in main.ts; anti-pattern grep in fullscreen.ts = 0; `console\.` = 0.
- Mutation check: neutering the version guard made FS2/FS5 fail (the tests pin the guard), file restored, suite green again.
## Deviations from Plan
None — plan executed as written. (One cosmetic self-correction during task 1: the fullscreen.ts header comment initially named the banned API strings verbatim, which would have tripped the plan's plain-grep `documentElement.requestFullscreen|screen.orientation.lock = 0` acceptance criterion; reworded to "the DOM Fullscreen API plus the generic screen-level orientation lock" — FS9's comment-stripped scan is unaffected since it reads the code, not the prose intent.)
## Auth Gates
None.
## Known Stubs
None — no placeholder values, no unwired data paths. The `watchFullscreenEvents` no-op callback is a DESIGN decision (RESEARCH Open Question 1: the events are observability, never correctness), not a stub.
## Self-Check: PASSED
- FOUND: web/src/telegram/fullscreen.ts
- FOUND: web/test/fullscreen.test.mjs
- FOUND: af0c0e9 (task 1 — contract + wrapper)
- FOUND: 95ad3ea (task 2 — safe-area watch)
- FOUND: aec8b07 (task 3 — mock + FS suite)
- FOUND: 5b124e0 (task 4 — edge wiring)