# Phase 06.6: UAT accounts, integer viewport scaling, chat region clamp, right-click menu - Discussion Log

> **Audit trail only.** Do not use as input to planning, research, or execution agents.
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.

**Date:** 2026-05-16
**Phase:** 06.6-uat-accounts-integer-viewport-scaling-chat-region-clamp-right-click-menu
**Areas discussed:** UAT seed mechanism, Integer scaling fix, Chat overlay clamp, Right-click menu / left-click fate

---

## UAT Account Seeding

| Option | Description | Selected |
|--------|-------------|----------|
| Idempotent migration (run-migrations) | Add a migration that INSERTs the two accounts if absent. Persists across SQLite/Litestream restores. Passwords hardcoded or from env. Lives in run-migrations chain. | (Claude's discretion → chose this) |
| Standalone seed script (npm/pnpm task) | apps/server/scripts/seed-uat-accounts.ts — operator runs once per env, also runnable in Playwright globalSetup. Not part of normal migration chain. | |
| Real account via Better-Auth signup at test setup | Playwright globalSetup hits /api/auth/sign-up directly each test run if 404. Idempotent; no migration file. Closer to user behavior; slower than DB seed. | |

**User's choice:** "agent decides using best judgment. note that playwright should be configured to use these two accounts from now on, while operator uses uat_a and uat_b"
**Notes:** Critical constraint surfaced — Playwright must switch FROM `dev-bypass:uat_a/uat_b` synthetic tokens TO real login with `dunsen_uat`/`rebbie_uat`. Operator keeps using `uat_a`/`uat_b` dev-bypass for manual UAT. Goal = avoid account-id collision when operator + Playwright run against staging simultaneously. Idempotent migration chosen: Litestream-restore-safe, zero manual step, parallels existing migration pattern, no Playwright-side latency cost.

---

## Integer Viewport Scaling

| Option | Description | Selected |
|--------|-------------|----------|
| Cap MAX_ZOOM lower (operator picks 1x/2x/3x) | Replace MAX_ZOOM with floor(min(winW/640, winH/480)) so canvas stays integer but never exceeds. Letterbox enlarged. | |
| Bug — current setup produces non-integer scale somewhere | Observed fractional zoom. autoRound only floors rendering, not zoom. Need manual integer compute + setZoom on resize. | ✓ |
| Lock to fixed integer once (no live re-scale on resize) | Pick best integer zoom at boot, ignore resizes. Stable layout; removes resize listener. | |

**User's choice:** "current game viewport scales to fit the window perfectly. it is not an integer scale of 640x480"
**Notes:** Confirms Phaser's `Scale.FIT + autoRound:true + MAX_ZOOM` combination yields fractional fit despite the autoRound name (autoRound rounds rendered coords, not the zoom factor itself). Fix per D-06: switch mode to NONE/RESIZE, compute integer zoom manually, re-apply on every resize via the existing resize listener at `main.ts:49-51`.

---

## Chat Overlay Clamp

| Option | Description | Selected |
|--------|-------------|----------|
| Match canvas rect dynamically (resize-aware) | On resize/zoom-change, read game.canvas.getBoundingClientRect(), set ChatHUD container left/top/width/height. Always lives directly over canvas. | ✓ |
| Match canvas rect once at boot only | Compute once after Phaser READY, set static position. Resize behavior = chat stays put. Simpler; jarring if window resizes. | |
| CSS-only via canvas wrapper sibling element | Add wrapper around #game-root that constrains to canvas dimensions; chat HUD as sibling inside wrapper. No JS rect-tracking. May need observer for Phaser scale changes. | |

**User's choice:** Match canvas rect dynamically (resize-aware)
**Notes:** Couples cleanly with the new D-06 manual setZoom resize callback — single source of truth for "the canvas just resized, update overlays". Must NOT violate the HARD mount-point invariant from `ChatHUD.ts:7-14` / ADR 0008 (overlay stays under `#dom-overlay`, never inside `#game-root`).

---

## Right-Click Menu — Left-Click Fate

| Option | Description | Selected |
|--------|-------------|----------|
| No-op (reserved for future click-to-move/interact) | Left-click does nothing in GameScene. Keeps slot open for Phase 7 (click-to-walk, NPC click). Menu only via right-click or ESC. | ✓ |
| No-op now; document as reserved in code comment | Same behavior; add `// reserved for Phase 7` comment at dead handler. | |
| Remove left-click pointerdown handler entirely | Strip the registration. New right-click handler added cleanly. Cleanest diff. | |

**User's choice:** No-op (reserved for future click-to-move/interact)
**Notes:** No code comment per CLAUDE.md "Default to writing no comments" rule. Reservation captured in CONTEXT.md D-16 instead.

---

## Claude's Discretion

- Migration file name + exact env var name (likely `UAT_TEST_PASSWORD` — researcher to grep existing CI workflow for established convention before naming).
- Whether to extract a small `useCanvasRectTracker` helper or inline tracker logic in ChatHUD.
- Whether to listen on `game.scale.on('resize')` vs `window.addEventListener('resize')` for the canvas-rect re-clamp (pick whichever fires reliably after the new manual setZoom call).

## Deferred Ideas

- **Settings panel** in EscMenu — already deferred to Phase 7.
- **Click-to-walk / pathfinding** on left-click — D-16 reserves; belongs in Phase 7 PAR-* row.
- **Touch / mobile input** — out of scope (Chrome desktop only).
- **Per-account UAT pool growth** beyond 2 Playwright accounts — defer until Playwright actually shards.
- **Smoke flake** (`waitForGameReady` 15s timeout) — pre-existing CI issue tracked separately; researcher to flag if D-01 real-login could regress it.
</content>
</invoke>
