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

7.9 KiB
Raw Blame History


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)