RELEASE
planning/2.8.0/plans/SETTINGS_TAB_PLAN.md
sha256 8de1581372224a73 · 27305 bytes ·
original held in the private archive
# SETTINGS Tab ([6]) — Opus Diagnose & Plan (Protocol 8, Stage 1)
**Branch:** `dev` · **HEAD:** `192500b` · **APP_VERSION:** `2.7.0` · **Cache:** `robco-terminal-v2.7.0-r56` · **Tests:** `2126/2126`
**Design source:** `planning/mockups/settings-tab.html` + `settings-tab-notes.md` + `settings-tab-*.png` (owner-approved 2026-07-04).
**Scope:** PLAN ONLY. No code edits. Implementation is a later Sonnet/Opus pass.
---
## 0. Confirmed current state (Protocol 27 — reproduce before fixing)
Read directly from the clean `origin/dev` tree, not assumed:
| Thing | Where it lives NOW | Mechanism |
| --- | --- | --- |
| Bezel nav | `index.html` `.nav-cluster` (lines ~310–378): 5 `role=tab` keycaps `navkey-operator/operations/databank/uplink/chassis` + `navkey-directory` (DIR, outside the tablist) | 6 buttons; DIR `[0]` opens a modal |
| Tab machinery | `ui-core.js`: `TAB_NAMES=['stat','inv','data','campg']`, `_DATABANK_TABS=['data','campg']`, `TAB_TO_SUBSYSTEM`, `switchTab()`, `initTabs()` | `.panel[data-tab]` get `.tab-visible` toggled |
| Bezel routing | `ui-core.js`: `NAV_KEYS=['operator','operations','databank','uplink','chassis']`, `_NAV_TAB_FOR={operator:'stat',operations:'inv',databank:'data'}`, `selectSubsystem()`, `_syncBezelNav()`, `_bezelSubsystemLabel()`, `openBezelDirectory()` | keycap → tab or scroll |
| Hotkeys | `ui-core.js` ~1809: `hotkeyMap {1..5}` → `selectSubsystem()`; `'0'` → `openBezelDirectory()` | keydown, guarded against inputs |
| `#go=` deep-links | `ui-core.js`: `SHORTCUT_ROUTES {comm,inv,stat,data,new}`, `routeLaunchShortcut()`, allow-list `^go=([a-z]+)$` | PWA shortcuts |
| Prefs | `state.js` `META_MANIFEST`: `robco_active_tab` (default n/a), `robco_bezel_subsystem` (default `'operator'`), `robco_scroll_positions`, `robco_panel_state`, `robco_bay_opened` | MetaStore |
| **Module Bay** | `index.html` `#securityConfigPanel` (line ~1604) — **NO `data-tab`** → always-visible in `.col-left`. Reached by `selectSubsystem('chassis')` which just **scrolls to** `#moduleBay` | `initModuleBay()`/hatch fires on this panel's OWN toggle-open |
| **Saves / SAVE MENU** | **Nested INSIDE `#securityConfigPanel`** (lines ~2293–2458): `#savesListBody`, `#btnSaveToCloud`, `#btnSyncSlotsToCloud`, EXPORT/IMPORT/RESTORE/FULL BACKUP, SAVE/LOAD A-C slots, `#slotStatus`, `#fileInput` | `renderSavesList()` |
| **Account** | `index.html` `#accountPanel` (line ~1591) — **`data-tab="data"`** → lives under **DATABANK**, NOT CHASSIS | `renderAccount()` fills `#accountBody` (plain, not the amber board yet) |
| Overseer's Log | `#overseerLogPanel` `data-tab="data"` (line ~1340) → **DATABANK** (device telemetry + campaign stats) | `renderOverseerLog()` + `renderSessionStats()` |
**Critical consequence:** CHASSIS `[5]` today has **no content of its own** — it is defined ad-hoc as "scroll to the always-visible Module Bay." Once Module Bay + saves move to SETTINGS, **CHASSIS is empty and must be repopulated** (see §3). Account is a DATABANK panel, so removing it from "its current tab" means pulling it out of DATABANK, not CHASSIS.
**Also note:** the real app already has the DO-N/DO-O device chrome (bezel, glass, Overseer column). The mockup is a *standalone* machine demonstrating the design. We are **not** rebuilding the mockup's bespoke `.board`/`.bay-grid`/`.overseer` shell — we reuse the shipped chrome and the existing `<details class="panel">` + `.tab-visible` system. The genuinely-new visual is the **amber REG PORT / ID-card styling** applied to the Account panel body.
---
## 1. Approved design + locked owner decisions
1. **7th keycap `[6] SETTINGS`** (sub-label `CONFIG·ACCT`), between `CHASSIS[5]` and `DIR[0]`; all 7 caps in one centered row at 360–412px (widths tightened); centering rule holds.
2. **SETTINGS tab contents, top-to-bottom:** ACCOUNT — Operator Registry (REG PORT) → Module Bay (SLOT 01–05 + SVC TRAY, reused verbatim) → SAVE ARCHIVE (saves). All collapsible (closed = one-line status).
3. **Remove** Module Bay + Account + saves from their current tabs.
4. **CHASSIS keeps NON-settings SYSTEM content** — *what stays is an open decision* (§3).
5. **Hatch ceremony replays on first `[6]` visit.**
6. ⭐ **Account board status words are DYNAMIC** — wired to real auth + cloud-sync state, live-updating (§5).
**Owner override of mockup:** the mockup shows the SAVE ARCHIVE staying on CHASSIS (its SVC-tray note and CHASSIS placeholder say so). The **owner decision supersedes**: saves MOVE into SETTINGS `[6]`. Open-question #1 in `settings-tab-notes.md` is resolved in favor of moving saves.
---
## 2. Architecture decision — SETTINGS is a first-class tab-gated subsystem
Make `settings` a real tab value (`data-tab="settings"`), exactly like `stat`/`inv`/`data`. 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). It is strictly better than the CHASSIS-style "always-visible + scroll-to" model, which requires an always-visible target and cannot *hide* settings when another subsystem is active.
The three SETTINGS panels become three `<details class="panel" data-tab="settings">` in `.col-left`, in source order **Account → Module Bay → Saves**. Each keeps the standard `<summary><h2>> HEADING</h2>` (Protocol UI-1); the mockup's "collapsed = one-line status" is honored by adding a status `<span>` into each summary that the render fns update.
---
## 3. ⭐ CHASSIS repopulation (the primary open decision)
Because CHASSIS is losing 100% of its current content, it must be given a home. **Recommended plan (CHASSIS-as-tab):**
- Convert CHASSIS from the scroll-to model into a **tab-gated subsystem** (`data-tab="chassis"`), symmetric with SETTINGS.
- Its SYSTEM content = the **OVERSEER'S LOG** panel (device UNIT TELEMETRY + CAMPAIGN LOG — WU-F7/F10), **moved from DATABANK** (`data-tab="data"` → `data-tab="chassis"`). This matches the mockup's CHASSIS placeholder ("OVERSEER'S LOG · firmware/system readouts") and is the most natural non-config "SYSTEM" surface already in the app.
- `selectSubsystem('chassis')` drops its Module-Bay scroll logic and routes through `switchTab('chassis')`.
**Trade-off / why it needs owner sign-off:** this relocates Overseer's Log out of DATABANK (a DATABANK change + Protocol 25 muscle-memory consideration) and leaves CHASSIS holding a single panel. Alternatives:
- **(b)** Also author a small new "SYSTEM STATUS / FIRMWARE" readout panel on CHASSIS (APP_VERSION, cache rev, uptime, a FIRMWARE REVISION LOG button that opens the existing changelog modal) so CHASSIS isn't a one-panel tab. (More scope; nothing to source — it's derived data.)
- **(c)** Leave Overseer's Log in DATABANK and give CHASSIS only the new SYSTEM STATUS panel from (b).
I recommend **(a)** for minimal scope, with **(b)** as an easy additive upgrade. **This is Open Question #1 — confirm before implementation.**
---
## 4. File-by-file change plan (exact targets + rationale)
### 4.1 `index.html`
- **Bezel: add the 7th keycap.** Insert `#navkey-settings` `role="tab"` after `#navkey-chassis`, before the DIR button (DIR must stay outside `role=tablist`). Markup mirrors the others: `[6]` `nk-num`, `SETTINGS` `nk-label`, `CONFIG·ACCT` `nk-sub`, `onclick="selectSubsystem('settings')"`, `aria-label` describing it + hotkey 6, plus a `.setkey` class for the amber service-stripe (mockup `.navkey.setkey::after`). Update `#navkey-chassis` aria-label ("Module Bay, settings and saves" → "system telemetry, hotkey 5").
- **Account panel** (`#accountPanel`): change `data-tab="data"` → `data-tab="settings"`; move its markup to be the FIRST of the three settings panels (source order). Restyle body into the amber REG PORT / ID-card board (§5) — CSS class only, `<summary><h2>> ACCOUNT</h2></summary>` preserved; add a summary status `<span id="acctSummaryStatus" class="panel-substatus">`. Drop the now-wrong "Manage saves in Security & Configuration below" note.
- **Module Bay panel** (`#securityConfigPanel`): add `data-tab="settings"`. Keep everything inside (`#moduleBay`, hatch, schematic) unchanged. **Extract the saves block** (the `input-group` at ~2293 through the SAVE SLOTS grid + `#fileInput`, ~2293–2458) OUT of this panel.
- **New Saves panel:** wrap the extracted saves markup in a new `<details class="panel" data-tab="settings" id="savesPanel"><summary><h2>> SAVE ARCHIVE</h2></summary>…`, placed immediately after `#securityConfigPanel`. **Every id and handler preserved verbatim** (`#savesListBody`, `#btnSaveToCloud`, `#btnSyncSlotsToCloud`, `exportSaveFile`, `triggerFileInput`, `restoreRollingBackup`, `exportFullBundle`, `saveToSlot`/`loadFromSlot`, `#slotStatus`, `#fileInput`, `showSaveHelpModal`) — Protocol 22 relocate-not-rewrite.
- **Overseer's Log panel** (`#overseerLogPanel`): `data-tab="data"` → `data-tab="chassis"` (per §3a). Move it to sit with any CHASSIS content, out of the DATABANK group.
- **Contiguity:** ensure the three `data-tab="settings"` panels are adjacent and ordered Account → Module Bay → Saves in `.col-left` (source order = stacking order; `.tab-visible` hides the rest).
### 4.2 `js/ui-core.js`
- `TAB_NAMES` → add `'settings'` and `'chassis'` (chassis becomes a real tab per §3a).
- `TAB_TO_SUBSYSTEM` → add `settings:'settings'`, `chassis:'chassis'`.
- `_NAV_TAB_FOR` → add `settings:'settings'`, `chassis:'chassis'` (so `selectSubsystem` routes both through `switchTab`).
- `NAV_KEYS` → add `'settings'` (already has `chassis`). This auto-enrolls both in `_syncBezelNav`, scroll-memory (`_scrollElFor`/`_saveScrollFor`/`_restoreScrollFor` — both live in `#uiPanel`/page, so no special-case needed), and telemetry.
- `selectSubsystem()` → the `chassis` branch (scroll-to `#moduleBay`) is **removed**; `chassis` now falls into the `_NAV_TAB_FOR` → `switchTab` path. Add the **first-visit hatch trigger** for `settings` (below). `uplink` branch unchanged.
- **Hotkeys** (~1809): add `6:'settings'` to `hotkeyMap`. `[0]` DIR unchanged. (`[1]-[5]` unchanged.)
- `_bezelSubsystemLabel()` → add `case 'settings'` (e.g. `'▸ SUBSYSTEM: SETTINGS · MODULE BAY + REGISTRY'`); update `case 'chassis'` from "MODULE BAY" to a SYSTEM/telemetry label (e.g. `'▸ SUBSYSTEM: CHASSIS · UNIT TELEMETRY'`).
- `openBezelDirectory()` → add a 7th row `['settings','SETTINGS','config & account','CONFIG·ACCT · [6]']`; fix the `chassis` row desc from "settings & saves" to "system telemetry".
- `initBezelSubsystem()` → chassis is now a tab (restored by `initTabs` via `robco_active_tab`), so the cosmetic restore list narrows to `'uplink'` only. Confirm `robco_bezel_subsystem` values `settings`/`chassis` still round-trip (they're written by `_syncBezelNav` and don't need the cosmetic restore because their tab restores itself).
- **First-visit hatch** (owner decision #5): add a small helper invoked from `selectSubsystem('settings')` (a **user-triggered** path only — boot uses `initTabs`→`switchTab` directly, never `selectSubsystem`, so this can't fire at boot): if `MetaStore.get('robco_bay_opened')` is falsy, `setAttribute('open','')` on `#securityConfigPanel`, whose existing `toggle` listener fires `initModuleBay()` → shows the hatch **once ever** (the `robco_bay_opened` gate already guarantees once-only; `releaseBayHatch()` sets it). Reuses shipped machinery, no new hatch code. (Alternative: a dedicated `robco_settings_visited` pref if the owner wants the hatch to replay independently of `robco_bay_opened` — see Open Question #3.)
- `SHORTCUT_ROUTES` (recommended, additive): add `settings: () => switchTab('settings')` so `#go=settings` PWA deep-links work. Requires updating Suite 158.9 (which currently asserts exactly the 5 existing keys). Marked recommended, not mandatory.
### 4.3 `js/ui-account.js` — dynamic REG PORT board (§5)
Rewrite `renderAccount()` to paint the amber ID-card board and drive the summary status span from real state (details in §5).
### 4.4 `css/terminal.css`
- **Tighten keycap widths for 7 caps.** Current `.navkey { flex: 1 1 60px; max-width: 110px }` under `.nav-cluster { flex-wrap: nowrap }`. With `min-width:0` the keys already shrink, but verify/tighten (e.g. `flex-basis` ~48px) so seven caps + DIR hold ONE row with **no horizontal overflow at 360/412** (Protocol 10/17). The mobile bezel is `position:fixed` docked-bottom (Suite 160) — re-verify at 360 that 7 keys don't force wrap or overflow.
- `.navkey.setkey::after` — the amber service-stripe on the SETTINGS cap (from mockup lines ~808–815).
- `.panel[data-tab="settings"].tab-visible` — inherits the existing `.panel[data-tab].tab-visible` rule automatically; confirm no `[data-tab]` value is hardcoded anywhere that would exclude `settings`/`chassis` (the selector is attribute-generic).
- **Amber REG PORT / ID-card styling** for the Account panel body (`.id-card`, `.id-photo`, `.id-lines`, `.id-status/.id-name/.id-mail/.id-note`, amber accents) — ported from mockup §"ACCOUNT / operator registry". Scoped to the account panel so it doesn't bleed.
- `.panel-substatus` — the one-line summary status span style (small, dim, `white-space:nowrap; overflow:hidden; text-overflow:ellipsis`).
### 4.5 `js/state.js`
- If a `robco_settings_visited` pref is chosen (Open Question #3), register it in `META_MANIFEST` (Protocol 4/UI-6) with a sensible default. If we reuse `robco_bay_opened`, **no new pref** — preferred for minimal footprint.
- `robco_bezel_subsystem` already defaults `'operator'`; its accepted value set now includes `settings` — no schema change, values are free-form strings.
### 4.6 `sw.js`
- **Bump `CACHE_NAME`** `robco-terminal-v2.7.0-r56` → `r57` (Protocol 1 — `index.html`, `css/`, `js/`, `sw.js` all change).
---
## 5. ⭐ Dynamic Account (REG PORT) status wiring
The board's diegetic words must reflect **real auth + cloud-sync state** and live-update — no hardcoded strings. Reuse the shipped signals; do NOT invent a parallel connection check (Protocol 22).
**Inputs (already available):**
- `window.getAccountState()` → `{uid, isAnonymous, email, displayName}` (auth truth).
- `window.isFeatureEnabled('googleSignIn')` / `'cloudSync')` (kill-switch, Protocol 32/33).
- `navigator.onLine` for the cloud-reachability line.
**State → words (rendered by `renderAccount()`):**
| Condition | LED (`acctLed`) | Summary status (`acctSummaryStatus`) | ID-card status / board line |
| --- | --- | --- | --- |
| Signed out (anon / no uid) | off | `UPLINK OFFLINE — ARCHIVES STORED LOCALLY` | `NO OPERATOR ON RECORD` · `> REG PORT VACANT — LOCAL ARCHIVES ACTIVE · CLOUD ARCHIVE SYNC OFFLINE` + the ESTABLISH GOOGLE UPLINK button |
| Signed out + `googleSignIn` flag OFF | off | same offline line | `UPLINK TEMPORARILY UNAVAILABLE — LOCAL ARCHIVES ACTIVE` (no button) |
| Signed in, online | on | `UPLINK ACTIVE · <name/email>` | `UPLINK ACTIVE · OPERATOR VERIFIED` + `displayName`/`email` + `> CLOUD ARCHIVES + CIPHER-KEY SYNC AVAILABLE` + SEVER UPLINK button |
| Signed in, offline (`!navigator.onLine`) | on (dimmed) | `UPLINK ACTIVE — CLOUD UNREACHABLE` | `> OPERATOR VERIFIED · CLOUD ARCHIVE SYNC OFFLINE (NO CARRIER)` |
All strings escaped (`escapeHtml` on name/email — already done). Game-agnostic (no FNV/FO3 literals; identity fiction is generic operator/registry wording).
**Live-update trigger points (so words flip without reload):**
- Boot: `loadUI()` already calls `renderAccount()`.
- Sign-in / sign-out / collision recovery: `cloud.js` already calls `window.renderAccount()` at 3 sites.
- **Connectivity change:** add `renderAccount()` into the **existing connection choke point** `refreshOverseerCarrier()` (`ui-core.js`) — this is already invoked on `online`/`offline` events, on key edits via `saveApiKeySilent()`, and at boot, and it's the same signal `_isUplinkConnected()`/the UPLINK lamp/bezel VITALS use. One-line addition keeps the REG PORT words, the casing UPLINK lamp, and the bezel CARRIER suffix all synchronized (they can't drift — single choke point).
---
## 6. Protocol-8 plan-audit — enumerate every entry path & edge
For each, the intended behavior after the change:
**Adding the 7th keycap / nav:**
- **Click `[6]`** → `selectSubsystem('settings')` → `switchTab('settings')` → the 3 settings panels get `.tab-visible`, all others hidden; `_syncBezelNav('settings')` lights the cap, sets `aria-selected="true"`, writes telemetry + `robco_bezel_subsystem`, sets `body.dataset.subsystem`. SWEEP plays. First visit also triggers the hatch (§4.2).
- **Hotkey `6`** (no modifier, not in input) → same as click. `[1]-[5]` unchanged; `[0]` DIR unchanged.
- **ARIA:** `#navkey-settings` is `role="tab"` inside `role="tablist"`; `aria-selected` toggled by `_syncBezelNav` (already iterates `NAV_KEYS`). DIR stays outside the tablist (a11y `aria-required-children` — unchanged).
- **`#go=settings`** (if added) → `routeLaunchShortcut()` matches allow-list, calls `switchTab('settings')`. Without it, `#go=settings` is silently ignored (allow-list) — no breakage.
- **`robco_bezel_subsystem` restore:** on reload, `initTabs()` restores the last tab (incl. `settings`/`chassis`) from `robco_active_tab`; `initBezelSubsystem()` handles only `uplink` cosmetic restore. No double-restore conflict.
- **Centering at 360/412:** nav-cluster `justify-content:center` + nowrap; keys shrink together; verify no overflow (render-check).
**Routing the new subsystem WITHOUT breaking the existing 6:**
- `operator/operations/databank` still map via `_NAV_TAB_FOR` to `stat/inv/data`; `_DATABANK_TABS` merge (data+campg) unchanged. `uplink` branch unchanged (scroll/focus composer). `chassis` changes from scroll-to-bay to `switchTab('chassis')` — verify DATABANK no longer shows Overseer's Log and CHASSIS does.
**Moving 3 panel groups (Protocol 22 — relocate markup, keep handlers/ids):**
- Module Bay: `#securityConfigPanel` + `#moduleBay` + hatch + schematic keep ids/handlers; only gains `data-tab="settings"`. `initModuleBay()`/`renderModuleBay()`/`renderBaySchematic()` unaffected. `_wirePanelPersistence()` still excludes `securityConfigPanel` from desktop auto-open + still wires its toggle→`initModuleBay` (Suite 154.16/154.22 hold).
- Saves: new `#savesPanel` keeps `#savesListBody` etc.; `renderSavesList()` (`ui-account.js`) still finds `#savesListBody` (Suite 46.16/63.7 pass on id). Update those tests' message strings (say "SETTINGS/Save Archive" not "Security & Config") for accuracy.
- Account: `#accountPanel`/`#accountBody` ids kept; `renderAccount()` rewritten for the board; `getAccountState`/`signInWithGoogle`/`signOutAccount` handlers unchanged.
**What remains on CHASSIS:** OVERSEER'S LOG (`#overseerLogPanel`, `renderOverseerLog`+`renderSessionStats`+`resetSessionStats` all keep ids/handlers; only `data-tab` flips to `chassis`). See §3 for the confirm-needed decision.
**Dynamic-account-words wiring:** §5 table; trigger via `loadUI`/`cloud.js`/`refreshOverseerCarrier`. Edge: signed-in-but-offline → distinct line. Edge: `googleSignIn` flag off → no sign-in button, offline copy.
**Collapsible-section persistence (`data-sub-id` / `robco_panel_state`):** the 3 settings panels are `details.panel` (persist via `robco_panel_state`, id-keyed — unchanged). Module Bay's internal `data-sub-id` sub-panels are untouched. New `#savesPanel` id enrolls in panel persistence automatically. Edge: `#savesPanel` should default **closed** on mobile, and default-open on desktop is fine (it has no hatch) — but if the owner wants it closed on desktop too, add to the exclusion list next to `securityConfigPanel`.
**Mobile-compact (0i) + desktop layout:** panels stack in `.col-left` exactly like every tab; no new grid. Desktop keeps the Overseer column on the right (already always-visible). Mockup's default "Account+SLOT01 open, rest collapsed" on mobile maps to per-panel `robco_panel_state` defaults — the shipped default is closed-on-mobile, which is acceptable; optionally seed Account open. Confirm no horizontal overflow at 360/412 (Module Bay chip/tube grids already centered — Suite 159).
**Per-tab scroll memory interplay:** `settings`/`chassis` join `NAV_KEYS`; `_scrollElFor` returns `#uiPanel` (desktop) / page (mobile) for both, same as other tabs — scroll offset saved/restored per subsystem, and `switchTab` already calls `_saveOutgoingScroll`/`_restoreScrollFor`. No special-case.
**PWA-intact (Protocol UI-7):** pure markup/CSS/JS-routing change inside the existing shell; no service-worker 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` are additive. Existing `robco_bezel_subsystem='chassis'` still lights the chassis cap — content differs (Overseer's Log now), which is the intended change, not a break. Existing `#go=` links unchanged. `robco_bay_opened` reused as-is (hatch still once-only). No data migration required.
---
## 7. Acceptance criteria (Protocol 26 — verify on the real artifact)
Rendered at **360px, 412px, and ≥1000px desktop**:
1. Seven keycaps in ONE centered row, no horizontal overflow (`documentElement.scrollWidth === innerWidth`) at 360 & 412; desktop unchanged.
2. All 7 keys route: `[1]-[5]` unchanged; `[6]` shows SETTINGS (Account+Bay+Saves) and hides all else; DIR modal lists 6 subsystems incl. SETTINGS.
3. Hotkeys `1-6` + `0` work; not while typing in an input.
4. `#go=stat/inv/data/comm/new` still route; `#go=settings` routes if added.
5. SETTINGS shows exactly Account → Module Bay → Saves, each collapsible with a one-line closed status.
6. Module Bay + Account + Saves are ABSENT from DATABANK and every other tab; CHASSIS shows Overseer's Log (per §3).
7. Hatch ceremony plays on first-ever `[6]` visit, never at boot, never again after.
8. Account words flip live: sign in → "OPERATOR VERIFIED / SYNC AVAILABLE"; sign out → "REG PORT VACANT / SYNC OFFLINE"; go offline while signed in → "CLOUD UNREACHABLE". No reload needed. LED + summary + board line all agree.
9. Saves reachable and functional (export/import/restore/full-backup/slots/cloud) from the new panel.
10. Reload restores the last subsystem incl. SETTINGS; scroll memory per subsystem holds.
11. Full gate green (2126 + new tests), both runners at parity; `render-check.mjs` passes (Protocol 10).
---
## 8. Tests to add / update (both runners at parity — Protocol 13/15/2a)
**Update existing:**
- **Suite 158** (bezel nav): add `158.1g` `navkey-settings` exists; add settings keycap wired to `selectSubsystem('settings')`; add hotkey `[6]`; keep centering/tap-target/cache-bump asserts (cache regex already `v2.7.0-rN`). If `#go=settings` added, update **158.9** to the new key set.
- **Suite 119** (`/…data-tab="data" id="overseerLogPanel"…/`): change to `data-tab="chassis"`.
- **Suite 154.16/154.22:** re-confirm `securityConfigPanel` still hatch-wired + desktop-auto-open-excluded after gaining `data-tab="settings"` (regexes should still match; verify).
- **Suites 46.16 / 63.7:** `#savesListBody` id assert still passes (moved, id kept); update the human message string to reference SETTINGS/Save Archive.
- Any suite asserting Account/Overseer's-Log under DATABANK, or `TAB_NAMES` contents — re-sync.
**Add (new suite, e.g. Suite 169 — "SETTINGS tab"):**
- 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 3 panels carry `data-tab="settings"`; `#savesPanel` exists with `#savesListBody` inside; Account/Module-Bay/Saves absent from `data-tab="data"`.
- `#overseerLogPanel` is `data-tab="chassis"` (CHASSIS content).
- `renderAccount()` emits the dynamic REG PORT strings for each state (structural: strings present + state-branched; ideally a small VM/behavioral check of the signed-out/in/offline branches).
- First-visit hatch: `selectSubsystem('settings')` opens `securityConfigPanel` gated on `robco_bay_opened`; boot path (`initTabs`) does NOT.
- Nav layer still writes no campaign state (extend the 158.8-style guard to the new fns).
- If a new pref is added: register in `META_MANIFEST` + Suite 134 boundary + `test.html` `KNOWN_KEYS` (Protocol 40).
**Protocol 40:** update `tests/test.html` only if a new state field / boot-order change lands (a new device pref would touch `KNOWN_KEYS`; the `Suites: N` marker if a runtime suite changes — likely not).
**Protocol 2a:** sync the test count everywhere (CLAUDE.md, RULES.md, README, ARCHITECTURE, CHANGELOG `[Unreleased]` header, both runners' per-suite comments, `test.html`).
---
## 9. Protocol touchpoints
- **Protocol 1** — cache bump `r56→r57` (served files change).
- **Protocol 2/2a** — CHANGELOG `[Unreleased]`, README, ARCHITECTURE, test counts.
- **Protocol 4 / UI-6** — only if a new device pref (`robco_settings_visited`) is chosen; otherwise none.
- **Protocol 10 / 17** — render-check at 360/412/desktop; 7-key row no-overflow; ≥28px caps, ≥16px inputs.
- **Protocol 22** — relocate markup, keep ids/handlers; reuse `switchTab`/`refreshOverseerCarrier`/`initModuleBay`; do NOT fork the mockup shell.
- **Protocol 25 + DO-N extension** — owner-approved reframe; guardrails hold (same/adjacent location within the machine, no added taps, DIR flat fallback lists SETTINGS, real labels ride as sub-labels, stored semantics unchanged). Relocating Overseer's Log (§3) is the one item needing explicit sign-off.
- **Protocol UI-1** — new/relocated panels keep `<summary><h2>> HEADING</h2>`.
- **DO-N invariants (Suite 158)** — hotkeys, `role=tab`/`aria-selected`, `#go=`, `robco_bezel_subsystem`, DIRECTORY fallback all preserved/extended.
- **Protocol 38** — account/registry fiction stays game-agnostic (no game literals); amber accent uses existing tokens.
- **Protocol 13/15/42** — regression tests in the same commit, both runners at parity.
---
## 10. Open questions (owner)
1. **CHASSIS content (primary).** Recommend **(a)** move OVERSEER'S LOG to CHASSIS as its SYSTEM content. Confirm, or pick **(b)** add a new SYSTEM STATUS/FIRMWARE panel too, or **(c)** keep Overseer's Log in DATABANK and give CHASSIS only a new SYSTEM STATUS panel. (Blocks §3, §4.1, Suite 119.)
2. **`#go=settings` deep-link** — add it (recommended; small Suite 158.9 update) or leave settings reachable by keycap/hotkey only?
3. **Hatch trigger semantics** — reuse `robco_bay_opened` (hatch shows once ever, now surfaced by first `[6]` visit) — recommended; OR a dedicated `robco_settings_visited` pref so the SETTINGS hatch is independent of whether the bay was opened before this feature shipped?
4. **Saves-panel default state** — default closed on mobile (shipped behavior). Default open on desktop, or exclude like `securityConfigPanel`? And should Account seed open on first visit (mockup shows it open)?
5. **Keycap wording** — mockup ships `SETTINGS` / `CONFIG·ACCT`. Keep, or use `SERVICE`/`CONFIG`?
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).