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

SKIN_ARCHITECTURE_EXTRACTION.md



RELEASE

planning/2.8.5/audits/SKIN_ARCHITECTURE_EXTRACTION.md

sha256 15f4828291a1649f · 15811 bytes · original held in the private archive

# SKIN-ARCHITECTURE EXTRACTION PASS — measuring the per-game abstraction from two real machines **Analysis only. No code written, no source edited, no commit/push.** Branch clean at `5e36a52`. **What this is:** the owed post-FO3 measurement — now that both machines are real and built (NV = salvaged desk terminal; FO3 = Pip-Boy 3000), measure from the actual code how much of FO3 was genuinely per-game vs shared, decide the real skin seams, and give the FO4 (3.0) file-drop-vs-refactor verdict. Every number below is grepped/measured from `5e36a52`, not estimated. --- ## THE THREE HEADLINES (for the phone) 1. **Per-game-vs-shared:** FO3's *entire* divergence from NV is **~2,100 CSS lines in one quarantined file (≈13% of all CSS), ~5 identity data fields, and effectively ZERO forks in code** — no second render path, no game-name branches in feature code. The abstraction held far better than "reskin-only" feared. 2. **The real seam:** keep the chrome as per-game CSS in a dedicated file (what FO3 did — it works), but **it's keyed to the wrong thing: the GAME (`[data-game='FO3']`), not the MACHINE FAMILY.** That's the one place the current approach strains. 3. **FO4 verdict:** **a clean file-drop for a flat-view FO4; ONE small, already-scoped refactor if FO4 wants the Pip-Boy body** (it's a Pip-Boy Mk IV, so it probably does) — re-key the Pip-Boy shell from `[data-game]` to a machine attribute. Not a surprise, not big. --- ## 1 · THE TEMPTATION LIST (spec from evidence) **A maintained list already exists — it was not lost.** The FO3 work kept it in two places, and it is the living spec Discipline 4 asked for: - `planning/FO3_PIPBOY_DESIGN.md` §7 — the original **15** temptations, each "what tempted a fork / how FO3 resolved it without one," plus **4** real-device-pass additions (16–19). - `planning/FO3_BUILD_PLAN.md` §10 — carries those forward with **concrete code/CSS/JS seam evidence** attached to each, and adds build-observed items (16–24). The union is ~24 distinct temptation points. Rather than re-transcribe them, here is the **reconstructed-from-code enumeration of every per-game fork point that actually exists in the shipped tree today** — which is the real spec, and confirms the list is honest: | Fork point (what could have forked) | How it's expressed today | Evidence (grepped) | |---|---|---| | **Nav grammar** (NV 7-keycap row · FO3 3 lamps + knob + gauge + toggle) | Same `#navkey-*` buttons + `selectSubsystem()`; CSS re-skins/re-positions them under `[data-game='FO3']` landscape | one `.nav-cluster`, no forked nav DOM | | **Live-status location** (NV bezel LCD · FO3 in-glass top strip) | One writer, two projection targets; `#fo3TopStrip` `hidden` for NV | `identity.statusStrip` (3 reads) | | **Board grouping / 2nd nav axis** (FO3 groups 22 boards onto 15 sub-tab rails; NV = stacked accordion) | `identity.rails` data → `_applyRailGrouping()` stamps `data-subtab` → one FO3-scoped CSS `:not(.subtab-active){display:none}` rule | `identity.rails` (5 reads); NV has no `rails` key → axis dormant | | **Orientation model** (FO3 landscape-true / portrait-legacy · NV agnostic) | Pure CSS `@media(orientation:landscape)`, FO3-scoped | `identity.orientation` (1 read, flag only) | | **Control language** (NV faders/steppers · FO3 `▲▼` boxes, 4 primitives) | Shared handlers; per-game control-kit CSS classes | in the FO3 file, no handler fork | | **The figure** (FO3 Vault Boy · NV zone plate) | Inline SVG rendered by JS into shared markup, `aria-hidden`, phosphor-token strokes; the 5 real limb toggles stay the shared `toggleLimb()` buttons | Vault Boy built in `js/ui/ui-core.js`, not static DOM | | **Nav lamp labels** (STATS/ITEMS/DATA) | `identity.navLamps` data → `_applyFo3NavLabels()` no-op without key | `identity.navLamps` (2 reads) | | **Wire/accent color + its usage sparsity** | `--bezel-wire` token per game; WHERE it's spent = per-skin CSS | 12 `[data-game='FNV']` selectors (the accent) | | **Sounds / motion / boot / persona / overseer / databank / cursor / ambient** | All already `identity` data facets | `getIdentity()` — 22 call sites total | | **Directory key / hotkey glyphs / panel presence** | aria keeps semantics; glyphs are CSS-from-identity; rails consume `GAME_DEFS` capability flags | no game literal in the render path | **The list is accurate and the discipline held:** every fork point above resolves to either (a) identity DATA, (b) a CSS rule scoped `[data-game]`, or (c) CSS re-layout of shared markup — never a JS game-name branch in feature code, never a second renderer. --- ## 2 · ⭐ THE MEASUREMENT — how much of FO3 was genuinely per-game vs shared **This is the owner's answer, in real counts.** ### CSS - **Total app CSS: 15,932 lines across 12 order-prefixed files.** - **FO3-specific CSS: 2,104 lines — 100% in ONE file (`css/60-fo3-pipboy.css`) = 13.2% of all CSS.** - **Of that FO3 file, ~2,022 lines (~96%) live inside a SINGLE `@media(orientation:landscape)` block** (lines 59–2081) — i.e. the FO3 divergence is almost entirely "the landscape Pip-Boy re-body," cleanly quarantined. - **`[data-game='FO3']`-gated selectors: 279 total — 278 in the FO3 file, exactly 1 elsewhere** (a `[data-game='FO3'] .seat` motion-verb texture in `10-chrome.css`). - **`[data-game='FNV']`-gated selectors: 12** (all the amber "wire" accent, in `10-chrome.css`). - **Read that ratio carefully: NV is the *un-gated default* skin; FO3 is a pure 278-selector override layer, 99.6% contained in its own numbered file.** The shared files (`25-toolbar`, `30-modulebay`, `35-operator-boards`, `40-curio-operations`, `45-databank`, `50-chassis`, `15-overseer`) carry the shared machine + NV default, essentially untouched by FO3. ### JS - **Per-game game-name branches in feature code: effectively ZERO.** A grep for `=== 'FO3'` / `=== 'FNV'` / `ctx===` / `gameContext===` across `js/` returns 6 hits: 3 are comments or data-file header lines (`db_fo3.js`, `reg_fo3.js`, a `ui-render-factions.js` comment), 1 is a **dev-tool gate** (`test-console.js:4656` `getGameContext() !== 'FNV'`). **None are in the render / state / api / cloud pipeline.** - **Per-game behavior is data-driven via `getIdentity()` — 22 call sites**, reading `rails`(5), `theme`(4), `statusStrip`(3), `navLamps`(2), `persona`, `overseer`, `orientation`, `motionTexture`, `databank` (+ `audio`/`voice`/`ambient`/`ceremony`/`cursor` through sibling accessors). ### DOM - **~22 shared `.panel[data-tab]` boards, ONE markup each, ONE render path. Zero `renderXxxFO3()` functions exist** (grep confirms no forked renderer). FO3 reuses every board. - **FO3's only *added* DOM: 3 structural wrappers** (`#fo3TopStrip`, `#fo3SubtabRail`, `#fo3BoardScroll` — the last is a shared, inert-for-NV scroll wrapper) + a handful of `aria-hidden` casing cosmetic divs + the JS-rendered Vault Boy SVG. **10 `data-game` literals total in `index.html`** (the flavor-span pairs + the wrappers). - **The spine audit (`AUDIT_FO3_SPINE.md`) independently confirmed:** "No game branch / no forked render pipeline — CONFIRMED," 22 boards keep single shared markup, and the new shared wrappers render identically (`hidden`) for NV. ### Which shared panels needed FO3-specific layout vs rode unchanged - **Rode the shared render unchanged (behavior):** all 22 boards — FO3 changed their *skin and grouping*, never their render function or handlers. STATUS is the extreme case: an alternate CSS-revealed markup block wired to the *same* limb/HP/RAD handlers (temptation 18 resolved). - **Needed FO3-specific *layout* (CSS only):** the boards that fold into rails + the top-strip/sub-tab-rail re-flow — all expressed as the 278 gated selectors, no render change. **Bottom-line measurement:** **FO3 = ~2,100 CSS lines (13% of CSS) in one file + 5 identity data fields + 3 wrapper divs + 0 render forks + 0 feature-code game branches.** The `identity` + `[data-game]` + shared-render abstraction absorbed an entire second machine — including a different orientation model and a different nav grammar — with the divergence quarantined to a single file and a handful of data keys. That is a genuinely good result, not a strained one. --- ## 3 · THE REAL SKIN SEAMS (against both machines) **Device chrome — is it best as (a) per-game DOM in the page, CSS-gated by `[data-game]`, or (b) injected/selected at boot like `GAME_FILES` does for data?** **FO3 pushed decisively toward (a), and (a) won on the merits — keep it.** The feared downside of (a) ("dead DOM accumulates per game") **did not materialize**: FO3 added ~3 wrapper divs and one JS-rendered SVG, not a parallel DOM tree, because it *reuses the shared boards* and expresses its whole body as CSS re-layout in one file. Option (b)'s machinery (a boot-time chrome injector/selector) is **not justified** — there is almost no per-game DOM to select, and the one-file-per-skin rule (`60-fo3-pipboy.css`, a third game *adds* a file) already gives clean isolation with zero runtime machinery. Boot-injection would be solving a problem the code doesn't have. **Where `[data-game]` gating genuinely strained — the one real finding:** the chrome is keyed to the **GAME**, not the **MACHINE FAMILY**. The entire 2,022-line Pip-Boy shell is reachable only through `[data-game='FO3']`. But a Pip-Boy is not unique to FO3 — **FO4's identity is literally `machine: 'pipboy-3000-mk4'`**. The gate conflates "which game is loaded" with "which physical chrome to render," and those are not the same axis. The moment a second Pip-Boy game arrives, `[data-game]` forces a false choice: duplicate ~2,000 lines as `[data-game='FO4']`, or re-key. **The identity block already anticipated this seam — and left it unwired.** Every identity (FNV/FO3/FO4) declares **`machine`**, **`material`**, and **`structuralMode`** — and `material`/`structuralMode` are **read by exactly zero lines of code** (grep-confirmed: declared, never consumed). They are recorded intent for precisely the chrome-family selector that doesn't exist yet. **Recommendation (reasoned from what NV+FO3 showed):** - **Keep chrome as option (a)** — per-game CSS, one numbered file per skin, minimal shared-DOM wrappers. Do **not** build a boot-time chrome injector. - **Re-key the chrome gate from the GAME to the MACHINE/CHROME FAMILY.** Set a `data-machine` (or `data-chrome`) attribute on `<html>` from `identity.machine`'s family (wiring the already-declared, currently-dead `machine`/`structuralMode`), and change the Pip-Boy shell's 278 selectors from `[data-game='FO3']` to `[data-machine='pipboy']` (or a computed `[data-chrome='pipboy']`). Then **both FO3 and FO4 share the one shell**, and only their *true* differences (Mk IV clean vs FO3 clunk — colours/material/motion, already identity data) stay `[data-game]`-gated. This is a mechanical selector re-key of one file, not a re-architecture. --- ## 4 · THE FO4 VERDICT — file-drop or refactor? **Split by layer, because the two layers scale very differently:** ### The RESKIN layer → clean data/file-drop. ✅ FO4's `designOnly: true` identity stub (`state.js:1840–1948`) **already validates the full reskin contract** — persona, ceremony, motionTexture, cursor, audio, voice, ambient, overseer, databank, theme. Protocol 38's `GAME_FILES` boot manifest already handles per-game *data* file selection. So a flat-view FO4 is: add `db_fo4.js` + `reg_fo4.js`, one `GAME_FILES` manifest line, a `#gameContextSelect` `<option>`, flip `designOnly`. **No skin-system change.** This half is a genuine file-drop, exactly as designed. ### The RE-BODY layer → ONE scoped refactor, IF FO4 wants the Pip-Boy body. ⚠️ Two concrete gaps, both from evidence: 1. **FO4's stub carries the reskin facets but NOT the re-body facets** — it has `machine: 'pipboy-3000-mk4'` but **no `rails`, no `orientation`, no `statusStrip`, no `navLamps`** (grep-confirmed against lines 1840–1955). As stubbed, FO4 would render as the **flat NV-style view**, not a Pip-Boy. So the design-only stub proves the reskin generalizes to N games; **it does not prove the re-body generalizes.** 2. **The Pip-Boy shell CSS is hard-keyed `[data-game='FO3']`**, and **the only per-game root hook that exists is `[data-game]`** (no `data-machine`/`data-chrome` anywhere — grep-confirmed). So a Pip-Boy FO4 cannot reach the shell without duplicating it. **Verdict:** **FO4 is a file-drop *plus* one small, scoped pre-3.0 unit — and which one depends on a single owner decision: is FO4 a Pip-Boy body, or a flat view?** - **If FO4 ships flat** (no Pip-Boy chrome): **pure file-drop, zero refactor.** - **If FO4 ships as the Pip-Boy Mk IV** (its `machine` strongly implies this): **one scoped refactor unit, nameable exactly:** - **(i) Re-key the ~278 Pip-Boy shell selectors** in `60-fo3-pipboy.css` from `[data-game='FO3']` to a machine-family attribute; **wire the already-declared `identity.machine`/`structuralMode`** onto `<html>` as `data-machine`/`data-chrome` (the currently-dead seam). *(This is the §3 recommendation — it lands here.)* - **(ii) Give FO4's identity the re-body data** (`rails`, `orientation`, `statusStrip`, `navLamps`) — pure data, and the game-agnostic stampers (`_applyRailGrouping`/`_applyRails`/`_applyFo3NavLabels`) already consume it with no code change. - **(iii) Guard it:** a gate test that the shell selectors key off `data-machine`, not `data-game`, and that a second Pip-Boy game reuses the shell without duplicating it (extends the existing "no forked pipeline" guards). - Scope: **one file's selectors + a few identity data fields + one attribute write + one test.** Not a re-architecture, not a surprise — it is the *pre-3.0 unit this whole extraction pass exists to name.* ### What should later fold in (recommend — do NOT edit these now) - **`GAME_DEFS`/`identity`:** wire `machine`/`structuralMode`/`material` into a real `data-machine`/`data-chrome` root attribute (they are declared-but-dead today); add `rails`/`orientation`/`statusStrip`/`navLamps` to FO4 if it goes Pip-Boy. Record "chrome family ≠ game" as the identity block's design rule. - **`ARCHITECTURE.md` / Protocol 38:** document the chrome-family seam — the sanctioned extension is "chrome selectors key off `data-machine` (machine family), flavour keys off `[data-game]`" — so a future reviewer doesn't read the FO3-era `[data-game]` shell gate as the intended N-game pattern. - **`library/BRAIN_DUMP.md`:** update the per-game-layer section — it was written for reskins; record that the re-body layer exists, is one quarantined file, and needs the machine-family re-key before a second same-chassis game. - **`QUEUE.md`:** add the pre-3.0 "Pip-Boy shell → machine-family re-key" unit as a *conditional* (gated on the FO4-body decision), so it's planned, not discovered mid-3.0. --- ## END — the three one-line answers - **Per-game-vs-shared headline:** FO3's whole divergence = **~2,100 CSS lines in one file (13% of CSS) + ~5 identity data fields + 3 wrapper divs + 0 render forks + 0 feature-code game branches** — the abstraction held. - **Recommended seam:** keep chrome as per-game CSS in its own file (option a — FO3 proved it), but **re-key it from the GAME (`[data-game]`) to the MACHINE FAMILY (`data-machine`, wiring the already-declared-but-dead `identity.machine`/`structuralMode`)**. - **FO4 verdict:** **file-drop for the data/reskin; ONE scoped pre-3.0 refactor unit — the `[data-game='FO3']` → `data-machine='pipboy'` shell re-key + FO4 re-body identity data — only if FO4 takes the Pip-Boy body (which its `machine: 'pipboy-3000-mk4'` stub implies). Flat-view FO4 = pure file-drop.** *Analysis only. No code changed, no source edited, no commit, no push.*
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).