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

SETTINGS_TAB_PLAN_V2.md



RELEASE

planning/2.8.0/plans/SETTINGS_TAB_PLAN_V2.md

sha256 ba4b3687db48bb46 · 32958 bytes · original held in the private archive

# SETTINGS Tab `[6]` + CHASSIS Reorg — Opus Plan V2 (Protocol 8, Stage 1) **Branch:** `dev` · **HEAD:** `8f45abb` · **APP_VERSION:** `2.7.0` · **Cache:** `robco-terminal-v2.7.0-r69` · **Tests:** `2206/2206` across **176 suites** (last suite = **175**; new suite = **176**) **Design source:** `planning/mockups/settings-tab.html` + `settings-tab-notes.md` + `settings-tab-*.png` (owner-approved 2026-07-04). **Supersedes:** `planning/SETTINGS_TAB_PLAN.md` (that plan predates the CAMPAIGN-CONFIGS move and resolved CHASSIS differently — see §0.1). **Scope:** PLAN ONLY. No code edits. Implementation is a later Sonnet/Opus pass, split into the sub-units in §2. --- ## 0. Re-verified current state (Protocol 27 — read from the live `dev` tree, not assumed) | Thing | Where it lives NOW | Anchor / mechanism | | --- | --- | --- | | Bezel nav markup | `index.html` `.nav-cluster[role=tablist]` **L312–368**: 5 `role=tab` keycaps `navkey-operator/operations/databank/uplink/chassis` + `navkey-directory` (**L372–380**, DIR, OUTSIDE the tablist, in `.nav-row`) | 6 buttons; DIR `[0]` opens a modal | | Tab machinery | `ui-core.js`: `TAB_NAMES=['stat','inv','data','campg']` **L2496**, `_DATABANK_TABS=['data','campg']` **L2502**, `TAB_TO_SUBSYSTEM` **L2503**, `switchTab()` **L2510**, `initTabs()` **L2540** | `.panel[data-tab]` get `.tab-visible` toggled; databank merges data+campg | | Bezel routing | `ui-core.js`: `NAV_KEYS` **L2555**, `_NAV_TAB_FOR={operator:'stat',operations:'inv',databank:'data'}` **L2556**, `_syncBezelNav()` **L2637**, `_bezelSubsystemLabel()` **L2654**, `selectSubsystem()` **L2742**, `openBezelDirectory()` **L2778**, `initBezelSubsystem()` **L2807** | keycap → tab OR scroll-to | | Hotkeys | `ui-core.js` `hotkeyMap {1:operator..5:chassis}` **L1929**, `'0'`→`openBezelDirectory()` **L1939** | keydown, guarded against INPUT/TEXTAREA/SELECT | | `#go=` deep-links | `ui-core.js` `SHORTCUT_ROUTES {comm,inv,stat,data,new}` **L2821**, `routeLaunchShortcut()` **L2838**, allow-list `^go=([a-z]+)$` | PWA shortcuts | | Device prefs | `state.js` `META_MANIFEST`: `robco_active_tab`, `robco_bezel_subsystem` (default `'operator'`), `robco_scroll_positions`, `robco_panel_state`, `robco_bay_opened` | MetaStore | | **Account** | `#accountPanel` **L1617–1627**, `data-tab="data"` → under **DATABANK** (NOT chassis). Body `#accountBody` filled by `renderAccount()` (`ui-account.js` **L5–37**, plain text, no board). Has a stale note "Manage saves in Security & Configuration below." | `renderAccount()` | | **Module Bay** | `#securityConfigPanel` **L1629–1631**, a top-level `.panel` with **NO `data-tab`** → always-visible in `.col-left`. Contains `#moduleBay` **L1640**, bay hatch, SLOT boards, SVC TRAY **L2302–2341**, schematic. Reached by `selectSubsystem('chassis')` which **scrolls to `#moduleBay`** (`ui-core.js` **L2758–2768**). Panel closes **L2535**. | `initModuleBay()`/`renderModuleBay()`; hatch on this panel's own first user-toggle | | **Saves / SAVE ARCHIVE** | **Nested INSIDE `#securityConfigPanel`**, **L2369–2533** (`#savesListBody` L2396, `#btnSaveToCloud`, `#btnSyncSlotsToCloud`, EXPORT/IMPORT/RESTORE/FULL BACKUP, SAVE/LOAD A–C slots, `#slotStatus` L2526, `#fileInput` L2530) | `renderSavesList()` (`ui-account.js`) | | **FIRMWARE REVISION LOG** (changelog) | Button `#btnViewChangelog` **L2330** inside the **SVC TRAY** (`onclick="_svcViewChangelog()"`, `ui-core.js` **L691**; renderer `_showChangelogModal()` **L3102**) | modal | | **ERROR LOG** | `showErrorLog()` (`ui-core.js` **L3365**), reachable only via the `[LOGS]` native command (`api.js` **L1097**) — no button today | `#sysModal` | | **Overseer's Log** | `#overseerLogPanel` **L1366–1385**, `data-tab="data"` → **DATABANK**. Two halves: `> UNIT TELEMETRY` (`#overseerLogDisplay`, `renderOverseerLog()` — uptime/total power-on/boot count/longest session) + `> CAMPAIGN LOG` (`#sessionStatsList`, `renderSessionStats()` — kills/caps/dmg/collectibles/visits/current-sitting) + RESET CAMPAIGN STATS **L1374** | `renderOverseerLog()` + `renderSessionStats()`; both called from `loadUI()` | | **Campaign configs** | `#campgPanel` **L1396–1614**, `data-tab="campg"` (part of DATABANK). Controls: `#gameContextSelect` (`onGameContextChange`), `#playstyleInput` (`changePlaystyle`), `#playthroughTypeSelect` (`onPlaythroughTypeChange`), `#completeRngToggle` (`onCampaignModeChange`) + banners, DANGER ZONE `#wipeTerminalBtn` (`wipeTerminal`). Record sub-panels: `#campaignStatusPanel`/`data-sub-id="campaign_status"`, `data-sub-id="crossroads_record"`, `data-sub-id="incident_log"` | plain `<select>`/checkbox, inline styles | | Connection choke point | `refreshOverseerCarrier()` (`ui-core.js` **L1513–1537**) — already runs on `online`/`offline`, on key edits via `saveApiKeySilent()`, and at boot; already calls `_updateUplinkLamp()` + `_refreshBezelTelemetry()`. Connection truth: `_isUplinkConnected()` **L761** (wraps `_overseerRestState(_overseerRestSignals())`) | single source | | Panel persistence | `_wirePanelPersistence()` (`ui-core.js` **L1653**) — `securityConfigPanel` EXCLUDED from desktop default-open **L1683**; hatch wired to its toggle **L1707** | `robco_panel_state` | | Feature flags | `isFeatureEnabled(key)` (`cloud.js` **L127**), LKG cache `robco_feature_flags`; keys incl. `aiChat`/`cloudSync`/`googleSignIn` | fail-open | ### 0.1 What changed since `SETTINGS_TAB_PLAN.md` 1. **Tree moved a lot:** r56→**r69**, 2126→**2206** tests, last suite 168→**175**. Every line anchor above is re-verified against `8f45abb`. 2. **CAMPAIGN CONFIGS are now in-scope** for SETTINGS (owner decision #2) — the old plan omitted them entirely. 3. **CHASSIS decision is now LOCKED and different** (owner decision #3): the old plan's Open-Question-#1 recommendation ("move the whole Overseer's Log to CHASSIS") is **superseded**. The Overseer's Log is **SPLIT**: device-telemetry half → a new **SYSTEM STATUS** panel on CHASSIS (merged with version/cache + carrier + feature-flags + the relocated FIRMWARE REVISION LOG + ERROR LOG access); campaign-stats half → a new **CAMPAIGN LOG** panel on DATABANK. 4. **FIRMWARE REVISION LOG moves out of the SVC tray** → CHASSIS SYSTEM STATUS (owner decision #3). 5. **ERROR LOG gets a button** on SYSTEM STATUS (owner decision #3) — today it's command-only. --- ## 1. Locked owner decisions (this plan covers every one) 1. **7th bezel keycap `[6] SETTINGS`** (sub-label `CONFIG·ACCT`) between `CHASSIS[5]` and `DIR[0]`; all 7 caps one centered row at 360–412; hotkey `[6]`; `#go=settings`; `robco_bezel_subsystem` restore; `role=tab` ARIA; every DO-N invariant + the existing 6 subsystems' routing preserved. 2. **SETTINGS tab = ACCOUNT (REG PORT board) + Module Bay (SLOT 01–05 + SVC TRAY) + SAVE MENU/saves + CAMPAIGN CONFIGS**, all collapsible. Removed from their current tabs. 3. **CHASSIS reorg:** new **SYSTEM STATUS** panel = device-telemetry half of Overseer's Log (uptime/total power-on/boot count/longest session) **merged** with version/cache-rev + carrier/connection + feature-flag status (no duplicated uptime) + the **FIRMWARE REVISION LOG** moved here out of the SVC tray + **ERROR LOG** access. The **campaign-stats half** (kills/caps/dmg/collectibles/visits + RESET) → **DATABANK** as its own **CAMPAIGN LOG** panel. 4. **CAMPAIGN CONFIGS modernized** to the Module-Bay/Director-Uplink aesthetic + the "2281 modern-UX" north star — not a plain relocate. ⚠ Mockup recommendation in §5. 5. **Dynamic ACCOUNT status words** — the REG PORT board's diegetic text reflects REAL auth + cloud-sync state and LIVE-updates, routed through `refreshOverseerCarrier()` (single source; no hardcoded strings). **Architecture decision (unchanged from V1, still correct):** make `settings` AND `chassis` real tab values (`data-tab="settings"` / `data-tab="chassis"`), exactly like `stat`/`inv`/`data`/`campg`. This reuses the entire shipped machinery — `.tab-visible` show/hide, `_syncBezelNav` highlight/telemetry, per-subsystem scroll memory, `robco_active_tab` reload restore — with zero forked routing (Protocol 22). CHASSIS today is a bespoke "always-visible + scroll-to `#moduleBay`" hack; once the bay moves to SETTINGS, CHASSIS has no target, so it MUST become a proper tab-gated subsystem holding SYSTEM STATUS. --- ## 2. ⭐ Recommended sub-unit split (dependency-ordered) Too big for one commit. Four sub-units; **SU-1 is the backbone and must land first**; SU-2/SU-3/SU-4 are independent of each other and can follow in any order (SU-3 gated on its mockup — §5). ``` SU-1 (backbone) ──┬── SU-2 CHASSIS reorg + SYSTEM STATUS + CAMPAIGN LOG→DATABANK ├── SU-3 CAMPAIGN CONFIGS modernize (needs Fable mockup first) └── SU-4 Dynamic ACCOUNT REG PORT words ``` ### SU-1 — Bezel `[6]` keycap + tab-shell + relocation + routing The structural move. **Functional relocation only — no visual redesign of the moved content** (that's SU-3/SU-4). - Add the 7th keycap; make `settings` + `chassis` real tabs; wire hotkey `[6]`, `#go=settings`, DIRECTORY row, telemetry label, scroll memory. - Relocate **Account**, **Module Bay** (`#securityConfigPanel` incl. SVC tray *minus* the firmware-log button — see SU-2), **Saves**, and the **campaign-config CONTROLS** into `.col-left` as `data-tab="settings"` panels. Remove them from DATABANK/always-visible. - Convert `chassis` from scroll-to-bay into `switchTab('chassis')`; it is **temporarily empty** at the end of SU-1 (SYSTEM STATUS lands in SU-2). To avoid shipping an empty tab mid-stack, SU-1 may either (a) be committed together-with SU-2 as one logical CHASSIS-completing push (Protocol 19 batch), or (b) park a one-line "SYSTEM STATUS — pending" placeholder panel on `chassis`. **Recommend (a): land SU-1+SU-2 as one push** so CHASSIS is never shipped empty. SU-3/SU-4 remain separate pushes. - First-visit hatch fires on first `[6]` visit (reuse `robco_bay_opened`). **Acceptance (SU-1):** 7 caps in one centered row, no h-overflow at 360/412; `[1]-[6]`+`[0]` route; `#go=settings` routes; SETTINGS shows Account→Module Bay→Saves→Campaign-Configs (functional), each collapsible; those 4 groups ABSENT from every other tab; every id + handler preserved (Protocol 22); reload restores SETTINGS + scroll; gate green + render-check. ### SU-2 — CHASSIS SYSTEM STATUS + split Overseer's Log - New `#systemStatusPanel data-tab="chassis"`: hosts the **device-telemetry half** (move `#overseerLogDisplay` + `renderOverseerLog()` here, ids unchanged) + a new `#systemStatusDisplay` readout (APP_VERSION, cache/firmware rev, CARRIER ONLINE/OFFLINE via `_isUplinkConnected()`, feature-flag states via `isFeatureEnabled()`) + the **FIRMWARE REVISION LOG** button moved out of the SVC tray + a new **ERROR LOG** button (`showErrorLog()`). - New `#campaignLogPanel data-tab="data"` (DATABANK): hosts the **campaign-stats half** (move `#sessionStatsList` + `renderSessionStats()` + RESET CAMPAIGN STATS here, ids unchanged). - Retire the combined `#overseerLogPanel` container (its two children move out); wire `renderSystemStatus()` into `refreshOverseerCarrier()` so the carrier/flag lines live-update at the same choke point. **Acceptance (SU-2):** CHASSIS shows SYSTEM STATUS (telemetry + version + carrier + flags + firmware-log + error-log buttons), no duplicated uptime; DATABANK shows CAMPAIGN LOG (stats + reset); SVC tray no longer has the firmware button; carrier/flag lines flip live on online/offline + key add/remove; both render fns still fire from `loadUI()`; gate green. ### SU-3 — CAMPAIGN CONFIGS modernized *(gated on a Fable mockup — §5)* Restyle the relocated config controls (GAME/PLAYSTYLE/PLAYTHROUGH/COMPLETE-RNG/DANGER-ZONE) to the Module-Bay/Director-Uplink aesthetic. Behavior + handlers unchanged (Protocol 22/25 — stored semantics identical). **Acceptance (SU-3):** every control keeps its exact `id` + `onchange`/`onclick`; visual pass matches the approved mockup at 360/412/desktop; no h-overflow; gate green. ### SU-4 — Dynamic ACCOUNT REG PORT words Rewrite `renderAccount()` to paint the amber REG PORT / ID-card board and drive its diegetic status from real auth + cloud-sync state; live-update via `refreshOverseerCarrier()`. Detail in §4. **Acceptance (SU-4):** words flip live — sign-in → "OPERATOR VERIFIED / SYNC AVAILABLE"; sign-out → "REG PORT VACANT / SYNC OFFLINE"; offline-while-signed-in → "CLOUD UNREACHABLE"; flag-off → no button; LED + summary + board line agree; no reload; gate green. --- ## 3. File-by-file change map (exact targets) ### 3.1 `index.html` - **Bezel (SU-1):** insert `#navkey-settings` `role="tab"` after `#navkey-chassis` (L367), before the DIR button (DIR stays outside `role=tablist`). Mirror the sibling markup: `[6]` `nk-num`, `SETTINGS` `nk-label`, `CONFIG·ACCT` `nk-sub`, `onclick="selectSubsystem('settings')"`, `aria-label` + hotkey 6, plus a `.setkey` class for the amber service-stripe. Update `#navkey-chassis` `aria-label` from "Module Bay, settings and saves" → "system status & telemetry (hotkey 5)" and its `nk-sub` stays `SYSTEM`. - **Account (SU-1 move / SU-4 restyle):** `#accountPanel` L1617 → `data-tab="data"` becomes `data-tab="settings"`; move to be the FIRST settings panel (source order). Drop the stale "Manage saves…" note L1624–1626. Add a summary status `<span id="acctSummaryStatus" class="panel-substatus">`. SU-4 restyles `#accountBody` into the amber board. - **Module Bay (SU-1):** `#securityConfigPanel` L1629 → add `data-tab="settings"`; keep everything inside unchanged EXCEPT extract the two blocks below. Place it as the 2nd settings panel. - **Firmware-log button (SU-2):** remove `#btnViewChangelog` (L2330–2332) from the SVC tray; re-home it in the CHASSIS SYSTEM STATUS panel. EJECT HOLOTAPE + INSTALL PWA stay in the SVC tray. - **Saves (SU-1):** extract the saves block (L2369–2533, i.e. the `input-group` through FULL BACKUP + SAVE SLOTS + `#slotStatus` + `#fileInput`) out of `#securityConfigPanel` into a NEW `<details class="panel" data-tab="settings" id="savesPanel"><summary><h2>&gt; SAVE ARCHIVE</h2></summary>…`. **Every id/handler verbatim** (`#savesListBody`, `#btnSaveToCloud`, `#btnSyncSlotsToCloud`, `exportSaveFile`, `triggerFileInput`, `restoreRollingBackup`, `exportFullBundle`, `saveToSlot`/`loadFromSlot`, `#slotStatus`, `#fileInput`, `showSaveHelpModal`). Place 3rd settings panel. - **Campaign-config controls (SU-1 move / SU-3 restyle):** extract GAME/PLAYSTYLE/PLAYTHROUGH/COMPLETE-RNG + banners + DANGER ZONE (from `#campgPanel` L1401–1613) into a NEW `<details class="panel" data-tab="settings" id="campaignConfigPanel"><summary><h2>&gt; CAMPAIGN CONFIGS</h2>…`. Place 4th settings panel. Keep every `id`+handler. ⚠ **Decision — the record sub-panels** (`#campaignStatusPanel`, `crossroads_record`, `incident_log`) are records, NOT config: recommend they STAY in DATABANK. Options: (a) leave `#campgPanel` in place holding only the 3 record sub-panels + rename its `<h2>` to `CAMPAIGN RECORD` (keeps `data-tab="campg"`); or (b) fold them into the new DATABANK CAMPAIGN LOG panel from SU-2. **Recommend (a)** — least churn, keeps `data-sub-id` persistence untouched. (See Open Question 2.) - **Overseer's Log split (SU-2):** the `#overseerLogPanel` L1366–1385 is dismantled — `#overseerLogDisplay` + its telemetry label move into the new `#systemStatusPanel data-tab="chassis"`; `#sessionStatsList` + RESET button move into the new `#campaignLogPanel data-tab="data"`. Add the new SYSTEM STATUS readout markup (`#systemStatusDisplay`, firmware-log button, error-log button). - **Contiguity:** the four `data-tab="settings"` panels adjacent + ordered Account→Bay→Saves→Campaign-Configs; the chassis + new databank panels grouped with their tab-siblings (source order = stacking order; `.tab-visible` hides the rest). ### 3.2 `js/ui-core.js` - `TAB_NAMES` L2496 → add `'settings'`, `'chassis'`. - `TAB_TO_SUBSYSTEM` L2503 → add `settings:'settings'`, `chassis:'chassis'`. - `_NAV_TAB_FOR` L2556 → add `settings:'settings'`, `chassis:'chassis'`. - `NAV_KEYS` L2555 → add `'settings'` (already has `chassis`). Auto-enrolls both in `_syncBezelNav`, scroll memory (`_scrollElFor` returns `#uiPanel`/page for both — no special case), telemetry. - `selectSubsystem()` L2742 → **remove** the `chassis` scroll-to-`#moduleBay` branch (L2758–2768); `chassis` now falls through `_NAV_TAB_FOR`→`switchTab`. Add the **first-visit hatch trigger** for `settings` (user-triggered path only; boot uses `initTabs`→`switchTab` directly, never `selectSubsystem`): if `MetaStore.get('robco_bay_opened')` is falsy, `setAttribute('open','')` on `#securityConfigPanel` — its existing toggle listener runs the once-only hatch. `uplink` branch unchanged. - Hotkeys L1929 → add `6:'settings'` to `hotkeyMap`. `[0]`/`[1]-[5]` unchanged. - `_bezelSubsystemLabel()` L2654 → add `case 'settings'` (e.g. `'▸ SUBSYSTEM: SETTINGS · CONFIG + REGISTRY'`); change `case 'chassis'` from `MODULE BAY` → `'▸ SUBSYSTEM: CHASSIS · SYSTEM STATUS'`. - `openBezelDirectory()` L2778 → add 7th row `['settings','SETTINGS','config &amp; account','CONFIG·ACCT · [6]']`; fix the `chassis` row desc from `'settings &amp; saves'` → `'system status &amp; telemetry'`. - `initBezelSubsystem()` L2807 → narrow the cosmetic restore from `(saved === 'uplink' || saved === 'chassis')` to `(saved === 'uplink')` — `chassis` (and `settings`) are now real tabs restored by `initTabs()` via `robco_active_tab`, so a cosmetic re-highlight would be redundant/conflicting. - `SHORTCUT_ROUTES` L2821 (SU-1, recommended) → add `settings: () => switchTab('settings')` so `#go=settings` works. Requires updating the Suite-158 `#go=` key-set assertion. - **SU-2:** add `renderSystemStatus()` (new); call it from `loadUI()` and add a call inside `refreshOverseerCarrier()` (L1513) so carrier/flag lines flip live at the same choke point that already drives the UPLINK lamp + bezel telemetry. `renderOverseerLog()` / `renderSessionStats()` bodies unchanged (their target ids `#overseerLogDisplay` / `#sessionStatsList` are preserved, just re-parented). ### 3.3 `js/ui-account.js` (SU-4) Rewrite `renderAccount()` (L5–37) to paint the amber REG PORT / ID-card board and set `#acctSummaryStatus` from real state (§4). `renderSavesList()` is unaffected (still targets `#savesListBody`). ### 3.4 `css/terminal.css` - **SU-1:** tighten `.navkey` width so 7 caps + DIR hold ONE centered row at 360/412 (mockup: `clamp(46px, 12.2vw, 88px)`; keep `.nav-cluster { flex-wrap … justify-content:center }` so an incomplete row centers — centering rule). The mobile bezel is `position:fixed` docked-bottom (Suite 160) — re-verify 7+DIR fit with no h-overflow. Add `.navkey.setkey::after` amber service-stripe. - `.panel-substatus` — one-line summary status span (small, dim, `white-space:nowrap;overflow:hidden;text-overflow:ellipsis`). - **SU-4:** amber REG PORT / ID-card styles (`.id-card`, `.id-photo`, `.id-lines`, `.id-status`/`.id-name`/`.id-mail`/`.id-note`) ported from the mockup, scoped to the account panel so they don't bleed. - **SU-3:** modernized campaign-config styles per the Fable mockup (§5). - `.panel[data-tab="settings"].tab-visible` / `[data-tab="chassis"]` inherit the existing attribute-generic `.panel[data-tab].tab-visible` rule — confirm no `[data-tab]` value is hardcoded anywhere that would exclude the new values. ### 3.5 `js/state.js` - **No new device pref needed** if the hatch reuses `robco_bay_opened` (recommended). `robco_bezel_subsystem` already free-form strings; `settings`/`chassis` are additive values (no schema change). If SU-3 adds any new toggle it follows Protocol 4/UI-6 + Suite 134 + `test.html` KNOWN_KEYS. ### 3.6 `sw.js` - Bump `CACHE_NAME` per commit that touches served files (Protocol 1): each of SU-1…SU-4 that edits `index.html`/`css/`/`js/`/`sw.js` increments `-rN` (r69→r70→…). If SU-1+SU-2 land as one push, that's one bump. --- ## 4. ⭐ Dynamic ACCOUNT (REG PORT) status wiring (SU-4) Reuse shipped signals — do NOT invent a parallel connection check (Protocol 22). **Inputs:** `window.getAccountState()` → `{uid,isAnonymous,email,displayName}` (`cloud.js` L258); `window.isFeatureEnabled('googleSignIn')`/`('cloudSync')`; `navigator.onLine` (or reuse `_isUplinkConnected()` for the carrier line to stay identical to the UPLINK lamp/bezel CARRIER). | Condition | LED | `#acctSummaryStatus` (collapsed line) | Board line | | --- | --- | --- | --- | | Signed out (anon/no uid), `googleSignIn` ON | off | `UPLINK OFFLINE — ARCHIVES STORED LOCALLY` | `NO OPERATOR ON RECORD` · `REG PORT VACANT — LOCAL ARCHIVES ACTIVE · CLOUD ARCHIVE SYNC OFFLINE` + ESTABLISH GOOGLE UPLINK button | | Signed out, `googleSignIn` OFF | off | `UPLINK TEMPORARILY UNAVAILABLE` | `LOCAL ARCHIVES ACTIVE` (no button) | | Signed in, online | on | `UPLINK ACTIVE · <name/email>` | `UPLINK ACTIVE · OPERATOR VERIFIED` + name/email + `CLOUD ARCHIVES + CIPHER-KEY SYNC AVAILABLE` + SEVER UPLINK button | | Signed in, offline | on (dim) | `UPLINK ACTIVE — CLOUD UNREACHABLE` | `OPERATOR VERIFIED · CLOUD ARCHIVE SYNC OFFLINE (NO CARRIER)` | All name/email `escapeHtml`'d (already done). Game-agnostic (Protocol 38 — generic operator/registry fiction, no game literals). **Live-update trigger points:** boot `loadUI()`→`renderAccount()` (exists); sign-in/out/collision in `cloud.js` already call `window.renderAccount()` at 3 sites; **connectivity** → add `renderAccount()` into `refreshOverseerCarrier()` (L1513) — same one-line addition SU-2 uses, keeping REG PORT words + UPLINK lamp + bezel CARRIER + SYSTEM STATUS carrier all synchronized through one choke point (they can't drift). --- ## 5. ⚠ Campaign-Configs mockup recommendation **Recommend: YES — a quick Fable mockup before SU-3 builds.** Rationale: - Every other SETTINGS surface already has an approved visual (bezel, REG PORT/ID-card, and the Module Bay ship verbatim in `settings-tab.html`). The campaign-config controls are the **one genuinely-new visual** with no approved design — today they're plain `<select>`/checkbox with inline styles. - Owner decision #4 asks for a real aesthetic uplift ("2281 modern-UX", Module-Bay/Director-Uplink language), which is a design judgement call — a mockup prevents an implement→reject→re-implement cycle (Protocol 8/26/28) and lets the owner approve the look before code. - It's cheaply scoped: one screen, ~5 controls + the DANGER ZONE. SU-1/SU-2/SU-4 do not depend on it and can proceed in parallel while the mockup is made. **Guardrails for the mockup (Protocol 25 sanctioned-exception bar):** same tab/location, no added taps, stored semantics unchanged (same `id`+`onchange`), real control labels ride along as sub-labels, and a plain fallback preserved. --- ## 6. Protocol-8 plan-audit — every entry path & edge **7th keycap / nav:** - **Click `[6]`** → `selectSubsystem('settings')` → `switchTab('settings')` → the 4 settings panels get `.tab-visible`, all else hidden; `_syncBezelNav('settings')` lights the cap, `aria-selected=true`, writes telemetry + `robco_bezel_subsystem`, sets `body.dataset.subsystem`; SWEEP plays; first visit triggers the hatch. - **Hotkey `6`** (no modifier, not in INPUT/TEXTAREA/SELECT) → same. `[1]-[5]`/`[0]` unchanged. - **ARIA** — `#navkey-settings` is `role=tab` in the tablist; `_syncBezelNav` (iterates `NAV_KEYS`) toggles `aria-selected`. DIR stays outside the tablist (`aria-required-children` unchanged). - **`#go=settings`** → allow-list matches, `switchTab('settings')`. Absent the route, silently ignored (no breakage). - **Restore on reload** — `initTabs()` restores `robco_active_tab` ∈ {stat,inv,data,campg,**settings,chassis**}; `initBezelSubsystem()` now only re-highlights `uplink`. No double-restore. - **Centering** — 7 caps + DIR shrink together under `justify-content:center` + nowrap; verify no h-overflow at 360/412 (render-check). **Existing 6 subsystems unbroken:** operator/operations/databank still map via `_NAV_TAB_FOR`→stat/inv/data; `_DATABANK_TABS` (data+campg) merge unchanged. `uplink` branch unchanged. `chassis` changes from scroll-to-bay → `switchTab('chassis')` (verify DATABANK no longer shows the bay/Overseer-Log, CHASSIS shows SYSTEM STATUS). **Moving 5 panel groups (Protocol 22 — relocate markup, keep ids/handlers):** - Module Bay: `#securityConfigPanel`/`#moduleBay`/hatch/schematic keep ids/handlers; only gains `data-tab="settings"`. `initModuleBay`/`renderModuleBay`/`renderBaySchematic` unaffected. `_wirePanelPersistence()` STILL excludes `securityConfigPanel` from desktop default-open (L1683) + STILL wires its toggle→hatch (L1707) — verify the regexes/branches still match after the attribute add (Suite 154.16/154.22). - Saves: new `#savesPanel` keeps `#savesListBody` etc.; `renderSavesList()` still finds `#savesListBody` (Suite 46/63 pass on id) — update those tests' human strings. - Account: `#accountPanel`/`#accountBody` ids kept; SU-4 rewrites the body; `getAccountState`/`signInWithGoogle`/`signOutAccount` handlers unchanged. - Campaign configs: new `#campaignConfigPanel` keeps every `id`+`onchange`; record sub-panels stay in DATABANK (recommended (a) §3.1) with `data-sub-id` persistence untouched. - Overseer's Log split: `#overseerLogDisplay`/`renderOverseerLog` → SYSTEM STATUS; `#sessionStatsList`/`renderSessionStats`/`resetSessionStats` → CAMPAIGN LOG. Both render fns unchanged; only parent panels differ. **Firmware-log + error-log relocation:** `#btnViewChangelog`/`_svcViewChangelog` moves from SVC tray → SYSTEM STATUS (same handler). New ERROR LOG button calls the existing `showErrorLog()`. `[LOGS]` command still works (unchanged). **Dynamic account words:** §4 table; trigger via `loadUI`/`cloud.js`(×3)/`refreshOverseerCarrier`. Edges: signed-in-offline distinct line; `googleSignIn` OFF → no button. **Collapsible persistence:** all new panels are `details.panel` → persist via `robco_panel_state` id-keyed automatically. Module Bay internal `data-sub-id` sub-panels untouched. `#savesPanel`/`#campaignConfigPanel`/`#systemStatusPanel`/`#campaignLogPanel` enroll automatically. **Edge:** decide default-open state per panel on mobile vs desktop (mockup: Account+SLOT01 open, rest collapsed) — the shipped default is closed-on-mobile / open-on-desktop-except-`securityConfigPanel`. If any new panel should be closed on desktop too, add it to the L1683 exclusion list. **Per-tab scroll memory:** `settings`/`chassis` join `NAV_KEYS`; `_scrollElFor` returns `#uiPanel` (desktop) / page (mobile) for both — offset saved/restored per subsystem via `switchTab`'s existing `_saveOutgoingScroll`/`_restoreScrollFor`. No special case. **PWA-intact (Protocol UI-7):** pure markup/CSS/JS-routing inside the existing shell; no SW scope/`skipWaiting` change; installable/offline/standalone unaffected; cache bump is the only `sw.js` edit. **Migration / backward-compat:** existing `robco_active_tab` ∈ {stat,inv,data,campg} still valid — `settings`/`chassis` additive. Existing `robco_bezel_subsystem='chassis'` still lights the chassis cap; content differs (SYSTEM STATUS now) — intended. `#go=` links unchanged. `robco_bay_opened` reused as-is (hatch once-only). `robco_panel_state` keyed by panel id — new panels get their own keys; old `overseerLogPanel` key becomes inert (harmless). No data migration. **Mobile-compact (0i) + desktop:** panels stack in `.col-left` like every tab; no new grid; Overseer column stays right; Module Bay chip/tube grids already centered (Suite 159). Confirm no h-overflow at 360/412. --- ## 7. Tests — add / update (both runners at parity, Protocol 13/15/2a) **Update existing:** - **Suite 158** (bezel nav): add `navkey-settings` exists + `role=tab` + `.setkey` + wired to `selectSubsystem('settings')` + hotkey `[6]`; update the DIRECTORY row set to include SETTINGS; update the `#go=` key-set assertion if `settings` route added; update the `chassis` desc string; keep centering/tap-target/cache-regex asserts. - **Suite 119** (currently asserts `<details class="panel" data-tab="data" id="overseerLogPanel">` at L13389–13390): **rework** — device telemetry now `#overseerLogDisplay` inside `#systemStatusPanel data-tab="chassis"`. - **Suite 125** (asserts `#sessionStatsList` inside `#overseerLogPanel`, L14085–14101): **rework** — `#sessionStatsList` now inside `#campaignLogPanel data-tab="data"`; RESET still present. - **Suite 154.16 / 154.22:** re-confirm `securityConfigPanel` still hatch-wired + desktop-default-open-excluded after gaining `data-tab="settings"`. - **Suites 46 / 63:** `#savesListBody` id assert still passes (moved, id kept) — update human strings to "SAVE ARCHIVE / SETTINGS". - **Suite 62** (changelog viewer): if it asserts the firmware button lives in the SVC tray, update to SYSTEM STATUS. `_svcViewChangelog`/`_showChangelogModal` bodies unchanged. - Any suite asserting Account/Campaign-configs under DATABANK or `TAB_NAMES` contents — re-sync. **Add — new Suite 176 "SETTINGS tab + CHASSIS reorg":** - 7th keycap exists + `role=tab` + wired + `[6]` hotkey + `.setkey` stripe. - `TAB_NAMES` includes `'settings'` + `'chassis'`; `TAB_TO_SUBSYSTEM`/`_NAV_TAB_FOR`/`NAV_KEYS` include both. - The 4 settings panels carry `data-tab="settings"`; `#savesPanel`/`#campaignConfigPanel` exist with their inner ids; Account/Module-Bay/Saves/Campaign-configs ABSENT from `data-tab="data"`/`campg`. - `#systemStatusPanel data-tab="chassis"` exists with `#overseerLogDisplay` + firmware-log button + error-log button + version/carrier/flag readout; `#campaignLogPanel data-tab="data"` exists with `#sessionStatsList` + RESET. - Firmware button ABSENT from SVC tray. - `renderAccount()` emits the dynamic REG PORT strings per state (structural + a small VM/behavioral check of signed-out/in/offline branches). - First-visit hatch: `selectSubsystem('settings')` opens `securityConfigPanel` gated on `robco_bay_opened`; boot path (`initTabs`) does NOT. - Nav/render layer writes no campaign state (extend the 158.8-style guard to the new fns; `renderSystemStatus` reads only version/flags/carrier). **Protocol 40:** update `tests/test.html` only if a new state field / boot-order change lands (none expected unless SU-3 adds a toggle). **Protocol 2a:** sync the new total everywhere (CLAUDE.md, RULES.md, README, ARCHITECTURE, CHANGELOG `[Unreleased]` header, both runners' per-suite comments, `test.html` `Suites:` marker). --- ## 8. Protocol touchpoints - **P1** — cache bump per served-file push (r69→…). - **P2/2a** — CHANGELOG `[Unreleased]`, README, ARCHITECTURE, ARCHITECTURE Quick-Reference suite/test counts. - **P4 / UI-6** — only if SU-3 adds a device pref; hatch reuses `robco_bay_opened` (no new pref). - **P10 / 17** — render-check at 360/412/desktop; 7-key row no-overflow; ≥28px caps, ≥16px inputs. - **P22** — relocate markup, keep ids/handlers; reuse `switchTab`/`refreshOverseerCarrier`/`initModuleBay`/`renderOverseerLog`/`renderSessionStats`/`showErrorLog`/`_svcViewChangelog`; do NOT fork the mockup shell. - **P25 + DO-N extension** — owner-approved reframe; guardrails hold (same machine location, no added taps, DIR flat fallback lists SETTINGS, real labels ride as sub-labels, stored semantics unchanged). Splitting Overseer's Log is owner-locked (decision #3), not an unprompted redesign. - **UI-1** — every new/relocated panel keeps `<summary><h2>&gt; HEADING</h2></summary>`. - **DO-N invariants (Suite 158)** — hotkeys, `role=tab`/`aria-selected`, `#go=`, `robco_bezel_subsystem`, DIRECTORY fallback all preserved/extended. - **P38** — account/registry/system-status fiction stays game-agnostic; amber accent uses existing `--bezel-wire`/tokens. - **P13/15/42** — regression tests same commit, both runners at parity, counts synced. - **P19** — SU-1+SU-2 recommended as one push (avoid shipping an empty CHASSIS); SU-3/SU-4 separate. --- ## 9. Open questions (owner) — recommendations given; NOT blocking the plan 1. **SU-1+SU-2 as one push** (recommended, so CHASSIS is never shipped empty) vs SU-1 shipping a "pending" placeholder. → Recommend combined. 2. **Campaign RECORD sub-panels** (STATUS/CROSSROADS/INCIDENT): keep in DATABANK (recommended (a): rename `#campgPanel`'s heading to CAMPAIGN RECORD, keep `data-tab="campg"`) vs fold into the new DATABANK CAMPAIGN LOG panel. 3. **Campaign-configs mockup** — recommend YES before SU-3 (§5). 4. **`#go=settings` deep-link** — recommend add (small Suite-158 update). 5. **Per-panel default-open state** on mobile/desktop for the 4 new SETTINGS panels + the 2 new CHASSIS/DATABANK panels (mockup: Account+SLOT01 open, rest collapsed).
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).