RELEASE
planning/2.8.0/audits/DIEGETIC_AUDIT.md
sha256 a83871da54106bc8 · 12750 bytes ·
original held in the private archive
# RobCo U.O.S. — Site-Wide Diegetic Audit (Stage A0)
> **FILED UNDER 2.8.0 — PRODUCED-BY, NOT MEASURED-VERSION.** This document *measures* an earlier codebase
> (see its baseline stamp below), but it was *produced* as part of the audit/planning phase that became the
> 2.8.0 overhaul. Planning artifacts are filed by the release whose work produced them, never by the version
> they discuss. The version in the title or baseline is the code it examined, not the folder it belongs in.
> (A 2.5.0 and 2.6.0 folder do exist, holding feature designs for features that shipped in those releases. There
> is no 2.7.0 folder because no artifact has been identified as produced during 2.7.0.)
> Plan-only. Measured against `HOUSE_STANDARD.md`. Targets `origin/main` @ `bc8c2eb` (v2.6.0, cache r1, 1078 tests).
> Untracked planning doc — feeds later chain stages. **Nothing built.**
## House Standard summary (yardstick)
Voice = `> ` prompt prefix · ALL-CAPS chrome · bracketed `[STATUS]` · period-leader progress · terse machine register · `⚠` faults. Palette = phosphor `#14fdce` / amber / red / blue on near-black, monospace, scanlines, no modern GUI chrome. Terminology locked (TERMINAL not "app", COMM-LINK not "chat", CAMPAIGN CONFIGURATION not "settings", UPLINK not "sign in", RETRIEVING… not "Loading…", VISUAL UPLOAD not "upload"). Guardrails: ARIA literal, AA contrast, ≥16px inputs, keyboard order preserved, **CRT motion must honor prefers-reduced-motion**.
## Coverage matrix (screens × states — provable enumeration)
| Surface | Loading | Empty | Error | Offline | Auth | Success | Verdict |
|---|---|---|---|---|---|---|---|
| Boot / TERMLINK shell | ✅ canon | — | ⚠ BOOT DATA FAILED ✅ | n/a | n/a | ✅ | **canon** |
| Header / status line | n/a | n/a | n/a | n/a | n/a | n/a | ✅ canon |
| COMM-LINK (chat) | ✅ (typewriter) | ✅ sys init | ✅ SYS-ALERT/FATAL | local-ok | n/a | ✅ telemetry | mostly canon; error tails MN-9 |
| STAT tab (BIO-METRICS/SKILL MATRIX/SPECIAL/BOOKS/MAGAZINES/PERKS) | n/a | MN-7 | n/a | n/a | n/a | n/a | placeholders MN-6, empties MN-7 |
| INV tab (BACKPACK/AMMO/COLLECTIBLES/CRAFTING) | n/a | MN-7 | n/a | n/a | n/a | n/a | empties MN-7, VISUAL UPLOAD MN-8 |
| DATA tab (QUEST/STATUS/SQUAD/FACTION/KARMA/NOTES/STATS/MAP/CROSSROADS/TIMELINE) | n/a | MN-7 | n/a | n/a | n/a | n/a | empties MN-7 |
| CAMPG tab (CONFIGURATION) | n/a | n/a | n/a | n/a | n/a | n/a | ✅ mostly canon |
| Security & Config (saves/key/audio/optics) | MJ-3 | n/a | ✅ SYS-ALERT | local-ok | n/a | ✅ | upload MN-8, key tail MJ-5 |
| ACCOUNT / cloud archives | MJ-3 "Loading saves…" | MJ-3 "No saves found." | MJ-3 "Failed to load" | local-ok | **MJ-2** sign-in copy | ✅ SYNC COMPLETE | **MJ-2/MJ-3** |
| Update modal | n/a | n/a | n/a | n/a | n/a | n/a | PO-10 |
| Out-of-frame (manifest/shortcuts/title/icon) | — | — | — | — | — | — | **MJ-4** |
| Motion / a11y (all tabs) | — | — | — | — | — | — | **CR-1** + TN-13 |
---
## CRITICAL
### CR-1 · [GUARDRAIL / INTERACTION] `css/terminal.css` (≈20 `@keyframes`: flicker, header-pulse, rad-pulse/shift/chaos, vats-scan, cursor-blink, phosphor-ghost, weight-sag/buckle, karma flashes, crit-hp-flash, badge-pop, refresh-bar) + boot type-out
- **What:** Zero `prefers-reduced-motion` gating anywhere (`grep prefers-reduced-motion css/ = 0`). Every CRT animation + the boot type-out runs unconditionally.
- **Why it breaks the lens:** The lens's own guardrail mandates CRT flicker/scanline/type-out honor reduced-motion. Unguarded flicker/flash is a vestibular/photosensitivity hazard — the one place immersion must yield to usability.
- **In-world fix:** Add `@media (prefers-reduced-motion: reduce) { … }` that freezes the decorative loops (flicker/pulse/scan/ghost/sag/flash) to their resting phosphor state and reduces the boot type-out to an instant static panel. The *static* CRT look (scanlines, glow, monospace) stays — only motion is calmed.
- **VERDICT:** SPEC-FIRST · `[TEST][CACHE]` (css served → bump; add a static guard that a `prefers-reduced-motion` block exists, both runners).
- **Files:** `css/terminal.css` (+ `tests/check-persistence.{js,ps1}`).
---
## MAJOR
### MJ-2 · [VOICE / AUTH] `js/ui-account.js:41–53`
- **What:** `NOT SIGNED IN — saves are local only.` / `> SIGN IN WITH GOOGLE (SYNC ACROSS DEVICES)` / `SIGNED IN`.
- **Why:** "Sign in / signed in / sync across devices" is modern-web auth vocabulary on a primary panel.
- **In-world fix (copy only — mechanic untouched):** `UPLINK OFFLINE — ARCHIVES STORED LOCALLY` / `> ESTABLISH GOOGLE UPLINK — SYNC TELEMETRY ACROSS TERMINALS` / `UPLINK ACTIVE`. The Google popup itself is NOT touched (frame copy *around* it).
- **VERDICT:** SPEC-FIRST · `[AUTH][CACHE][TEST]` (popup-only; redirect stays banned; no mechanic change).
- **Files:** `js/ui-account.js` (+ tests).
### MJ-3 · [SYSTEM STATES / VOICE] `js/ui-account.js:90,94,100`
- **What:** `emptyState('Loading saves...')`, `'Failed to load cloud saves'`, `'No saves found.'` — sentence-case generic web states on the cloud-archive list.
- **Why:** Breaks loading/empty/error register (§1, §4).
- **In-world fix:** `RETRIEVING ARCHIVES…` / `⚠ ARCHIVE LINK FAILED` / `NO ARCHIVES ON FILE`.
- **VERDICT:** BUILD-NOW (copy-only) · `[CACHE][TEST]`.
- **Files:** `js/ui-account.js` (+ tests).
### MJ-4 · [TERMINOLOGY / OUT-OF-FRAME] `manifest.json:4,30,44,58,72`
- **What:** description = `"… — Fallout: New Vegas tactical companion terminal."` surfaces the real game title to the install/store frame; shortcut descriptions = `"Open the Inventory tab"`, `"Wipe terminal and start a new campaign (confirmation required)"` (modern UI vocabulary).
- **Why:** Out-of-frame surfaces (install prompt, OS app list) leak modern + real-world-IP framing.
- **In-world fix:** Description → keep RobCo framing, drop the bare game name: e.g. `"RobCo Industries Unified Operating System — wasteland survival TERMINAL."` Shortcut descriptions → diegetic verbs (`"Access BACKPACK INVENTORY"`, `"PURGE terminal — begin new campaign"`). Names (`Comm-Link`) already fine.
- **VERDICT:** SPEC-FIRST · `[CACHE][TEST]` (manifest is served → bump; Suite already checks manifest — extend). Sourcing note: no fallout.wiki data needed (pure copy).
- **Files:** `manifest.json` (+ tests).
### MJ-5 · [TERMINOLOGY] `js/api.js:1211, 1328` (and surrounding narrative errors)
- **What:** `⚠ [SYS] AI returned an unexpected response format…` and `⚠ AI KEY REJECTED — Your Gemini key was refused.` leak "AI" / "Gemini" into Courier-facing narrative (the in-world director is GEM / THE DIRECTOR).
- **Why:** Terminology break — the narrative voice should never name the real model/vendor.
- **In-world fix:** `⚠ [SYS-ALERT] DIRECTOR LINK RETURNED MALFORMED TELEMETRY — NOTHING APPLIED.` / `⚠ DIRECTOR ACCESS KEY REJECTED — RE-ENTER ACCESS KEY.` **TENSION:** the *config field label* must stay literal "Gemini API Key" (the user needs to paste their real Gemini key) — only the **narrative error** reframes; the input label stays in the LITERAL layer.
- **VERDICT:** SPEC-FIRST · `[TEST][CACHE]`.
- **Files:** `js/api.js` (+ tests).
---
## MINOR
### MN-6 · [MICROCOPY / CONSISTENCY] `index.html:602–865` (input placeholders)
- **What:** `Item Name`, `Qty`, `lbs`, `val`, `Quest Name`, `Current objective (optional)`, `Perk Name`, `Rank`, `Lvl`, `Ticks`, `Filter traits...`, `Caliber (e.g. 5.56mm)` — mixed/lower-case.
- **Why:** Breaks the ALL-CAPS terminal register; reads like a web form.
- **In-world fix:** Uppercase + terse: `ITEM DESIGNATION`, `QTY`, `WT (LBS)`, `VAL`, `QUEST`, `OBJECTIVE (OPTIONAL)`, `PERK`, `RANK`, `LVL`, `TICKS`, `FILTER TRAITS…`, `CALIBER (E.G. 5.56MM)`. **TENSION:** keep them unambiguous — caps yes, cryptic no (don't rename "Qty" to something obscure).
- **VERDICT:** SPEC-FIRST (batch) · `[CACHE][TEST]`.
- **Files:** `index.html` (+ a static guard that no lower-case `placeholder=` remains on content inputs).
### MN-7 · [SYSTEM STATES / CONSISTENCY] empty-states across `index.html` (674,697,704,736,770,830,950,1140,1163) + render fns
- **What:** `No collectibles loaded`, `No recipes loaded`, `No matching items in inventory.`, `No active quests`, `No perks acquired`, `No active effects`, `No notes recorded`, `No matches`, `No decisions recorded…`, `No timeline generated…`.
- **Why:** Sentence-case + generic; inconsistent empty-state voice.
- **In-world fix:** One canon empty-state vocabulary, ALL-CAPS: `ARCHIVE EMPTY` / `NO ENTRIES IN MEMORY` / `NO ACTIVE DIRECTIVES` / `NO TELEMETRY ON FILE`. (`emptyState()` is a shared helper — reframe centrally.)
- **VERDICT:** SPEC-FIRST (batch) · `[CACHE][TEST]`.
- **Files:** `js/ui-render.js` (`emptyState` callers), `index.html` static empties (+ tests).
### MN-8 · [MICROCOPY] image-upload control near `index.html:1748` (`triggerImageUpload`)
- **What:** "Upload"-style optic-scan trigger.
- **Why:** "Upload" is modern; the app already has the `[VISUAL UPLOAD]` command vocabulary.
- **In-world fix:** `> VISUAL UPLOAD` / `OPTIC SCAN`. (Also audit IMPORT/EXPORT SAVE labels — "IMPORT/EXPORT" reads OK as TERMINAL ops; keep but ensure `> ` prefix consistency.)
- **VERDICT:** BUILD-NOW (copy) · `[CACHE][TEST]`.
- **Files:** `index.html` (+ tests).
### MN-9 · [VOICE] `js/api.js` error tails (1211, 1328, 1361, etc.)
- **What:** In-world `⚠ [SYS]` wrappers with modern-polite tails: "…Please try again.", "Re-enter it in the API KEY field.", "Wait and try again later."
- **Why:** Register cross — terse machine voice broken by polite phrasing.
- **In-world fix:** `RETRY TRANSMISSION.`, `RE-ENTER ACCESS KEY.`, `AWAIT QUOTA RESET.`
- **VERDICT:** SPEC-FIRST · `[CACHE][TEST]`.
- **Files:** `js/api.js` (+ tests).
---
## POLISH
### PO-10 · [VOICE] `index.html:73` update-modal message
- **What:** "A new version of RobCo U.O.S. is ready. Reboot to load it — the terminal must update to continue."
- **Fix:** `FIRMWARE UPDATE STAGED — REBOOT REQUIRED TO APPLY.` (Button `> REBOOT TERMINAL` already canon.)
- **VERDICT:** BUILD-NOW (copy) · `[CACHE][TEST]`. Files: `index.html`.
### PO-11 · [CONSISTENCY] `> ` prefix uniformity across panel `<h2>`/`<h3>`
- **What:** Mix of `> ` and `> ` (both render `>` — fine) and a few headers/buttons missing the prompt prefix.
- **Fix:** Ensure every panel header + primary button carries the `> ` prompt prefix consistently.
- **VERDICT:** SPEC-FIRST (batch) · `[CACHE][TEST]`. Files: `index.html`.
### PO-12 · [TERMINOLOGY] `index.html:1207` `> ACCOUNT` header
- **What:** "ACCOUNT" is borderline-modern.
- **Fix (optional):** `> OPERATOR UPLINK` / keep `ACCOUNT` (acceptable). Low priority.
- **VERDICT:** SKIP-or-SPEC (owner call). Files: `index.html`, `js/ui-account.js`.
---
## TENSIONS (logged, NOT auto-applied — would trade usability/safety for immersion)
- **TN-13 · A11Y on dynamic content:** `aria-label` = 8 in static HTML, **0 in JS-rendered rows** (inventory/skills/collectibles/recipes); `alt=` = 1. Not a diegetic break, but any copy reframe **must keep ARIA literal** and ideally *add* literal labels to rendered rows. Reframing must never move a screen-reader string into diegetic jargon (HOUSE_STANDARD §3/§5). → feed to a separate a11y pass; do not diegetic-ize.
- **TN-MJ5 · Gemini key label:** narrative error reframes to "DIRECTOR ACCESS KEY", but the **config field label stays literal "Gemini API Key"** (user must paste the right value). Two registers, intentionally.
- **TN-MN6 · placeholder clarity:** caps yes, obscurity no — don't sacrifice comprehension for register.
- **TN-CR1 · reduced-motion is the resolution, not a tension:** calming motion *improves* usability and *preserves* the static CRT look — apply it.
---
## Counts
**By severity:** CRITICAL 1 · MAJOR 4 · MINOR 4 · POLISH 3 · TENSIONS 4 (+ ~6 GOOD/canon anchors noted, no action).
**By dimension:** Voice© 5 (MJ-2, MJ-5, MN-9, PO-10, MN-7) · Terminology 3 (MJ-4, MJ-5, PO-12) · Visual/chrome 0 (palette/chrome already clean) · Interaction/motion 1 (CR-1) · System states 3 (MJ-3, MN-7, CR-1) · Shell/boot 0 (canon) · Auth-as-diegesis 1 (MJ-2) · Out-of-frame 1 (MJ-4) · Microcopy 3 (MN-6, MN-8, PO-11) · Internal consistency 2 (MN-7, PO-11).
**Build order suggestion for the synthesis stage:** CR-1 (a11y/safety, own push) → MJ-2/MJ-3 (account copy, one push) → MJ-4 (manifest, served + tests) → MJ-5/MN-9 (api error voice) → MN-6/MN-7/PO-11 (batched copy/register sweep) → MN-8/PO-10 (button copy). All copy-level fixes are `[CACHE][TEST]`, none touch `[AUTH]` mechanics or `[DATA]`/`[NET]` paths, none bump APP_VERSION.
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).