GENERATED VIEW · PRIVATE ARCHIVE · DO NOT SERVE
RobCo Industries · Archive Museum

DESIGN_OVERHAUL_BUILD_PLAN.md



RELEASE

planning/2.8.0/plans/DESIGN_OVERHAUL_BUILD_PLAN.md

sha256 94d04f8277ed00e1 · 55919 bytes · original held in the private archive

# DESIGN_OVERHAUL_BUILD_PLAN.md — RobCo U.O.S. Total-Transformation Build Plan > **PLAN ONLY.** Nothing here is implemented, committed, or pushed. Lives in gitignored > `planning/`. Turns the owner-approved **adopted scope** of `DESIGN_OVERHAUL_SLATE.md` into a > concrete, dependency-ordered build program. Grounded in live `dev` @ `cd8a824` > (v2.7.0, cache `robco-terminal-v2.7.0-r38`, 1903 tests / 157 suites). Every file/line/function > named below was read out of the actual code this session, not imagined. > > **Supersedes/absorbs:** `STEP2_PHASE2_PLAN.md` Chapters C–F for the identity + visual work > (§6 below maps the restructure exactly). Chapters A (Ambient Runtime A1–A3) and B (B1 command > mode, B2 Module Bay) are **already shipped** and are consumed, not rebuilt. --- ## 0. LOCKED DECISIONS + RECONCILIATIONS (bake these in before anything) These are owner-locked and **override the slate wherever they differ.** ### 0.1 Brand - Keep **RobCo U.O.S.**, reframed as *"the Unified OS that runs on each game's machine."* - **NV** = a **salvaged RobCo desk terminal** wired into a Pip-Boy uplink (scavenged, amber-native). - **FO3** = a **Pip-Boy 3000** strapped to the wrist (Vault-Tec, dull `green3`, dented). - **FO4** = a **Pip-Boy 3000 Mark IV** — a *refined evolution of the FO3 Pip-Boy.* ### 0.2 ★ FO4 RECONCILIATION (supersedes the slate) The slate's §0.1/§3/§B-3/§15.2/§16 describe FO4 as an **Institute holo-terminal** (floating glass, holographic panes, synthetic-blue, "lattice" structural mode). **That is DROPPED.** FO4 is now the **Pip-Boy 3000 Mark IV** — Pip-Boy family, not Institute. Consequences the build must honor: - The "structurally different — holographic panes vs. boards" proof-of-Unlock-3 in the slate **no longer rides on FO4.** Structural per-game difference is carried by **NV (desk-terminal + uplink framing) vs. FO3/FO4 (wrist-device framing)** and by the `identity.structuralMode` hook remaining data-driven and open for future use — not by a holographic renderer. - The `identity.structuralMode` field is retained (values e.g. `'bay'`), but there is **no `'lattice'` build this cycle.** FO4's `identity` entry describes a *cleaner, refined Pip-Boy* (Mark IV casing, crisper phosphor), not projected light. - **FO4 is DESIGN-ONLY this cycle** — its `identity` data entry exists and validates; it is redesigned/refined and *built* at 3.0. Everywhere the slate says "FO4 holographic assembly boot," read "FO4 = Mark IV Pip-Boy, spec-only, 3.0." ### 0.3 The Unlock Charter (in force) AI form is reimaginable · navigation is an experience, not tabs · per-game differs structurally · first-boot/game-switch is a ceremony · the app is a physical device · break webapp conventions — **with the one HARD guardrail: the PWA must not break** (installable / offline / service-worker / standalone all keep working). Every unit below re-states its PWA-intact posture. ### 0.4 Held constraints (design *within* these — non-negotiable) - **Player authority** — AI opt-in, never authoritative over durable state; every atmospheric idea writes **nothing** to the campaign save. - **Free + offline + PWA-intact** — no paid infra, bring-your-own key, airplane-mode capable. - **The campaign save/data layer is SACRED** — the `robco_v8` container + `migrateState()` / `sanitizeImportedContainer()` path + every save/cloud mechanic are skinned, **never altered.** The visual overhaul never touches the atmosphere↔save boundary. - **Mobile AND desktop both fully work** (they may — and should — differ). - **Accessibility floor holds** — reduced-motion, AA contrast per machine, ≥16px inputs, ≥28px targets, literal ARIA under every diegetic skin. ### 0.5 Test churn is EXPECTED and fine The order is **mockup → build → adapt tests to new code.** UI-contract suites (render markup, CSS invariants, tab/nav structure, panel IA) will be rewritten to match the new UI. The full gate still passes on every commit; both runners stay at parity (Protocol 15); counts sync everywhere (Protocol 2a). "The tests changed" is a normal outcome of a redesign unit — a *broken* gate is not. ### 0.6 Honored CUT-LIST (do NOT plan these — from slate §15) Day/night / dusk-dawn (killed — weather/rad-storms only) · FO4 as anything past design-only · re-opening the Module Bay's internal board/chip/dial design · per-game fonts · a rigged 3D avatar (Overseer stays a **canvas waveform**) · device-tilt on the critical path (off-by-default chip only). --- ## 1. THE KEYSTONE-FIRST ARCHITECTURE (why `identity` is built first) Today per-game identity is **data-driven but shallow**: `GAME_DEFS[ctx].theme` (`state.js:849–1060`) carries only `defaultOptics` / `framing` / `pipBoyModel` / `bootFlavor` / `saveLabel`, and `.ai` carries directive text. `window.THEMES` (`state.js:796–847`) holds the optic palette. **Crucially, the `data-game` root attribute is NOT set anywhere** — grep of `index.html` / `js/*` / `css/*` finds zero `[data-game]` usage. GT-4 is unbuilt. The architecture is *ready* for deep identity; almost none of it is *spent*. **The keystone:** widen `theme` into a single `GAME_DEFS[ctx].identity` block that owns everything per-machine (material, ceremony, persona, motion texture, cursor, structural mode, audio params, voice/empty-state banks, ambient banks), and set `data-game` on `<html>` at boot + on switch. Then **every later chapter is a *consumer*** of `identity` — one data block per machine → the whole site re-skins coherently, zero feature-code game branching (Protocol 38). Get this wrong and the overhaul fragments into scattered `ctx === 'FO3'` literals; get it right and NV/FO3/FO4 are three reads of one table. ``` GAME_DEFS[ctx].identity ← THE SPINE (DO-K, built first) │ ├─ material/bezel → DO-N bezel chrome ([data-game] CSS) ├─ persona → DO-O Overseer presence (waveform texture + cadence + blip bank) ├─ ceremony → DO-C cartridge-swap + cold-start ├─ motionTexture → DO-Q2 motion-verb grammar (SEAT/SWEEP/WAKE/FAULT/BREATHE tuning) ├─ audio → DO-Q4 per-game sonic identity (synth param block) ├─ cursor → DO-Q3 custom cursor ├─ voice/empty → DO-Q5 empty/loading voice banks └─ ambient → DO-W living-world banks (broadcasts/news/weather copy) Ambient Runtime (shipped) ← THE HEARTBEAT: DO-W living world + DO-O idle presence are observers ``` --- ## 2. BUILD SEQUENCE — chaptered units, dependency-ordered **Naming:** `DO-*` = Design Overhaul units. **Model column** follows Protocol 8 (Opus plan/audit, Sonnet implement, adaptive escalation). **Mockup column** flags the Fable-mockup requirement (§7). ### The spine (committed) | # | Unit | Depends on | Mockup | Model | |---|---|---|---|---| | **DO-K** | **Identity keystone** — `GAME_DEFS[ctx].identity` data block + `data-game` root attribute + FO4 stub entry | — (FIRST) | **No** (data spec) | Opus design + audit; Sonnet impl | | **DO-N** | **Bezel chrome + subsystem nav** — device body around the CRT, tab bar → illuminated bezel subsystem selector. **NV-tuned. Vertical-slice part 1.** | DO-K | **Yes (heavy)** | Opus design; Sonnet impl; Opus audit | | **DO-O** | **Overseer presence** — phosphor-waveform AI face (idle/think/speak) + thinking-as-event. **NV persona. Vertical-slice part 2.** | DO-K, DO-N | **Yes** | Opus design; Sonnet impl; Opus audit | | **✅ SLICE GATE** | **Prove ONE machine (NV) end-to-end** — bezel + nav + Overseer + the OPERATOR subsystem fully working @ 360/412/desktop before replicating | DO-N, DO-O | — | Opus audit | | **DO-C** | **Cartridge-swap game-switch + cold-start ceremony** — wrap the `onGameContextChange()` reload as an eject/seat; cold-start salvage boot | DO-K, DO-N | **Yes** | Opus design; Sonnet impl; Opus audit | | **DO-M** | **Per-game machines** — replicate the NV slice to **FO3** (material/persona/texture); author **FO4** `identity` + design spec (build 3.0) | DO-K…DO-C | **Yes (FO3)** + design doc (FO4) | Opus design; Sonnet impl; Opus audit | | **DO-W** | **Living-world layer** — deeds-as-news + rad-storm weather + intercept-at-Overseer, as Ambient-Runtime observers | DO-K, DO-O | Copy deck + light | Opus design; Sonnet impl | | **DO-P** | **Cohesion / polish** — motion-verb grammar applied site-wide; dress the remaining subsystems (§13.x); SYSTEM STATUS + hardware-life whisper | DO-K…DO-M | **Yes** per major surface | Sonnet; Opus reviews big surfaces | | **DO-G** | **Mobile casing** — the phone IS the device (edge-filling casing + slide-over subsystems), per machine | DO-N, DO-M | **Yes (mobile)** | Opus design; Sonnet impl; Opus audit | ### The quick-win sweep (interleaved early — §4) | # | Unit | Depends on | Mockup | |---|---|---|---| | **DO-Q1** | Centering-rule house sweep | — (can start w/ DO-K) | No (owner-locked rule) | | **DO-Q2** | Motion-verb token/class system (front-loads the grammar) | DO-K (`data-game`) | Optional motion-demo | | **DO-Q3** | Custom cursor per machine | DO-K (`identity.cursor`) | Light | | **DO-Q4** | Per-game sonic identity | DO-K (`identity.audio`) | No (audio preview branch) | | **DO-Q5** | Per-game empty / loading voice | DO-K (`identity.voice`) | No (copy deck) | | **DO-Q6** | Deeds-as-news on the live runtime | DO-K, runtime | No (copy deck) → folds into DO-W | ### The vertical-slice-first principle (non-negotiable sequencing) **Do NOT build all three machines in parallel.** Build the **NV machine end-to-end first** — bezel (DO-N) + Overseer presence (DO-O) + one fully-dressed subsystem (OPERATOR) — and pass the **SLICE GATE** (verified at 360/412/desktop, gate green, PWA intact) *before* replicating to FO3 (DO-M). This de-risks the heaviest architectural bets (chrome layout, nav routing, presence canvas, the `identity` consumption pattern) on **one** machine, so the FO3/FO4 replication is *authoring data + running the render matrix*, not re-litigating the architecture three times. This is the slate's §19 sequencing logic made concrete. **Dependency spine:** `DO-K → DO-N → DO-O → [SLICE GATE] → DO-C → DO-M → DO-W → DO-P → DO-G`, with `DO-Q1..Q6` interleaved as momentum units the moment their dependency (usually just DO-K) lands. `state.js` / `GAME_DEFS` touches **serialize** (never two identity-data commits concurrently — Protocol 12). --- ## 3. PER-UNIT SPECIFICATIONS Standing constraints on **every** unit (Protocol 43 — same bar on `dev` as `main`): full gate green each commit; both-runners parity (Protocol 15); counts synced everywhere (Protocol 2a); `CACHE_NAME` bump on any served-file change (Protocol 1); UTF-8 via Edit/Node writes only (Protocol 39); game- agnostic (Protocol 38); Protocol 10/17 mobile verification for any UI; Protocol UI-1..6 for panels; **APP_VERSION stays 2.7.0 under `[Unreleased]`** (cache-rev only) until the owner-gated release. **The three prime invariants hold on every unit:** (1) the hard atmosphere↔save boundary — the overhaul writes nothing durable to the campaign; (2) no-auto-anything / player authority; (3) dial- gated ambience (every ambient consumer declares an immersion tier the runtime enforces). --- ### DO-K · IDENTITY KEYSTONE — `GAME_DEFS[ctx].identity` + `data-game` (build FIRST) **Mockup:** No Fable HTML mockup — this is data/architecture. **Owner-approval artifact:** a written **identity data-contract spec** (the full field list below with NV/FO3/FO4 values) + the FO4 design brief. Owner signs off the *contract shape* before Sonnet writes it. **Files touched:** - `js/state.js` — widen each `GAME_DEFS[ctx].theme` (≈`849–1060`) into an `identity` block; **keep `theme` as a facet inside `identity`** (or alias `identity.theme = {...}` so `changeOpticsColor`'s existing reads at `ui-audio.js:415/441/477` don't break). Add the **FO4** `GAME_DEFS.FO4` entry (identity-complete, design-only — it must validate but no FO4 game-data files are required this cycle; guard that FO4 selection can't be reached in the UI yet). - `js/ui-core.js` — set `document.documentElement.dataset.game = getGameContext()` in a boot phase (beside the existing optic restore, `_restoreOpticsPreference`) **and** update it in `onGameContextChange()` (`ui-core.js:1810`) before the reload so the pre-paint state is correct. - `index.html` — add a pre-paint `<head>` inline `data-game` set (mirror the existing flash-free optics/high-lumen head scripts) so the attribute is present before first paint (no flash). This is the one sanctioned pre-`state.js` bare read (like the optics head script — Suite 134.7 precedent). - `eslint.config.mjs` — no new globals (data only). - `sw.js` — bump cache (served `state.js`/`index.html`/`ui-core.js` change). **The `identity` contract (proposed — owner signs off):** ``` GAME_DEFS[ctx].identity = { machine: 'salvaged-terminal' | 'pipboy-3000' | 'pipboy-3000-mk4', // NV / FO3 / FO4 material: 'scavenged-steel' | 'vaulttec-molded' | 'vaulttec-mk4', // → [data-game] bezel CSS structuralMode: 'bay', // hook kept open; no 'lattice' build this cycle (§0.2) theme: { …existing defaultOptics/framing/pipBoyModel/bootFlavor/saveLabel… }, // preserved persona: { texture:'uplink-scratchy'|'vault-institutional'|'mk4-clean', cadence:'…', blipBank:[…diegetic idle status lines…] }, // → DO-O ceremony: { coldStart:'mojave-uplink-handshake'|'vaulttec-post'|'mk4-post', switchLabel:'…' }, // → DO-C motionTexture: { seat:'…', sweep:'…', wake:'…', fault:'…', breathe:'…' }, // → DO-Q2 (tuning only) cursor: 'amber-block' | 'pipboy-reticle' | 'mk4-pointer', // → DO-Q3 audio: { humFreq, humGrit, bootDrone, wakeTone, radioBed }, // → DO-Q4 voice: { emptyStates:{…}, loading:[…] }, // → DO-Q5 ambient: { broadcasts:[…], news:[…], weatherLabel:'…' }, // → DO-W (born per-game) } ``` All banks are **data**, born per-game (J-8 discipline: author the per-game string the first time the key exists, so banks never multiply mono-game strings). No `ctx === …` literals in any consumer. **Acceptance criteria (Protocol 26):** 1. `GAME_DEFS.FNV.identity`, `.FO3.identity`, `.FO4.identity` all exist and validate; every field in the contract is present for all three (FO4 included). 2. `changeOpticsColor` / `_resolveOptic` / `_resolveDefaultOptics` / `_opticStorageKey` (`ui-audio.js:415–480`) still resolve identically (theme preserved as a facet) — **golden-master: optic resolution byte-identical to pre-refactor for FNV + FO3.** 3. `document.documentElement` carries `data-game="FNV"|"FO3"` at boot (verified pre-paint) and flips correctly through `onGameContextChange()`. 4. **AI contract unchanged** — `getSystemDirective()` output is byte-identical (Protocol 14 golden- master, the Suite-131 technique): widening the data block must not move one byte of the AI-facing string. 5. FO4 cannot be selected as a live game yet (guarded) — its entry exists purely to prove the N-game abstraction (Protocol 38) and force the contract to be right. 6. Save boundary: no `state.<field>` / `saveState` / `robco_v8` write anywhere in DO-K (data-def + an attribute set only). **Constraints:** Protocol 38 (the `identity` block IS the sanctioned per-game data home — see the proposed Protocol-38 extension §8.6); Protocol 39 UTF-8 (banks contain `>`/`⚠`/box chars — Edit/Node writes only); Protocol 22 (widen `theme`, do not fork a parallel block). **Verification:** `npm run gate`; a `test.html` behavioral proof driving `data-game` set on switch + the identity-facet reads; the two golden-master suites (optic resolution + AI directive) prove zero behavior change. Cache-rev bump. Docs (ARCHITECTURE new "Per-Game Identity Block" section; CHANGELOG `[Unreleased]` Under-the-Hood; README file/architecture notes). --- ### DO-N · BEZEL CHROME + SUBSYSTEM NAV (vertical-slice part 1, NV-tuned) **Mockup:** **YES — heavy.** Fable HTML mockup at **360 / 412 / desktop**, NV material, showing the bezel body, the illuminated subsystem selector, the "you are here" telemetry line, and one subsystem (OPERATOR) inside the curved glass. Rationale.md alongside (the module-bay-mockup precedent). Owner sign-off → Opus plan-audit → Sonnet build → Opus audit. **The change:** render the machine's **body around the CRT** and replace the abstract tab bar with an **illuminated hardware subsystem selector on the bezel**, preserving the working router underneath. **Current reality (grounded):** - Header `index.html:223–226` (h1 title + `#uptimeClock`); tab bar `index.html:231–268` (`.tab-bar[role=tablist]`, 4 `button.tab-btn` → `switchTab('stat'|'inv'|'data'|'campg')`, titles `[1]`–`[4]`); shell `index.html:228` (`.main-grid` → `.col-left#uiPanel` / `.col-right`). - `switchTab()` `ui-core.js:1934`, `initTabs()` `1957` (restores `robco_active_tab` via MetaStore); hotkeys `[1]`–`[4]` `ui-core.js:1398–1410`; `SHORTCUT_ROUTES`/`routeLaunchShortcut` `1965–1994` (PWA `#go=` deep-links); `expandPanelForCategory` `2015`; `_updatePanelBadges` `1860`. - Desktop shell CSS `terminal.css:1951–2006` (`@media (min-width:1000px) and (hover:hover) and (pointer:fine)` → `.main-grid{380px 1fr}`, `body{overflow:hidden}`); mobile default `terminal.css:128`; `.crt-overlay` `index.html:206` + `terminal.css:27–82`. **Subsystem model (slate §A-1, keeping 4-way muscle memory as an alias):** ``` OPERATOR OPERATIONS DATABANK UPLINK CHASSIS (STAT) (INV) (DATA) (comm/AI) (Module Bay/settings) ``` - **Keep `switchTab()` routing + the `data-tab` panel model + `robco_active_tab` + `[1]`–`[4]` hotkeys + `role=tablist/tab` ARIA + `#go=` deep-links intact.** The bezel selector is a **new presentation** over the *same* router: each bezel button calls `switchTab()`; the 5th subsystem (UPLINK) maps to the Comm-Link/Overseer column, CHASSIS to the Module Bay. Add a subsystem→tab alias map so no existing route breaks (Protocol 22). Persist the *subsystem view* as a MetaStore pref (`robco_bezel_subsystem`, Protocol UI-6 — a view choice that must survive reload). - **SWEEP transition** on subsystem change (the DO-Q2 motion verb, NV texture). - **"You are here" telemetry line** on the bezel (`▸ SUBSYSTEM: OPERATIONS · N ACTIVE DIRECTIVES · RAD NOMINAL`) — the slate's A-4 quick win, folded in here. - **Fallback (Protocol 25):** a flat "DIRECTORY" view (plain labelled buttons = the old tab bar's mental model) reachable one tap away, so the old muscle memory always works (mirrors the Module Bay's Schematic View). **Only OPERATOR is fully "dressed" in this unit.** The other four subsystems keep their **current panels rendering unchanged** under the new nav — they get their diegetic dressing progressively in DO-P. Swapping the nav must not require redesigning every panel at once. **Files touched:** `index.html` (bezel markup around `.main-grid`; subsystem selector replacing/ wrapping `.tab-bar`; telemetry line; keep `.crt-overlay`); `css/terminal.css` (`[data-game]` bezel material, curved-glass frame vignette on the *frame* not the text, desktop bezel layout inside the `min-width:1000px` gate, mobile edge-band); `js/ui-core.js` (subsystem selector wiring over `switchTab`; the alias map; telemetry-line updater; `robco_bezel_subsystem` persistence); `sw.js` (cache bump). **Acceptance criteria:** 1. Tab bar is replaced by the bezel subsystem selector; **all four original routes still work** via `switchTab`, `[1]`–`[4]` hotkeys still fire, `#go=` deep-links still route, `robco_active_tab` still restores, `role=tab`/`aria-selected` preserved. 2. Selected subsystem persists across reload (`robco_bezel_subsystem`, MetaStore); the flat DIRECTORY fallback is reachable in one tap and lists every subsystem plainly. 3. Bezel renders NV material via `[data-game="FNV"]`; curved-glass frame **never distorts text** (curve/vignette on frame + corner-darkening layer only); reduced-motion → static frame. 4. **No horizontal overflow at 360/412** (`documentElement.scrollWidth === innerWidth`); desktop two-column shell preserved; ≥28px tap targets on every bezel control; ≥16px inputs. 5. **PWA intact** — the bezel is CSS chrome around the app, **not a new shell**; installable / offline / standalone / service-worker all verified still working (Protocol 11 posture). 6. Save boundary: nav is presentation + MetaStore only; no campaign write. **Constraints:** Protocol 25 (owner-approved redesign — see the §8.1 amendment; reframes entry, keeps routing + hotkeys + a flat fallback + real labels as sub-labels); Protocol UI-1..6; Protocol 10/17 (the heavy render surface); PWA-intact guardrail. **Verification:** Fable mockup approved first; then `tests/render-check.mjs` at 360/412 (no overflow, no focus-zoom) + desktop; live preview of every route + hotkey + deep-link + reload-restore; boot- smoke (0 console errors); PWA install/offline smoke. Rewrite the tab-structure suites (Suite 57 shortcut routes, any tab-DOM-binding suites) to the new nav contract — **expected churn**, both runners at parity. --- ### DO-O · OVERSEER PRESENCE (vertical-slice part 2, NV persona) **Mockup:** **YES.** Animated Fable mockup of the waveform face in its three states (**idle** = gentle sine, **thinking** = jagged spikes, **speaking** = pulse-to-text), NV persona (scratchy uplink). Rationale.md. Owner sign-off → Opus audit → Sonnet build → Opus audit. **The change:** the AI stops being a blue text column and gains a **diegetic canvas presence** — a phosphor oscilloscope face that reacts — while the transcript still appears beneath it. Presentation only; the Tri-Node contract and opt-in/non-authoritative posture are **unchanged** (Protocol 14/24). **Current reality (grounded):** - `transmitMessage(overrideText)` `api.js:1756` (routes native commands first, then Gemini); `getSystemDirective()` `api.js:251` (modular builders, Tri-Node JSON, golden-mastered); `startThermalLoad()`/`stopThermalLoad()` `ui-audio.js:254/263` (the API-call hum ramp — the presence *visualizes this existing hook*, adds no new AI behavior). - `appendToChat(text, sender, isHistoryLoad)` `ui-core.js:3584` (`sender ∈ user|sys|ai`, typewriter for AI); `#chatDisplay` / `#chatInput`; `#modePill` `ui-core.js:2692` (OVERSEER/TERMINAL via `getInputMode`/`setInputMode` in `state.js`); `macroCommand` `3638`. **Build:** - **Presence canvas** in the UPLINK subsystem (Comm-Link column): idle = slow scan (drives on the Ambient Runtime as a **`tier:'balanced'` observer** in `ACTIVE`/`IDLE` so it's dial-gated and perf-bounded); **thinking** = spikes while the `thermal-load` class is on (hook the existing `startThermalLoad`/`stopThermalLoad` call sites); **speaking** = pulse synced to the `appendToChat` AI typewriter; settles to idle after. - **Thinking-as-event (C-4):** the wait becomes a diegetic transmission — presence animates, hum ramps (existing), a `> ESTABLISHING DIRECTOR LINK… DECRYPTING TELEMETRY…` period-leader readout ticks (ties DO-Q5 loading voice). No new spinner. - **NV persona** from `identity.persona` (texture `uplink-scratchy`, blip bank) — DO-M authors FO3's. - **Idle-life (C-5):** when unprompted, the presence emits an occasional **templated** diegetic blip from `identity.persona.blipBank` (NOT the AI — a device template, so it works keyless/offline). - **VOX (C-3)** is **deferred to backlog** as an opt-in Module-Bay chip (SpeechSynthesis, per-game voice, off by default) — *not* in this unit (keep the slice tight); scheduled as DO-Q-adjacent once the presence exists. **Files touched:** `index.html` (presence `<canvas>` in the Comm-Link/UPLINK region); `css/terminal.css` (presence framing, `[data-game]` persona texture); `js/ui-render.js` or a small new render helper (the canvas draw loop — registered as a runtime observer, not its own `setInterval`); `js/api.js` (fire presence state on transmit start/finish — thin hooks at the existing thermal-load call sites); `js/ui-core.js` (idle-blip observer registration; wire presence to `appendToChat` speak); `eslint.config.mjs` (new presence globals); `sw.js` (cache bump). **Acceptance criteria:** 1. Presence idles when quiet, spikes while a transmission is in flight (bound to the existing `thermal-load`/`startThermalLoad` window), pulses while the AI reply types, settles after. 2. **Reduced-motion:** the canvas collapses to a static resting frame (no animation); **aria-live is NOT spammed** by the canvas (the transcript keeps its existing `#chatDisplay` aria-live=polite; the presence is `aria-hidden` decorative). 3. **Works keyless/offline** — idle presence + idle blips render with no API key; the AI's behavior, JSON shape, and opt-in posture are unchanged (Protocol 14 golden-master still green). 4. Dial-gated: the presence idle animation is a `balanced`-tier runtime observer (silent at Minimal); perf-bounded (one canvas, throttled draw on the one heartbeat — no second timer). 5. Save boundary: presence writes nothing durable; NV persona banks read from `identity`. **Constraints:** slate §15.10 (canvas waveform, **never** a rigged 3D avatar); Protocol UI-10 (proposed §8.4 — presence a11y/perf discipline); Protocol 22 (reuse thermal-load + appendToChat + the runtime, no new AI path, no parallel timer). **Verification:** mockup approved; `npm run gate`; live preview of idle→think→speak with and without a key; reduced-motion check (static frame); a11y check (no aria-live spam); boot-smoke; render-check 360/412/desktop. --- ### ✅ SLICE GATE — prove the NV machine end-to-end Before any FO3/FO4 work: verify the **NV machine** as one coherent device — bezel (DO-N) + subsystem nav + Overseer presence (DO-O) + the **OPERATOR subsystem fully dressed** — at **360 / 412 / desktop**, gate green, **PWA intact** (install/offline/standalone), 0 console errors, no overflow, reduced-motion honored, save boundary clean. **Opus audits the committed diff + live evidence against the vertical-slice definition of done.** Only after this passes does DO-C/DO-M proceed. If it fails, loop back (Protocol 8 adaptive escalation) — do not replicate a shaky architecture to three machines. --- ### DO-C · CARTRIDGE-SWAP GAME-SWITCH + COLD-START CEREMONY **Mockup:** **YES.** Animated mockup of the eject→seat choreography (power-down → cartridge ejects with a clunk → new cartridge seats → the new machine boots in its own identity) + the cold-start salvage boot. Rationale.md. Owner sign-off → Opus audit → build → Opus audit. **Current reality:** `onGameContextChange()` `ui-core.js:1810` does a hard `location.reload()` on switch (a jarring white flash — the biggest wasted-ceremony opportunity). `runBootSequence(onComplete)` `ui-audio.js:1193`, `_pickBootFlavor()` `1139` (normal/cold/degraded), `_bootLinesFor()` `1151`, `_bootActive` gate. First-boot gated by `robco_booted_before` (MetaStore). The Module Bay hatch is the ceremony proof-of-concept; A3 `shutdown-crt` + `#powerOnBtn`/`_powerOnFromShutdown` + `body.rt-shutdown` already exist (`ui-core.js:1095` observers, `index.html:197`). **Build:** - **H-2 cartridge swap:** wrap the *existing* `onGameContextChange()` reload in choreography — power-down (reuse the A3 SHUTDOWN motion), a cartridge **eject** (clunk SFX, DO-Q4), then the new game's cartridge **seat** (thunk-and-spin), then the **new machine boots in its own `identity`** (`data-game` already flipped in DO-K; boot flavor from `identity.ceremony`). **The reload + per- game data load are UNCHANGED** — the ceremony wraps them; it never replaces or delays the real data path (save layer sacred). - **H-1 cold-start:** the once-per-device first boot becomes a salvage-and-power-on ceremony (per-game `identity.ceremony.coldStart`: NV modem-handshake, FO3 Vault-Tec POST, FO4 Mk-IV POST spec-only), built on the existing `robco_booted_before` gate + boot flavors. - **Skippable + reduced-motion-instant + never-nag** — persist "seen" device flags; a ceremony that can't be skipped is friction on the 100th boot. **Files touched:** `js/ui-core.js` (`onGameContextChange` wrapper — choreography around the reload; "seen ceremony" MetaStore flags); `js/ui-audio.js` (cartridge eject/seat SFX via DO-Q4 sources; cold- start boot lines from `identity`); `index.html`/`css/terminal.css` (cartridge-slot chrome on the bezel — ties DO-N + the DO-P cartridge-slot object); `sw.js` (cache bump). **Acceptance criteria:** 1. Switching FNV↔FO3 plays the cartridge-swap ceremony, then the target machine boots in its own identity; **the underlying reload + `GAME_FILES` data load are byte-for-byte the existing path** (no change to switch mechanics — verified). 2. Ceremony is **skippable**, **reduced-motion-instant** (collapses to a plain cut), and **never nags** after the first sighting (persisted device flag). 3. Cold-start ceremony fires once per device (`robco_booted_before`), per-game flavored, skippable. 4. **PWA/data intact** — the reload path, service worker, and save layer are untouched; ceremony is cosmetic on top. 5. Save boundary: ceremony writes only device "seen" flags (MetaStore); no campaign write. **Constraints:** slate §H; Protocol 25 (destructive confirm gates on WIPE/new-campaign stay exactly as-is — ceremony is cosmetic); reduced-motion; PWA-intact. **Verification:** mockup approved; live switch FNV↔FO3 (ceremony + correct machine boots + data correct); skip + reduced-motion + never-nag paths; first-boot cold-start on a fresh profile; gate green; render-check. --- ### DO-M · PER-GAME MACHINES (replicate NV → FO3; FO4 design-only) **Mockup:** **YES** for the **FO3 machine** (bezel material, persona, motion/sonic texture at 360/412/desktop). **FO4 = a design-spec doc only** (Mark IV Pip-Boy, §0.2 — no build). **The change:** with the NV slice proven and `identity` data-driven, "replicate" = **author FO3's `identity` facets** (material `vaulttec-molded`, persona `vault-institutional`, `green3` optic already shipped, dented casing, thicker scanlines) and **run the 3×3 render matrix** (machine × breakpoint). Almost no new feature code — this is data + CSS `[data-game="FO3"]` + verification. Author the **FO4** `identity` entry fully (validates) + a written Mark-IV design brief for the 3.0 build. **Files touched:** `js/state.js` (FO3 identity facet values; FO4 identity complete); `css/terminal.css` (`[data-game="FO3"]` bezel material, CRT character, cursor); `sw.js` (cache bump). Feature code: **none** (Protocol 38 — if replication needs a code branch, the keystone was wrong; fix the keystone). **Acceptance criteria:** 1. FO3 renders as a visibly distinct machine (Vault-Tec molded bezel, dull green, dented, thicker scanlines) at 360/412/desktop with **no `ctx === 'FO3'` literals** in feature code — all via `[data-game]` + `identity` data. 2. The 3×3 matrix passes: {NV, FO3} × {360, 412, desktop} — no overflow, AA contrast per machine, reduced-motion honored. (FO4 excluded — design-only.) 3. FO4 `identity` exists + validates (proves N-game); FO4 remains unselectable in the UI. 4. Game-switch (DO-C) shows the *correct* target machine's identity on boot. **Constraints:** Protocol 38 (the acid test — a new game = a new `identity` entry + data files, never a code branch); Protocol 10/17 (the 2×3 — soon 3×3 — render matrix is the verification standard, see §8.7 Protocol-10 amendment); AA contrast per machine. **Verification:** FO3 mockup approved; render matrix; live switch; gate green; FO4 spec doc reviewed. --- ### DO-W · LIVING-WORLD LAYER (Wasteland Uplink on the runtime) **Mockup:** Copy deck (owner approves the *voice* of broadcasts/news) + a **light** mockup for any new visible surface (the rad-storm CRT effect; an optional INTERCEPT readout at the Overseer presence). The full UPLINK *subsystem* (signal scanner, inbox, broadcast history) is **backlog** — this unit ships the committed slice. **Current reality:** the **Ambient Runtime is live** (`runtime.js` — `register({id,cadenceMs,states, tier,onTick,onEnter,onExit})` line 103, one 250ms heartbeat `_beat` line 168, dial gate `_allows` re-evaluated live every beat). `RobcoEvents` bus; `_logEvent`/`state.eventLog` (read-only source for news); `appendToChat(…, 'sys')` for broadcasts; geiger/CRT hum + `body` CRT classes for weather; the app-badge single source. **Build (all as tier-gated runtime observers, no-durable-write):** - **Deeds-as-news (D-2, the emotional core + DO-Q6):** templated "news" that **READS** `state.eventLog` and echoes your real deeds back as wasteland chatter (`> RADIO NEW VEGAS: A COURIER WAS SEEN LEAVING GOODSPRINGS…`). Pure read + template from `identity.ambient.news`. **Never writes state.** - **Ambient broadcasts (D-1):** unsolicited `> [WASTELAND UPLINK] …` sys-lines templated from real state (loc, nearest faction, active-quest count) via per-game `identity.ambient.broadcasts`. - **Rad-storm weather (D-1):** occasional storm drives the existing `body.rad-warning` CRT interference + geiger tick-up, then clears. **Visual/audio only — NEVER mutates `state.rads`.** **No day/night** (§0.6). - **Intercept-at-Overseer (D-1/C-5):** a rare `> UNKNOWN FREQUENCY DETECTED` nudge arrives at the DO-O presence and can pre-fill `#chatInput` (inviting you back in) — unread → app badge (reuse the single badge source, Protocol 22). **Files touched:** a new served `js/uplink.js` (or fold into an existing ambient file) registering the observers; `js/state.js` (`identity.ambient` banks — DO-K seam); `js/ui-core.js`/`api.js` (badge + presence hooks); `sw.js` (cache bump). **Acceptance criteria:** 1. Broadcasts/news/weather fire as **runtime observers** declaring an immersion tier; **Minimal = silent** (dial-gated in exactly one place — the runtime). 2. **Hard no-durable-write** — a gate-guarded suite proves `uplink.js` (and every ambient file) contains no `saveState`/`robco_v8`/`state.<field>=`/`_logEvent`/`eventLog` *write*; weather never touches `state.rads`; news only *reads* `eventLog`. 3. Deterministic + offline (seeded rolls from a device seed + wall-clock; no AI, no network); per- game banks born from `identity`. 4. Conservative intensity + cooldowns (failure mode is "noisy gimmick" — mitigate). **Constraints:** the atmosphere↔save boundary is the load-bearing invariant here (slate §18, §D); Protocol 32/33 fail-safe posture for any live channel (the live Remote-Transmissions channel is **backlog/Phase-3**, not this unit); dial-gated. **Verification:** copy deck approved; gate + the no-durable-write suite; live observation at each dial tier; badge behavior. --- ### DO-P · COHESION / POLISH (motion grammar site-wide + surface dressing) **Mockup:** **YES per major surface** (SYSTEM STATUS, inventory-as-cargo-manifest, world-map recon grid); light/none for small copy or class sweeps. **The change:** apply the DO-Q2 motion-verb grammar everywhere, and progressively **dress the remaining subsystems** (the panels DO-N left functional-but-undressed) per slate §13. Sub-units, each its own commit (Protocol 19 batch per sub-unit): - **P-a · Motion grammar applied** — every interaction speaks a verb (SEAT/SWEEP/WAKE/FAULT/BREATHE), per-game texture; physical value transitions (HP drain lag, level sweep, faction jolt, limb sag — extends the existing keyframe lib + WU-C1 reduced-motion gating); traveling-light focus (keep the literal `:focus-visible` outline underneath). - **P-b · OPERATIONS** (inventory=cargo manifest, ammo=reserve gauges, collectibles=display case, craft/barter=workbench framing) — centering rule on filter row + collectible grid. - **P-c · DATABANK** (quests=active-directive strips w/ status flags, notes=operator logbook). - **P-d · OPERATOR** stats/skills/bio dressing beyond the slice (medical-telemetry readout, SPECIAL as tuning gauges, skills as a tuning matrix, faction standing board / FO3 karma gauge). - **P-e · SYSTEM STATUS + Hardware-Life whisper (old Chapter D, committed slice):** consolidate Overseer telemetry + `[LOGS]` error ring + a `RUN DIAGNOSTIC` self-test into one machine-vitals surface (device state, read-only view — never writes `eventLog`); **emergent CRT wear at a WHISPER** (2–3 subtle stages driven by boot-count/power-on-hours, always loses to real hazards, reduced-motion honored). Deep hardware *quirks* → backlog. - **P-f · WORLD MAP recon grid** — coord-placed cells, radar-SWEEP verb, tap-to-travel, fog-of-war; the known-hard 6×6-at-360px problem needs zoom-to-region (slate §13.9). Its own mockup + heavy render-check. - **P-g · Modals / macro pad / native terminals / saves-as-cartridges** diegetic framing (update modal stays the deliberate blocking exception, Suite 65; save integrity path untouched). **Acceptance criteria (per sub-unit):** the surface reads as physical hardware; **centering rule holds** on every grid touched; reduced-motion resting state per verb; no 360 overflow; **save layer untouched** (P-e/P-g are reframes over the sacred layer); AA contrast; both-runners suites rewritten to the new markup contracts (expected churn). **Constraints:** Protocol 25 (dress in place, don't relocate working controls); Protocol UI-8/UI-9 (centering + motion-verb, proposed §8.2/8.3); the atmosphere↔save + device↔campaign boundaries. **Verification:** per-surface mockups approved; render matrix; gate; live. --- ### DO-G · MOBILE CASING (the phone IS the device) **Mockup:** **YES (mobile)** — per machine, at 360/412; the edge-filling casing + slide-over subsystems. The single biggest mobile win; heaviest mobile render burden. **The change:** on mobile the machine's **casing fills the screen edges** (bezel → phone frame), and subsystems **slide over** the Overseer home like handheld drawers (one visible at a time) rather than a stacked scroll-page. FO3 especially reads as a Pip-Boy held to your face. Thumb-zone hardware controls (≥28px). Haptics (WU-F2) extend to the motion verbs (seat/fault/intercept). **Device-tilt parallax stays an off-by-default chip** (§0.6 — never on the critical path). **Current reality:** desktop shell gated on `(min-width:1000px) and (hover:hover) and (pointer:fine)` `terminal.css:1951`; mobile = default stacked single column; WU-F1 wake-lock + WU-F2 haptics shipped. **Acceptance criteria:** 1. Mobile casing fills edges per `[data-game]`; **no horizontal overflow at 360** (`scrollWidth === innerWidth`); ≥28px targets; ≥16px inputs. 2. Slide-over subsystems: one visible at a time, swipe/tap between, over the Overseer home; the DO-N flat DIRECTORY fallback still reachable. 3. **PWA HARD guardrail** — casing is CSS, **not a new shell**; installable / standalone / offline all verified intact on a real device (Protocol 29-style real-device check for the mobile chrome). 4. Reduced-motion honored; device-tilt off by default and depended-on by nothing. **Constraints:** slate §G; Protocol 17 mobile baseline; the PWA-intact guardrail is sharpest here; Protocol 10 render-check + real-device. **Verification:** mobile mockups approved; `tests/render-check.mjs` 360/412; **real-device** install/ offline/standalone smoke (the mobile-chrome analogue of Protocol 29); gate green. --- ## 4. QUICK-WINS — placement (land EARLY for momentum) Each rides `identity` (DO-K) and mostly stands alone; land them as soon as their dep is in, in parallel with the spine (they de-risk and *show* the direction fast — slate §14). | Unit | What | Slot | Mockup | Files | |---|---|---|---|---| | **DO-Q1** | **Centering-rule sweep** — every incomplete final row centers its remainder (chip/tube/macro/save/map grids) | With/after DO-K | No (owner-locked) | `css/terminal.css` + a gate guard | | **DO-Q2** | **Motion-verb token/class system** — SEAT/SWEEP/WAKE/FAULT/BREATHE as classes w/ per-game texture + reduced-motion resting states; front-loads the grammar every later area inherits | Right after DO-K `data-game` | Optional demo | `css/terminal.css`, tiny `js` hook | | **DO-Q3** | **Custom cursor per machine** — amber block / Pip-Boy reticle / Mk-IV pointer; system fallback on coarse-pointer | After DO-K | Light | `css/terminal.css` (`[data-game]`, `@media (pointer:fine)`) | | **DO-Q4** | **Per-game sonic identity** — parameterize the ~23 existing synth sources into `identity.audio` (hum grit/freq, boot drone, wake tone, radio bed) | After DO-K | Audio preview branch | `js/ui-audio.js` (`AudioSettings`/synth reads `identity.audio`) | | **DO-Q5** | **Per-game empty / loading voice** — reframe the shared `emptyState()` helper + period-leader loaders via `identity.voice` | After DO-K | Copy deck | `js/ui-render.js` (emptyState), `identity.voice` | | **DO-Q6** | **Deeds-as-news** — read `eventLog`, echo as wasteland chatter on the live runtime | After DO-K | Copy deck | folds into **DO-W** | **Note:** VOX (opt-in SpeechSynthesis chip, per-game voice) is a *post-presence* quick-win — schedule it right after **DO-O** ships, as an off-by-default Module-Bay chip (Protocol 7 audio-source checklist; never load-bearing). --- ## 5. RANKED BACKLOG (deferred — pull from, not mandatory; slate effort/impact order) Committed scope = §2–§4. Everything else is a **prioritized backlog** to pull from as budget allows, NOT committed units: 1. **Cartridge-slot object unifying saves + game-switch (§K-3)** — saves become cartridges you handle (rides DO-C + DO-P-g). *High cohesion, medium effort.* 2. **VOX opt-in voice chip (§C-3)** — SpeechSynthesis, per-game voice, off by default. *High wow / low byte, post-DO-O.* 3. **UPLINK as a visible subsystem (§D-4)** — signal scanner + INTERCEPT inbox + broadcast history + weather readout (promotes DO-W's ambient noise into a place to *tune in*). 4. **Chained multi-part transmissions (§D / old C3)** — per-chain state machine, no-write until the player acts. 5. **Distribution Network live channel (§D / old C4)** — read-only public Firestore doc + `remoteTransmissions` kill-switch + App Check (Protocol 32/33). *Owner-gated; live infra.* 6. **Location-aware ambience (§D-3)** — zone-type tunes the ambient character. 7. **Hardware quirks bank (§E-2)** — a tiny curated per-game set (relay clicks, wake flicker). *Keep minimal — overdone reads as "broken app."* 8. **Screen-curvature glass depth + reflection sheen (§K-2)** beyond the DO-N frame vignette. 9. **Physical power-off/standby dressing (§K-5)** — dress the existing A3 shutdown/power-on as hardware. 10. **Filesystem nav power-route (§A-3 / old F2)** — `DIR`/`OPEN` + browsable tree as a *secondary* command layer (promote to primary IA is a **3.0** decision — discoverability/mobile risk). 11. **RobCo MANUALS (help-as-documents) + "?"-affordance rollout (old F1)** — help moves to precached documents; `HELP` opens the index. 12. **Local user MACROS (old F2)** — `MACRO REST = SLEEP; BIO-SCAN; STATUS`, device-side, replays through the same confirm-gated router. 13. **Device-tilt parallax chip (§G-3)** — off by default, reduced-motion/perf aware, depended-on by nothing. 14. **Animated re-center for the centering rule (§I-4)** — items glide to center on remainder change. 15. **FO4 Mark IV build (3.0)** — the whole FO4 machine, once its `identity` spec (DO-M) is proven. --- ## 6. HOW THIS RESTRUCTURES THE REMAINING PHASE-2 CHAPTERS The old `STEP2_PHASE2_PLAN.md` had Chapters A–F. **A (Ambient Runtime A1–A3) and B (B1 command mode, B2 Module Bay) are shipped.** The overhaul **supersedes/absorbs** the unbuilt C–F as follows: | Old Phase-2 chapter | Status | Where it goes in the overhaul | |---|---|---| | **A — Ambient Runtime** | ✅ Shipped | **Consumed** — DO-O idle presence + DO-W living world are observers on it. Not rebuilt. | | **B — Organizing (command + Module Bay)** | ✅ Shipped | **Consumed** — B1 command router underpins the bezel nav aliases (DO-N); Module Bay becomes the **CHASSIS subsystem** accessed through the bezel (DO-N/DO-P), boards untouched (§0.6). | | **C — Wasteland Uplink (unbuilt)** | Reframed | **Committed slice → DO-W** (deeds-as-news + broadcasts + rad-storm weather + intercept-at-Overseer, on the runtime). Intercepts surface at the **DO-O Overseer presence**. **Deferred → backlog** (§5.3–5.6): UPLINK-as-subsystem, chained transmissions, the live Distribution-Network channel. | | **D — Hardware Life (unbuilt)** | Reframed | **Committed slice → DO-P-e** (SYSTEM STATUS consolidation + emergent CRT wear at a whisper, device-state read-only view — never writes `eventLog`). **Deferred → backlog** (§5.7): deep hardware quirks. | | **E — Game Identity (unbuilt)** | **Absorbed whole** | **Becomes the spine:** DO-K (`identity` block + `data-game`) + DO-N (bezel/framing) + DO-M (per-game machines + game-styled saves via the shipped `saveLabel` hooks) + DO-Q3/Q4 (cursor/sound) + DO-G (mobile casing). This is the overhaul's core. | | **F — Navigation completion (unbuilt)** | Split | **Nav replacement → DO-N** (bezel subsystem selector replaces the tab bar, keeping the router). **MANUALS + filesystem + macros → backlog** (§5.10–5.12) / Phase 3 — the slate ships filesystem as a *secondary* power-route, promoting to primary at 3.0. | **Net:** C and D keep their **systems** (they ride the already-shipped Ambient Runtime) but get their **visual/experiential home** from the overhaul — C at the Overseer presence + broadcasts, D at SYSTEM STATUS. E is absorbed entirely as the identity spine. F's nav is done by the bezel; F's help/ filesystem defer. The **Phase-2 → Phase-3 boundary and the 2.7.0→2.8.0 release gate are unchanged** (the release stays an owner-gated `--no-ff dev→main` milestone; all overhaul work accrues under `[Unreleased]`). --- ## 7. THE PER-UNIT WORKFLOW (mockup → approval → audit → build → audit) Per the overhaul mandate, **every unit that changes visible chrome, layout, or a new visual surface opens with a Fable mockup the owner signs off before any app code is written.** The full loop (extends Protocol 8): 1. **Fable MOCKUP** — a self-contained HTML mockup in `planning/mockups/` (the `module-bay-mockup.html` precedent: rendered at **360 / 412 / desktop**, screenshots saved) + a `*-rationale.md` (concept, mapping, muscle-memory/fallback, mobile/desktop, game-agnostic, open questions). **Nothing in the app is touched.** 2. **Owner sign-off** — the owner approves the mockup against a concrete design (the Protocol-25 sanctioned-exception precondition). 3. **Opus — plan-audit** — Opus reads every file the unit can touch, enumerates every entry path/ state/edge/breakpoint, writes the concrete plan, and audits its own plan before hand-off (Protocol 8 stage 1). 4. **Sonnet — build** — reviews the plan against live files (line-drift), implements, runs the full gate, verifies at 360/412/desktop on the real artifact (Protocol 8 stage 2). 5. **Opus — audit before done** — reviews the committed diff + verification evidence against the mockup + acceptance criteria; loops back if short (Protocol 8 stage 3). **Mockup requirement per unit:** | Needs a Fable HTML mockup | Uses a lighter review artifact (still owner sign-off) | No mockup | |---|---|---| | DO-N (heavy), DO-O, DO-C, DO-M (FO3), DO-P (per major surface: SYSTEM STATUS, inventory, recon map), DO-G (mobile, per machine) | DO-K (identity data-contract spec + FO4 brief), DO-Q4 (audio preview branch), DO-Q5 / DO-Q6 / DO-W (copy decks), DO-Q3 (light cursor mockup), DO-Q2 (optional motion demo) | DO-Q1 (owner-locked centering rule — mechanical) | --- ## 8. PROTOCOL CHANGES NEEDING OWNER SIGN-OFF (flagged, NOT assumed) The overhaul needs several protocol updates. **None are adopted here — each is flagged for explicit owner sign-off before the units that rely on it build.** ### 8.1 Protocol 25 (UX Stability) — amend the sanctioned-exception clause Extend the WU-B2a "owner-approved redesign" exception from a **single panel** (Module Bay) to a **site-wide overhaul**, under the *same* five guardrails (mockup sign-off · no tap-count increase · a fallback preserving the prior mental model · real labels ride along as searchable sub-labels · stored semantics unchanged even when presentation inverts). **Additionally authorize replacing the tab bar with bezel subsystem nav** (DO-N), conditioned on: hotkeys `[1]`–`[4]` preserved, `role=tab` ARIA preserved, `#go=` deep-links preserved, and a flat DIRECTORY fallback that reproduces the old tab mental model. *Without this, DO-N/DO-P violate Protocol 25.* ### 8.2 NEW Protocol UI-8 — The Centering Rule (formalize the owner's standing rule) Promote slate §0.4's owner-locked centering rule to an enforceable UI protocol: any incomplete final row in any grid centers its remainder; "off-center = a bug." Add a static gate guard (Suite) so a future refactor can't silently drop it (Protocol 20 posture). *Gates DO-Q1 + every DO-P grid.* ### 8.3 NEW Protocol UI-9 — Motion-Verb Grammar Codify SEAT/SWEEP/WAKE/FAULT/BREATHE as the shared motion vocabulary (per-game texture via `identity.motionTexture`), each verb with a mandatory reduced-motion resting state, legibility never sacrificed to motion. *Gates DO-Q2 + DO-P-a.* ### 8.4 NEW Protocol UI-10 — Overseer Presence The AI presence is a **canvas waveform, never a rigged 3D avatar**; it must be reduced-motion-safe (static resting frame), must **not** spam `aria-live` (decorative `aria-hidden`; the transcript keeps its existing polite live region), must work keyless/offline, and writes nothing durable. *Gates DO-O.* ### 8.5 NEW Protocol UI-7 — Device Chrome / Bezel Standard The bezel/casing is **CSS chrome around the app, never a new shell** (the PWA-intact hard guardrail); desktop-first where space exists with a disciplined mobile edge-band; curvature/vignette live on the frame and **never distort text**; hardware controls array on the bezel obey the centering rule. *Gates DO-N + DO-G.* ### 8.6 Protocol 38 (Game-Agnostic) — extend to the `identity` block Declare `GAME_DEFS[ctx].identity` the sanctioned per-game data home: every per-machine facet (material/persona/ceremony/motion/audio/cursor/voice/ambient) is **data**, the **FO4 entry must exist and validate** (design-only), and **no feature code may branch on a game literal** for chrome, persona, motion, audio, or voice. *Gates DO-K + DO-M (this is the keystone's guardrail).* ### 8.7 Protocol 10 (UI Verification) — amend to the per-machine × per-breakpoint matrix Update the render-verification standard: chrome/identity units verify the **machine × breakpoint matrix** (this cycle {NV, FO3} × {360, 412, desktop} = 6; FO4 design-only). `tests/render-check.mjs` extended to iterate `data-game`. *Gates DO-N/DO-M/DO-P/DO-G.* ### 8.8 NEW "Design-Unit Workflow" protocol (or a Protocol 8 extension) Codify §7: every overhaul unit that changes visible chrome/layout opens with a Fable mockup + rationale in `planning/mockups/` → owner sign-off → Opus plan-audit → Sonnet build → Opus audit; data/ audio/copy units substitute a lighter review artifact but still get owner sign-off. *Governs the whole program.* ### 8.9 Reaffirm (not new) — the two load-bearing invariants The **atmosphere↔save boundary** (the overhaul writes nothing durable to the campaign; DO-W/DO-P-e gate-guarded no-durable-write) and the **PWA-intact guardrail** (DO-N/DO-G/DO-C chrome & nav are CSS/routing over the working shell, never a replacement) are restated as standing invariants of the overhaul — already Phase-2 invariants, called out so no unit breaches them. --- ## 9. RISKS + SEQUENCING NOTES - **The keystone is the make-or-break call.** If `identity` (DO-K) is shaped wrong, every consumer inherits a bad contract and DO-M's "replicate by data" collapses into code branches. Invest Opus depth in DO-K; the owner signs off the contract shape before it's written. - **DO-N is the heaviest architectural bet** — it's load-bearing IA (routing, hotkeys, deep-links, ARIA, PWA routing) *and* the biggest render surface. The vertical-slice-first rule exists precisely to prove it on NV before three-machine replication. Keep the router; only re-present it. - **PWA-intact is the sharpest guardrail** (DO-N/DO-G/DO-C). Chrome and nav must be CSS/routing over the working shell; verify installable/offline/standalone every chrome commit, real-device for mobile (DO-G). - **The atmosphere↔save boundary is easy to breach accidentally** (a "news" feature tempted to log to `eventLog`, a weather effect tempted to persist rads). The gate-guarded no-durable-write suite must land **with DO-W** and cover every ambient file. - **Test churn is the normal mode, not a red flag** — UI-contract suites get rewritten to the new markup every visual unit; keep both runners at parity and counts synced (the churn is *expected*; a *failing* gate is not). - **Line drift** — the file:line anchors in §3 are from `cd8a824`; each implement session re-verifies against live `dev` before touching a seam (Phase-1's standing lesson). - **Serialize `state.js`/`GAME_DEFS` touches** (Protocol 12) — DO-K → DO-M → DO-W all edit identity data; never two concurrently. - **Boot-order hazard** — any `runtime.js`/`uplink.js` top-level code touching `state.js` globals (`immersionAllows`, `RobcoEvents`, `getGameContext`) must sit inside a `window.onload`-called function, never module top level (the U7 lesson). --- ## 10. THE PROGRAM AT A GLANCE ``` DO-K identity keystone ........... data spine, FIRST [no mockup] │ ├─ DO-Q1 centering sweep ......... momentum [no mockup] ├─ DO-Q2 motion-verb tokens ...... front-load grammar [optional demo] ├─ DO-Q3 custom cursor ........... momentum [light] ├─ DO-Q4 per-game sonic .......... momentum [audio branch] └─ DO-Q5 empty/loading voice ..... momentum [copy deck] │ DO-N bezel + subsystem nav ....... NV, vertical-slice 1 [MOCKUP heavy] DO-O Overseer presence ........... NV, vertical-slice 2 [MOCKUP] ══ SLICE GATE: prove NV end-to-end (Opus audit) ══ DO-C cartridge-swap + cold-start . game-switch ceremony [MOCKUP] DO-M per-game machines ........... FO3 build, FO4 spec [MOCKUP FO3 + FO4 doc] DO-W living-world layer .......... deeds-as-news + weather [copy deck + light] (absorbs DO-Q6) DO-P cohesion / polish ........... motion + surface dress [MOCKUP per surface] DO-G mobile casing ............... phone IS the device [MOCKUP mobile] Backlog (§5): cartridge-slot object · VOX · UPLINK-subsystem · chained tx · live channel · location ambience · quirks · glass depth · power dressing · filesystem · MANUALS · macros · tilt · animated re-center · FO4 Mk-IV build (3.0) ``` --- *END — DESIGN_OVERHAUL_BUILD_PLAN.md. Plan only. No app code written, nothing committed or pushed. `planning/` is gitignored. Every protocol change in §8 awaits explicit owner sign-off.*
STAMP · generated for RELEASE v2.8.5 commit 06e5180 (06e51801b38a) · archive input-tree hash c07fbfbdd2e1ddeb · 754 files · no wall-clock timestamp (regenerates identically when nothing changed).