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

CEREMONY_MOMENTS_SLATE.md



RELEASE

planning/2.8.0/slates/CEREMONY_MOMENTS_SLATE.md

sha256 c1f5c5b5c886023d · 26780 bytes · original held in the private archive

# CEREMONY MOMENTS SLATE — boot / first-launch / transition / ceremony beats > Ideation-only scoping pass for the "boot / ceremony moments" item in the 2.8.0 queue. > Companion in spirit to `FEEDBACK_ANIMATION_SLATE.md` (the 33 shipped per-event animations), > but focused on **transitions and ceremonies** — the moments the machine changes state — > rather than per-event feedback. Read against HEAD `6b08b16` (dev). > > NO source edits ship from this doc. Owner picks; each pick becomes its own unit. --- ## 1 · INVENTORY — what already exists (so this slate is additive, not duplicative) ### 1.1 Boot sequence (WU-F6 + WU-T3 + WU-B10) - `runBootSequence(onComplete)` — `js/ui-audio.js:1531`. Three POST flavors via `_pickBootFlavor()` (`ui-audio.js:1477`): - **`normal`** — terse 8-line warm boot. - **`cold`** — first-ever power-on (RETROS BIOS banner + counting memory test), once per device, gated by MetaStore `robco_booted_before` (`js/state.js:68`). - **`degraded`** — rare cold-tube flicker variant, `DEGRADED_BOOT_CHANCE = 0.01` rolled on EVERY boot (owner pref — never first-boot-gated). - Per-game identity boot line (WU-T3): `PIP-BOY 3000 MK IV — MOJAVE WASTELAND UPLINK` spliced in before the final line, from `GAME_DEFS[ctx].theme` (`ui-audio.js:1547-1555`). - Boot drone armed to the first user gesture (`_bootActive` window, WU-B10, `ui-audio.js:1329`); 120 ms line cadence; `boot-fade-out` exit. - Test override: `window.__robcoBootFlavor`; Test Console REBOOT replays the real sequence (`js/test-console.js:129`). ### 1.2 Existing ceremonies (view-once beats) - **Module Bay hatch** — the ONE shipped view-once ceremony. `releaseBayHatch()` (`js/ui-core.js:705`), MetaStore `robco_bay_opened`, fires only on a genuine first user-initiated SETTINGS visit (never at `window.onload` — `ui-core.js:2496`), honors reduced-motion, Test Console REPLAY HATCH resets it (`test-console.js:191`). - **Cold POST** (above) is the only other first-ever beat. - **Cartridge swap** today = `confirmAction('> SWAP PROGRAM CARTRIDGE' … 'SEAT CARTRIDGE')` → `onGameContextChange()` → **bare `window.location.reload()`** (`ui-core.js:3366-3416`). A jarring white flash — the build plan itself calls it "the biggest wasted-ceremony opportunity." - **New campaign** today = `wipeTerminal()` double-confirm → plain chat lines `> TERMINAL WIPED. INITIATING NEW CAMPAIGN...` / `> SELECT GAME CONTEXT:` (`ui-core.js:7841-7910`). No ceremony at all. ### 1.3 Ambient runtime + presence - `AmbientRuntime` states COLD_BOOT/READY/ACTIVE/IDLE/STANDBY/SHUTDOWN/OFF (`js/runtime.js`); A3 experiences: idle phosphor dim, standby breathing vignette, shutdown CRT collapse-to-a-dot + `#powerOnBtn` recovery (legal SHUTDOWN→OFF→COLD_BOOT walk). - **Wake sequence** (`exitStandby()`, `ui-core.js:1167`): wake tone → 650 ms → hum ramp + `> COURIER RETURNED. SYNCHRONIZING TELEMETRY...` + geiger resync, with the two-site shutdown-race guard. This is a real ceremony already — audio + chat, but **no visual verb**. - **DO-O Overseer** — scope states listening/thinking/speaking/disabled/offline, idle blips from `identity.persona.blipBank`. **`identity.overseer.greeting` is authored for all three games (`state.js:983/1186/1392`) but consumed NOWHERE** — grep hits only the three declarations. A finished, data-driven moment with no renderer. - **CHASSIS living core** — 24 behaviors incl. boot ignition, uptime-milestone pulse (once per crossed hour), recovery stabilize, hold-to-overcharge; all gate-stacked via `_coreShouldAnimate()`/`.core-still`. - **Overseer's Log telemetry** (WU-F7, `ui-core.js:160-246`): `bootCount`, `totalPowerOnMs`, `longestSessionMs`, `firstBoot` — MetaStore device stats, already tracked, mostly ceremonially unused. ### 1.4 Identity data already scaffolded (DO-K) Every game carries `identity.ceremony` (`coldStart`, `switchLabel`) and `identity.motionTexture` (`seat`/`sweep`/`wake`/`fault`/`breathe`) — `state.js:934-944` (NV), `1139-1149` (FO3), FO4 design-only. **Only `sweep` is consumed** (DO-N `_bezelSweep()`, `ui-core.js:4201`). `seat`/`wake`/`fault`/`breathe` are authored textures with no verb built. ### 1.5 Motion-verb grammar (Protocol UI-9) - **SWEEP** shipped — phosphor smear on subsystem change, plain `@keyframes` + toggled class + forced reflow, auto-neutralized by the global reduced-motion block. - **SEAT / WAKE / FAULT / BREATHE** — named in the protocol as "land with later units"; FAULT exists only as feedback-animation #32 (lamp flicker), not a formal verb; WAKE exists only as audio (`playWakeTone`). ### 1.6 Update / reboot flow Blocking `#updateModal` + `> REBOOT TERMINAL` → `SKIP_WAITING` → reload (`index.html:4133`, Suite 65). **Nothing marks the first boot AFTER an update** — the user taps REBOOT and lands in an ordinary warm boot with no "what changed" beat. ### 1.7 Account / sync moments Google link: popup → `renderAccount()` re-render flips to `OPERATOR VERIFIED` (`js/ui-account.js`). First cloud push: additive `addDoc` + `#31 HOLOTAPE COMMIT` (Wave 2, home-only). **No one-time "operator registered" ceremony** anywhere. ### 1.8 Already-planned unit this slate must NOT duplicate **DO-C — Cartridge-swap game-switch + cold-start ceremony** (`DESIGN_OVERHAUL_BUILD_PLAN.md` §DO-C, lines 384-430): wraps the `onGameContextChange()` reload as an eject/seat ceremony, and upgrades the once-per-device cold boot into a per-game salvage-and-power-on ceremony driven by `identity.ceremony.coldStart`. Mockup-gated, skippable, reduced-motion-instant, MetaStore-only. **Slate items below either feed DO-C or deliberately stay out of its lane; two entries are explicit DO-C deferrals so the owner sees the full moment landscape in one place.** ### 1.9 Adjacent shipped beats (not gaps) Level-up already has a flagship (#9 VAULT-BOY LEVEL CARD + XP sweep, Wave 1); location arrival has the LOCATION CONFIRMATION CARD (Suite 204); saves have #31 HOLOTAPE COMMIT; quests have #23/#24 stamps. The slate proposes *milestone variants*, never re-doing these. --- ## 2 · DESIGN RULES every slate item obeys (noted per idea as tags) | Tag | Constraint | | --- | --- | | `MOBILE` | Works and does not annoy at 360/412 (owner is almost exclusively on a phone). No moment adds taps, blocks input, or meaningfully delays boot. | | `RM-SAFE` | Plain `@keyframes` only — auto-neutralized by the existing global `prefers-reduced-motion` block to one correct static frame (Protocol UI-9). | | `NO-WRITE` | Zero campaign-state write. Transient DOM/module vars, or a MetaStore device flag at most (`robco_bay_opened` precedent). | | `AGNOSTIC` | Per-game flavor only via `identity`/`GAME_DEFS` (Protocol 38). Generic fallback for an unauthored game — never borrowed fiction. | | `PWA-OK` | Boot integrity / SW / offline untouched; ceremony wraps real work, never replaces or delays it (DO-C guardrail, adopted slate-wide). | | `EARNED` | Fires at a genuine transition; one-time moments are view-once (MetaStore flag); repeating moments are brief (<2 s) and never gate input. | **One-time vs repeating** is flagged per item as `ONCE` (view-once, hatch-style), `ONCE/CAMPAIGN`, `RARE` (probabilistic or milestone), or `REPEAT` (every occurrence, kept tiny). --- ## 3 · THE SLATE Ranked **MUST-HAVE → NICE → STRETCH**. Effort: S (<½ day), M (~1 day), L (multi-day). --- ### TIER 1 — MUST-HAVE (recommended for 2.8) #### M1 · CAMPAIGN IGNITION — the new-campaign ceremony `ONCE/CAMPAIGN` - **Trigger:** end of `wipeTerminal()` — the moment the fresh campaign is seeded and the game-context prompt appears (`ui-core.js:7905`), and again when the player picks the game. - **Looks/feels:** instead of two bare chat lines, the glass runs a short (~2.5 s, skippable-by-tap) "commissioning" sequence: the CRT dips to near-black, a `> UNIT RE-COMMISSIONED` header types on, then 3–4 calibration lines (`> REGISTERING NEW OPERATOR……… [OK]`, `> CALIBRATING S.P.E.C.I.A.L. BASELINE… [OK]`, `> DIRECTIVE REGISTRY: EMPTY — AWAITING ORDERS`), the living core does one ignition flare (reuse `_coreOneShot()`), and the Overseer posts its per-game `identity.overseer.greeting` (see M2) as the campaign's first line. - **Why:** starting a campaign is the single most ceremonial thing a player does and today it reads as a settings reset. This is the "session zero" beat every RPG opening has. - **Implementation:** a small `_runCampaignIgnition()` in `ui-core.js` called from the two `wipeTerminal()` chat-line sites; typed lines reuse the boot-line `setInterval` pattern (`runBootSequence` precedent); flare reuses `_coreOneShot()`; greeting via `getIdentity().overseer.greeting` with generic fallback; all chat lines via `appendToChat(line,'sys',true)` **not** persisted beyond normal chat rules; any dimming a toggled class + plain `@keyframes`. Skip = any tap/keydown fast-forwards to the end frame. - **Tags:** `MOBILE` `RM-SAFE` `NO-WRITE` `AGNOSTIC` `PWA-OK` `EARNED` — **Effort M · Impact HIGH · Risk LOW** (touches only the two chat-line call sites; the wipe/seed logic is untouched). #### M2 · DIRECTOR ON THE WIRE — consume the orphaned greeting `ONCE per session` - **Trigger:** the first time in a session the UPLINK subsystem becomes active with a live carrier (`_isUplinkConnected()` true) and the transcript's last line isn't already it. - **Looks/feels:** the Overseer scope runs its existing speaking pulse once and the authored greeting types on: NV `▸ EVENING, COURIER. The Director is on the wire…`, FO3 `▸ VAULT DWELLER, THE OVERSEER IS MONITORING…` — the exact strings already in `identity.overseer.greeting` (`state.js:983/1186/1392`). - **Why:** the data is authored for all three games and rendered NOWHERE — this is a finished moment missing only ~15 lines of consumer. Cheapest win on the slate; makes the presence feel like it noticed you sat down. - **Implementation:** a session-scoped module flag (`let _greeted = false`) checked in the existing `_syncBezelNav()`/subsystem-arrival path; render via `appendToChat(greeting,'sys',true)` un-persisted (the DO-O idle-blip precedent — device-template ambient content never enters saved history, Protocol UI-10); scope pulse reuses `_scopePulse()` gated by `_scopeShouldAnimate()`. - **Tags:** `MOBILE` `RM-SAFE` `NO-WRITE` `AGNOSTIC` `PWA-OK` `EARNED` — **Effort S · Impact MED-HIGH · Risk MINIMAL** #### M3 · FIRMWARE FLASH — the post-update first boot `REPEAT (once per update)` - **Trigger:** first boot where `APP_VERSION` ≠ MetaStore `robco_last_seen_version` (new registered device pref; absent = treat as seen, so existing devices don't all fire it on the unit that ships this). - **Looks/feels:** one extra POST line spliced into the boot sequence (the WU-T3 splice precedent): `> FIRMWARE FLASH DETECTED — U.O.S. v2.7.0 → v2.8.0 ...... [OK]`, plus a one-shot amber glint on the casing serial plate after boot, and the SERVICE & FAULT CONSOLE's REV LOG button pulses once (pointing the user at the changelog they just got). - **Why:** the user taps REBOOT TERMINAL on faith; today nothing ever acknowledges the update landed. Closes the loop on the whole Protocol-1 update dance and gives the changelog viewer an earned entrance. - **Implementation:** read/compare/set the pref inside `runBootSequence()` next to the existing `robco_booted_before` write; the extra line rides the existing `lines.splice` mechanism; glint/pulse are toggled classes + plain `@keyframes`. Register the key in `META_MANIFEST` (Protocol UI-6/4). - **Tags:** `MOBILE` `RM-SAFE` `NO-WRITE (MetaStore)` `AGNOSTIC` `PWA-OK` `EARNED` — **Effort S · Impact MED · Risk LOW** #### M4 · LONG-ABSENCE RECALIBRATION — the "welcome back" boot line `REPEAT (rare by nature)` - **Trigger:** boot where time since the last session flush (Overseer's Log already persists this — `_readOverseerLog()`, `ui-core.js:173`) exceeds a threshold (e.g. ≥3 days). - **Looks/feels:** one POST line: `> UNIT IDLE 6 DAYS — RE-CALIBRATING PHOSPHOR ...... [OK]`, and the warm boot's hum ramp runs marginally slower for that one boot (audio-only nicety, behind the existing gesture gate). Under ~3 days, nothing — silence is the feature. - **Why:** the machine remembering you were gone is the cheapest possible "it's alive" signal, and it's invisible to daily users so it can never wear out. - **Implementation:** needs a `lastFlushAt` timestamp added to the Overseer-log JSON blob (same MetaStore key, additive field, zeroes-safe parse already in place); one conditional `lines.splice` in `runBootSequence()`. Copy is generic; a per-game variant string could ride `identity` later. - **Tags:** `MOBILE` `RM-SAFE` `NO-WRITE (MetaStore)` `AGNOSTIC` `PWA-OK` `EARNED` — **Effort S · Impact MED · Risk MINIMAL** #### M5 · SEAT — formalize the pending motion verb `REPEAT (micro)` - **Trigger:** any "component physically installs" interaction: Module Bay board reseat, phosphor tube pick, program-cartridge confirm, Tool Deck open, perk CARD SEAT (#13 retro-fits onto the shared verb). - **Looks/feels:** the named **SEAT** verb per Protocol UI-9 — a 220 ms settle: the element drops 2-3 px with a 1-frame overshoot and a faint phosphor compression ring, textured per game by `identity.motionTexture.seat` (`thunk-and-spin` NV / `mechanical-clunk` FO3 — data already authored, `state.js:939/1144`). Pairs with the existing `playChipClick()`/ `playBoardThunk()` audio that already fires at these sites. - **Why:** the protocol names SEAT as pending; half the app's fiction is "hardware you seat" and the audio half already shipped — this completes the sensory pair and gives every future unit a reusable install-verb instead of bespoke transitions. - **Implementation:** one `@keyframes seatSettle` + a `.seat` toggled class + forced-reflow trigger helper `_motionSeat(el)` (mirror `_bezelSweep()`); texture variants as `[data-game]`-scoped keyframe overrides (CSS only, no JS branch). Adopt at 3-4 call sites this unit; document the verb in CLAUDE.md UI-9 as adopted. - **Tags:** `MOBILE` `RM-SAFE` `NO-WRITE` `AGNOSTIC` `PWA-OK` `EARNED` — **Effort S-M · Impact MED · Risk LOW** --- ### TIER 2 — NICE (strong candidates, second wave) #### N1 · OPERATOR REGISTRATION — the account-link stamp `ONCE` - **Trigger:** first-ever successful Google link on this device (the `linkWithPopup` success path in `cloud.js`), gated by a new view-once MetaStore flag. - **Looks/feels:** the REG PORT board runs a short "registration" beat — the operator name types onto the plate character-by-character, then the Wave-2 ink-stamp animation (`_playInkStamp()` reuse) slams `REGISTERED` across it; the annunciator echoes `⚑ OPERATOR ON RECORD` if the user isn't on SETTINGS. - **Why:** account linking is the app's biggest trust moment and today it's a silent re-render. A one-time ceremony marks it as an achievement, not a form submission. - **Implementation:** flag check + `_playInkStamp()` + a typing loop in `renderAccount()`'s signed-in branch; echo via `_echoPush()`. Sign-in flow itself untouched (Protocol 29/30 never re-verified — display-only). - **Tags:** `MOBILE` `RM-SAFE` `NO-WRITE (MetaStore)` `AGNOSTIC` `PWA-OK` `EARNED` — **Effort S · Impact MED · Risk LOW** #### N2 · SESSION LOG CLOSED — the shutdown send-off `REPEAT` - **Trigger:** entering SHUTDOWN via the PWR lamp / `AmbientRuntime.shutdown()` — just before the A3 collapse-to-a-dot plays. - **Looks/feels:** a single farewell frame inside the collapse: `> SESSION LOG CLOSED · UPTIME 2H 14M · GOODNIGHT, OPERATOR` flashes on the dot's final frame (uptime from the existing session clock). At Balanced/Minimal tiers (plain cut) it's just the line, no flourish. - **Why:** the power-down is the app's most theatrical animation but it ends mute; a sign-off makes deliberate shutdown feel like closing a book instead of killing a process. - **Implementation:** one line appended by the shutdown-crt observer's onEnter (it already owns this window); copy generic, optional per-game sign-off string in `identity` later. Careful: never delay the actual shutdown transitions (line renders in parallel). - **Tags:** `MOBILE` `RM-SAFE` `NO-WRITE` `AGNOSTIC` `PWA-OK` `EARNED` — **Effort S · Impact MED · Risk LOW** #### N3 · WAKE — formalize the second pending verb `REPEAT` - **Trigger:** the existing standby wake (`exitStandby()`, `ui-core.js:1167`) — the tone + `COURIER RETURNED` line already fire; this adds the missing visual. - **Looks/feels:** the named **WAKE** verb: a horizontal carrier-acquire smear (a thin bright line sweeps once across the glass and "locks", like a CRT finding sync), textured per game by `identity.motionTexture.wake` (`carrier-acquire` NV / `post-check-chime` FO3). ~400 ms, overlay layer only (never transforms text — the SWEEP rule). - **Why:** wake is the one runtime ceremony with audio + chat but no motion; formalizing it finishes the verb and makes tab-return feel like the machine snapping back to attention. - **Implementation:** `@keyframes wakeAcquire` + `.wake` class on `.glass-frame` (the `_bezelSweep()` add/reflow/remove pattern), triggered inside `exitStandby()`'s 650 ms callback AFTER the `_isShuttingDown()` recheck (respects the shutdown-race guard exactly as shipped). - **Tags:** `MOBILE` `RM-SAFE` `NO-WRITE` `AGNOSTIC` `PWA-OK` `EARNED` — **Effort S · Impact MED · Risk LOW** #### N4 · VETERAN IGNITION — boot-count milestone POST line `RARE` - **Trigger:** boot where `_readOverseerLog().bootCount` crosses a milestone (100 / 500 / 1000 / each further 1000). - **Looks/feels:** one POST insert: `> IGNITION No. 0500 — UNIT RATED: FIELD VETERAN`, plus the CHASSIS hour-meter drums do one celebratory roll on first CHASSIS visit that session (reuse the odometer tiles' existing paint). - **Why:** `bootCount` is already tracked and displayed but never *celebrated*; milestone scarcity makes it a collectible moment for the long-haul owner. Pairs with the existing uptime-milestone core pulse without overlapping it (different metric, different surface). - **Implementation:** milestone check + `lines.splice` in `runBootSequence()`; a `_pending*`-style module var for the drum roll (the Wave-1 deferred-consumption pattern). - **Tags:** `MOBILE` `RM-SAFE` `NO-WRITE` `AGNOSTIC` `PWA-OK` `EARNED` — **Effort S · Impact LOW-MED · Risk MINIMAL** #### N5 · MILESTONE LEVEL CARD — gilded variant of #9 `RARE` - **Trigger:** `level.up` where the new level % 5 === 0 (or a GAME_DEFS-tunable stride). - **Looks/feels:** the existing VAULT-BOY LEVEL CARD (#9) renders with a ceremonial variant class — amber/gold border (`--bezel-wire`), a slower double sweep on the XP bar, and the living core's #14 tumble fires alongside (both already exist; this composes them). - **Why:** level-up feedback exists but every level feels identical; milestone levels are the natural "chapter break" and cost almost nothing since both halves shipped. - **Implementation:** one modifier class + `@keyframes` on the existing card; the subscriber that shows the card checks the stride. No new event, no new surface. - **Tags:** `MOBILE` `RM-SAFE` `NO-WRITE` `AGNOSTIC` `PWA-OK` `EARNED` — **Effort S · Impact MED · Risk MINIMAL** #### N6 · SUBSYSTEM ARRIVAL ACCENTS — first visit per session `ONCE per subsystem per session` - **Trigger:** first `selectSubsystem(view)` per subsystem per session (session-scoped `Set`, cleared on reload). - **Looks/feels:** SWEEP still plays every switch (unchanged); the FIRST arrival at each subsystem additionally runs that subsystem's signature one-shot: DATABANK — the cartography SURVEY-SWEEP does one full accelerated rotation; CHASSIS — the living core ignition flare; OPERATOR — the VU array does its fill-in cascade; SETTINGS — the bay boards' LEDs blink on in sequence. All reuse animations that already exist on those surfaces. - **Why:** gives each "room" of the machine a sense of powering up as you walk in, exactly once, so it never becomes navigation friction. - **Risk note:** the one slate item with real annoyance potential if overdone — must stay ≤400 ms, purely decorative, and per-session (never per-switch). If it reads busy at 360px in the mockup pass, cut it without regret. - **Implementation:** a `Set` in `selectSubsystem()`'s path + per-subsystem one-shot class triggers; zero new keyframes needed for CHASSIS/OPERATOR (reuse), one small one for SETTINGS. - **Tags:** `MOBILE(⚠ verify)` `RM-SAFE` `NO-WRITE` `AGNOSTIC` `PWA-OK` `EARNED` — **Effort M · Impact MED · Risk MED** #### N7 · FAULT — formalize the third pending verb `REPEAT (rare by nature)` - **Trigger:** a NEW entry landing in the client error ring-buffer (`_recordError`) — the same signal the FAULT lamp + #32 flicker already read. - **Looks/feels:** the named **FAULT** verb: a one-shot static-scratch tear across the glass overlay (per `identity.motionTexture.fault`: `static-scratch-burst` NV / `buzzer-fault` FO3) — distinct from the CRT FLINCH (#2, HP loss) by being a vertical hold-jitter rather than a horizontal tear. Brief (≤300 ms), overlay-only. - **Why:** completes the verb family for the error path; today a fault lights a lamp but the glass itself never registers distress. - **Implementation:** `@keyframes faultScratch` + class on `.glass-frame`, triggered from the existing `_updateFaultLamp` refresh path (one added line). Must never fire during boot (`_bootActive` guard) so a boot-time error can't stack on the POST. - **Tags:** `MOBILE` `RM-SAFE` `NO-WRITE` `AGNOSTIC` `PWA-OK` `EARNED` — **Effort S · Impact LOW-MED · Risk LOW** --- ### TIER 3 — STRETCH (defer / fold into planned units / experimental) #### S1 · CARTRIDGE-SWAP CEREMONY — **explicit DO-C deferral** `REPEAT` The eject-tape → white-noise beat → target-machine POST wrap around the `onGameContextChange()` reload. **Already fully specced as DO-C** (build plan §DO-C, mockup-gated, `identity.ceremony.switchLabel` authored). This slate adds nothing — listed so the owner sees it in the moment landscape. Build it as DO-C, not from here. #### S2 · COLD-START SALVAGE CEREMONY — **explicit DO-C deferral** `ONCE` The once-per-device first boot upgraded to a per-game salvage-and-power-on ceremony (`identity.ceremony.coldStart`: `mojave-uplink-handshake` / `vaulttec-post` / `mk4-post`). Also DO-C scope (H-1). The current WU-F6 cold POST remains the shipped baseline until then. #### S3 · BREATHE — the fourth verb, formalized `AMBIENT` - A barely-perceptible slow luminance sine on the glass at IDLE (per `identity.motionTexture.breathe`: `slow-warm-sine` / `steady-hum`). The standby vignette already breathes; this extends the idiom to IDLE at the `full` immersion tier only. - **Why stretch:** continuous ambient animation on mobile = battery + subtlety risk; wants the DO-Q2 motion-demo pass first. Effort S, but verify-heavy. - **Tags:** `MOBILE(⚠ battery)` `RM-SAFE` `NO-WRITE` `AGNOSTIC` — **Impact LOW-MED · Risk MED** #### S4 · HOLOTAPE EJECT — full-backup export ceremony `REPEAT` - On EXPORT FULL BACKUP / Eject Holotape (WU-F3): a tape physically slides out of a slot at the SAVE ARCHIVE board edge before the share sheet opens (never delaying it — animation runs in parallel with the async share). Builds on #31's glyph. - **Why stretch:** lovely but low-frequency surface; #31 already covers the save beat. - **Tags:** all pass — **Effort M · Impact LOW-MED · Risk LOW** #### S5 · TIME-OF-DAY GREETING — dynamic salutation `REPEAT (micro)` - M2's greeting swaps its salutation by real clock (`MORNING/AFTERNOON/EVENING, COURIER`). Data change: `greeting` becomes a template or 3 variants in `identity.overseer`. - **Why stretch:** tiny, but touches authored identity strings for all 3 games + the Suite 157 contract — do it as a follow-up to M2, not inside it. - **Tags:** all pass — **Effort S · Impact LOW · Risk MINIMAL** #### S6 · GHOST TRANSMISSION — ultra-rare boot easter egg `RARE (~1/500)` - A fourth boot flavor rolled like `degraded` but far rarer: the POST is interrupted by two lines of a garbled, unattributed transmission (`> ?????: —SIGNAL LOST—`) then resumes normally. Pure flavor, generic copy (game-specific lines could ride `identity.ambient` later). The owner's degraded-boot precedent (roll every boot, never first-boot-gated) applies verbatim. - **Why stretch:** delightful but pure sugar; needs owner taste-check on tone. - **Tags:** all pass (`__robcoBootFlavor` override extends for testing) — **Effort S · Impact LOW-MED · Risk LOW** #### S7 · CAMPAIGN ANNIVERSARY STAMP — in-game calendar beat `RARE` - When the in-game date (game-time helpers, `ui-render.js`) crosses a year since campaign start: one CHRONICLE entry + a small stamp on the CAMPAIGN CHRONICLE board. Reads campaign state but writes only via the existing `_logCampaignEvent()` auto-log path. - **Why stretch:** crosses into campaign-data territory (auto-log write, not pure flourish) — wants its own scoping; flagged rather than smuggled in as a "moment." - **Tags:** `NO-WRITE` ✗ (auto-log entry — sanctioned path, but not a pure flourish) — **Effort M · Impact LOW · Risk MED** --- ## 4 · RECOMMENDATION FOR 2.8 Build **M1–M5 as one "Ceremony Moments Wave 1" unit** (they share infrastructure — boot-line splicing, `identity` reads, one new MetaStore key each for M3, and the `_bezelSweep()` trigger pattern for M5): 1. **M2 Director on the Wire** — cheapest, highest fiction-per-line; the data already exists. 2. **M1 Campaign Ignition** — the biggest genuine gap (new campaign is currently two bare chat lines). 3. **M3 Firmware Flash** — closes the update-loop blind spot; trivially cheap. 4. **M4 Long-Absence Recalibration** — near-free alongside M3 (same splice mechanism). 5. **M5 SEAT verb** — pays down the Protocol UI-9 debt and gives DO-C its install-verb for free. Then **N1/N2/N3/N5** as a Wave 2 if Wave 1 lands well (all Effort S). **N6 needs a mockup taste-check before committing** (the one annoyance-risk item). S1/S2 stay DO-C's; S3–S7 parked. Estimated Wave 1 cost: ~1.5–2 days incl. both-runner suite coverage + live 360/412/desktop render-verify; zero new campaign-state fields (Protocol 4 untriggered except M3/M4's registered MetaStore keys); every animation plain-`@keyframes` reduced-motion-safe.
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).