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).