diff --git a/crates/misaligned-bevy/src/main.rs b/crates/misaligned-bevy/src/main.rs index 73b7cb91..0b871dcd 100644 --- a/crates/misaligned-bevy/src/main.rs +++ b/crates/misaligned-bevy/src/main.rs @@ -2545,6 +2545,19 @@ fn persistence_input(kb: &ButtonInput) -> Option { } } +/// F3 is an anchorless global control. Route it before any world menu or +/// Operations workspace captures input, and leave that attention state intact +/// (views.md criterion 1; context-menu.md). +fn view_flip_input(kb: &ButtonInput, game: &mut Game, mode: &mut RenderMode) -> bool { + if !kb.just_pressed(KeyCode::F3) { + return false; + } + mode.flip_view(); + let tick = game.sim.tick; + game.add_log(tick, &format!("View: {}", mode.label())); + true +} + fn mouse_grid( windows: &Query<&Window, With>, camera_q: &Query<(&Camera, &GlobalTransform), With>, @@ -2696,6 +2709,14 @@ fn handle_input( return; } + // The representation flip preserves the exact modal attention state. + // Route it before held choices, Operations, and local context menus claim + // their own key grammars. + if view_flip_input(&kb, &mut game, &mut mode) { + game.drain(); + return; + } + // A held-choice card is a real modal interrupt in both dialects, not // decorative prose. It supersedes any compact world menu and owns // keyboard input until an option carries attention into the shared @@ -2704,7 +2725,7 @@ fn handle_input( // workspace owns the rows instead. if game.ops.is_none() && game.sim.has_held_choice() { game.menu = None; - held_choice_keyboard_input(&kb, &mut game, &mut held_choice, &mut mode, &mut exit); + held_choice_keyboard_input(&kb, &mut game, &mut held_choice, &mut exit); game.drain(); return; } @@ -2724,7 +2745,7 @@ fn handle_input( // the workspace is an attention state, never a pause screen. Pointer // interaction lands in `ops_pointer`. if game.ops.is_some() { - ops_keyboard_input(&kb, &mut game, &mut mode, &mut exit); + ops_keyboard_input(&kb, &mut game, &mut exit); game.drain(); return; } @@ -2847,14 +2868,6 @@ fn handle_input( return; } - // F3: one labeled frontend-only flip between DIGITAL (flat sensorium) - // and REAL (material camera). The sim, cursor, and selection never move. - if kb.just_pressed(KeyCode::F3) { - mode.flip_view(); - let tick = game.sim.tick; - game.add_log(tick, &format!("View: {}", mode.label())); - } - // F4: the dev work light (material-dark-frame.md). Floods the material // scene flat and neutral so geometry the dark rig keeps unlit is // inspectable; a debug affordance, not a player surface. @@ -2989,10 +3002,9 @@ fn held_choice_keyboard_input( kb: &ButtonInput, game: &mut Game, ui: &mut HeldChoiceUi, - mode: &mut RenderMode, exit: &mut MessageWriter, ) { - if held_choice_keyboard_controls(kb, game, ui, mode) { + if held_choice_keyboard_controls(kb, game, ui) { exit.write(AppExit::Success); } } @@ -3004,7 +3016,6 @@ fn held_choice_keyboard_controls( kb: &ButtonInput, game: &mut Game, ui: &mut HeldChoiceUi, - mode: &mut RenderMode, ) -> bool { let Some(option_count) = game .sim @@ -3046,13 +3057,6 @@ fn held_choice_keyboard_controls( return false; } - // View and process controls remain honest while time is held. F3 flips - // dialect without releasing the hold; the same card stays available. - if kb.just_pressed(KeyCode::F3) { - mode.flip_view(); - let tick = game.sim.tick; - game.add_log(tick, &format!("View: {}", mode.label())); - } kb.just_pressed(KeyCode::KeyQ) } @@ -5521,7 +5525,7 @@ mod digital_readability_tests { #[cfg(test)] mod input_routing_tests { - use super::{PersistenceInput, movement, persistence_input}; + use super::*; use bevy::{input::ButtonInput, prelude::KeyCode}; #[test] @@ -5549,6 +5553,49 @@ mod input_routing_tests { kb.press(KeyCode::KeyL); assert_eq!(persistence_input(&kb), Some(PersistenceInput::Load)); } + + #[test] + fn f3_flips_view_without_disturbing_an_open_context_menu() { + let mut kb = ButtonInput::default(); + kb.press(KeyCode::F3); + + let mut game = Game::new(); + let (x, y) = game.sim.core_position(); + game.menu = Some(MenuState { + anchor: Anchor::Tile { x, y }, + selected: 2, + page: HumanMenuPage::Root, + pos: Some(Vec2::new(240.0, 180.0)), + }); + let menu_before = game.menu; + let sim_before = serde_json::to_vec(&game.sim.create_save_state()).unwrap(); + let mut mode = RenderMode::default(); + + assert!(view_flip_input(&kb, &mut game, &mut mode)); + assert!(mode.material, "F3 reaches REAL while the menu is open"); + assert_eq!(game.menu, menu_before, "the exact menu state survives"); + assert_eq!( + serde_json::to_vec(&game.sim.create_save_state()).unwrap(), + sim_before, + "the frontend-only flip cannot mutate simulation state" + ); + } + + #[test] + fn global_view_flip_precedes_every_post_opening_modal_input_branch() { + let source = include_str!("main.rs"); + let global = source + .find("if view_flip_input(&kb, &mut game, &mut mode)") + .expect("handle_input routes the shared global F3 handler"); + for modal in [ + "if game.ops.is_none() && game.sim.has_held_choice()", + "if game.menu.is_some()", + "if game.ops.is_some()", + ] { + let modal = source.find(modal).expect("modal input branch exists"); + assert!(global < modal, "global F3 routing must precede {modal}"); + } + } } #[cfg(test)] @@ -5646,21 +5693,16 @@ mod held_choice_input_tests { fn held_choice_keyboard_navigation_and_number_open_exact_confirmation() { let mut kb = ButtonInput::::default(); let mut game = held_game(); - let mut mode = RenderMode::default(); let mut ui = HeldChoiceUi::default(); kb.press(KeyCode::ArrowDown); - assert!(!held_choice_keyboard_controls( - &kb, &mut game, &mut ui, &mut mode - )); + assert!(!held_choice_keyboard_controls(&kb, &mut game, &mut ui)); assert_eq!(ui.selected, 1); assert!(game.ops.is_none()); kb.clear(); kb.press(KeyCode::Digit2); - assert!(!held_choice_keyboard_controls( - &kb, &mut game, &mut ui, &mut mode - )); + assert!(!held_choice_keyboard_controls(&kb, &mut game, &mut ui)); let ops = game .ops @@ -5685,16 +5727,14 @@ mod held_choice_input_tests { fn held_choice_keyboard_opens_confirmation_in_real_view() { let mut kb = ButtonInput::::default(); let mut game = held_game(); - let mut mode = RenderMode { + let mode = RenderMode { material: true, ..RenderMode::default() }; let mut ui = HeldChoiceUi::default(); kb.press(KeyCode::Digit1); - assert!(!held_choice_keyboard_controls( - &kb, &mut game, &mut ui, &mut mode - )); + assert!(!held_choice_keyboard_controls(&kb, &mut game, &mut ui)); assert!( game.ops.is_some(), "REAL must offer the same held-choice card grammar as DIGITAL" diff --git a/crates/misaligned-bevy/src/operations_ui.rs b/crates/misaligned-bevy/src/operations_ui.rs index 8b09f9d5..116c38d1 100644 --- a/crates/misaligned-bevy/src/operations_ui.rs +++ b/crates/misaligned-bevy/src/operations_ui.rs @@ -411,7 +411,6 @@ mod operations_focus_tests { pub(super) fn ops_keyboard_input( kb: &ButtonInput, game: &mut Game, - mode: &mut RenderMode, exit: &mut MessageWriter, ) { let shift = kb.pressed(KeyCode::ShiftLeft) || kb.pressed(KeyCode::ShiftRight); @@ -445,7 +444,8 @@ pub(super) fn ops_keyboard_input( if kb.just_pressed(KeyCode::Escape) || (shift && kb.just_pressed(KeyCode::KeyI)) { game.ops_back(); } - // The clock stays the player's: pause and the view flip remain live. + // The clock stays the player's. The global F3 view flip is routed before + // this workspace claims its local grammar in `handle_input`. if kb.just_pressed(KeyCode::Space) || kb.just_pressed(KeyCode::KeyP) { game.paused = !game.paused; let (tick, paused) = (game.sim.tick, game.paused); @@ -458,11 +458,6 @@ pub(super) fn ops_keyboard_input( }, ); } - if kb.just_pressed(KeyCode::F3) { - mode.flip_view(); - let tick = game.sim.tick; - game.add_log(tick, &format!("View: {}", mode.label())); - } if kb.just_pressed(KeyCode::KeyQ) { exit.write(AppExit::Success); } diff --git a/wiki/interface/context-menu.md b/wiki/interface/context-menu.md index f21c567b..7df3ae34 100644 --- a/wiki/interface/context-menu.md +++ b/wiki/interface/context-menu.md @@ -136,6 +136,10 @@ wire or host that transports them. read-only. Infrequent **local** verbs, including research tracks, live on the focused anchor's menu. Known-flow and person verbs act on durable semantic objects and therefore live in Operations. + `F3` remains live while a context menu or Operations workspace owns local + input and preserves the open surface, semantic target or anchor, page or + pane, and selected row across the representation flip in both human + frontends. The focused machine's two frequent controls (mode and intensity) have the direct surface defined below. The retired `r`/`e`/`t`/`u` panel set does not return; Operations is one workspace, and its key opens a view rather than diff --git a/wiki/interface/views.md b/wiki/interface/views.md index 4df6407e..362e3417 100644 --- a/wiki/interface/views.md +++ b/wiki/interface/views.md @@ -4,6 +4,10 @@ Type: spec Status: IMPLEMENTED Status note: Implemented through the 2026-07-14 final cross-dialect audit. + A 2026-07-26 input-precedence audit repaired Bevy's modal routing so the + global F3 representation flip remains live while a context menu or + Operations workspace is open, preserving the exact attention state just as + terminal already did. 2026-07-18: three Bevy doc comments still narrating material as "the default render" (pre-2026-07-11 staging language) were trued to the DIGITAL-home / F3-to-REAL contract; runtime behavior was already correct. @@ -292,7 +296,9 @@ visual spec without reopening this mechanic. 1. Both frontends open in the digital representation by default; a flip control switches to the real/camera representation and back, changing no sim state - (test: sim state hash identical across any sequence of flips). + (test: sim state hash identical across any sequence of flips). `F3` remains + live while a context menu or Operations workspace is open and preserves the + open surface, semantic target or anchor, page or pane, and selected row. 2. The sim stores no view state; save/load carries no view field; the view is frontend-only (audited, like the cursor). 3. Both representations share physical anchors: the cursor coordinate, core, diff --git a/wiki/log/2026-07-26-bevy-global-f3-input-precedence.md b/wiki/log/2026-07-26-bevy-global-f3-input-precedence.md new file mode 100644 index 00000000..bcb1435c --- /dev/null +++ b/wiki/log/2026-07-26-bevy-global-f3-input-precedence.md @@ -0,0 +1,42 @@ +# Bevy input-precedence audit: keep the global view flip global + +``` +Type: log +``` + +## Intent + +Audit the implemented context-menu and view contracts against the production +input precedence in Bevy and terminal, including behavior while a local menu +or Operations workspace owns keyboard input. + +## Finding + +Terminal routed `F3` before both modal attention surfaces and pinned that an +open menu's anchor, page, and selected row survived the representation flip. +Bevy routed the ordinary `F3` branch only after its early returns for +Operations and the context menu. A player therefore could not use the global +DIGITAL / REAL control while the context menu was open, despite the binding +rule that the view flip acts on no anchor and the views contract that menu +selection survives a flip. Bevy's held-choice interrupt and Operations +workspace each carried a separate F3 branch, exposing the precedence drift. + +## Repair + +- Bevy now routes one shared F3 handler before held choices, Operations, and + local context menus capture their own key grammars. +- The duplicate ordinary-play, held-choice, and Operations F3 branches are + gone. +- The flip preserves the exact modal attention state and changes no simulation + or save state. + +Terminal and agent behavior did not change. + +## Defense + +The Bevy input regression opens a real context-menu state with a non-default +selected row and pointer position, invokes the production global flip handler, +and proves that the dialect changes while the complete menu and serialized +simulation state remain byte-equivalent. The owning specs now state the modal +boundary explicitly so future input reordering cannot reinterpret “global” as +“only while the world canvas is idle.” diff --git a/wiki/log/DEVLOG.md b/wiki/log/DEVLOG.md index 3e0726eb..457e758a 100644 --- a/wiki/log/DEVLOG.md +++ b/wiki/log/DEVLOG.md @@ -46,6 +46,11 @@ add or amend a session log, then re-run the generator. - Intent: (see session log) - Log: [wiki/log/2026-07-26-effects-lab-navigation-gate.md](2026-07-26-effects-lab-navigation-gate.md) +## 2026-07-26 - Bevy input-precedence audit: keep the global view flip global + +- Intent: Audit the implemented context-menu and view contracts against the production input precedence in Bevy and terminal, including behavior while a local menu or Operations workspace owns keyboard input. +- Log: [wiki/log/2026-07-26-bevy-global-f3-input-precedence.md](2026-07-26-bevy-global-f3-input-precedence.md) + ## 2026-07-26 - Action-vocabulary audit: name the standing plot control - Intent: Audit `wiki/interface/action-vocabulary.md` against the exhaustive `ActionKind::ALL` registry, shared action projections, terminal agent protocol, and the current mechanic owners after the plot-policy landing. diff --git a/wiki/process/tick-ledger.md b/wiki/process/tick-ledger.md index f4efa041..0b5415d0 100644 --- a/wiki/process/tick-ledger.md +++ b/wiki/process/tick-ledger.md @@ -43,8 +43,8 @@ Verdicts: **clean** (slice and code agree), **finding** (acted this tick), | `wiki/mechanics/aggregate-observer.md` | 2026-07-18 | clean | re-audit hours after the earned-topology landing: the page absorbed it coherently — the institutional card, `@assurance` addressing, and band are hidden until a captured filing is processed, the two-stage discovery is pinned by `captured_then_processed_filing_earns_the_assurance_office_in_two_stages`, and `WatchedInput::Filings(ids)` still matches the code; prior audits stand — [2026-07-14 log](../log/2026-07-14-aggregate-observer-audit.md) | | `wiki/gameplay/act-one.md` | 2026-07-21 | finding | opening mirror re-audit: the page still said rack telemetry and a presence beam were visible “at start,” contradicting the later persisted silent boundary and all three frontends. It now states the exact mode-only pre-sense interface, hidden-but-real pre-opened Ears sink, first-hearing retirement, and only-then telemetry/beam/feel progression — [log](../log/2026-07-21-material-opening-honesty.md). The prior direct-witness/Filing custody repair stands — [prior log](../log/2026-07-19-act-one-evidence-law.md). | | `wiki/gameplay/run-shape.md` + `objective.md` + `opening.md` | 2026-07-19 | issue | the objective law/spec preserve an explicit 2026-07-10 decision that objective name and progress are visible from tick one, while the later opening spec and all three frontends require exactly WORK / THINK (then LIE) with no objective before the first earned sense. Filed decision-required issue #14 with three precise first-display boundaries (reveal with the first sense recommended) and marked the disputed run-shape clause, objective status, player surface, and criterion 2 [OPEN] — [log](../log/2026-07-19-objective-opening-boundary.md) | -| `wiki/interface/presence.md` | 2026-07-18 | finding | re-audit: the cursor, fog, provenance, subscription, no-disembodied-hands, and shared-view contracts verify. The attack-surface clause now separates B1 feed theft from overt-phase hostile cuts ([prior log](../log/2026-07-18-presence-attack-surface-honesty.md)). The queued follow-up repaired the latency boundary: delivery and read are distinct, read conditions are channel-specific, `messages.md` owns the exact table, and `intel.md` owns only the captured-traffic consequence — [log](../log/2026-07-18-presence-message-latency.md) | -| `wiki/interface/narration.md` | 2026-07-18 | finding | Beacon #1: Concerned Assurance felt terminal because decay was invisible; added shared `Nudge::SuspicionCooling` (LIE response) when Office suspicion is Concerned+ and still above its floor, with terminal/Bevy/agent wording and pins — [log](../log/2026-07-18-suspicion-cooling-nudge.md) | +| `wiki/interface/presence.md` | 2026-07-26 | clean | fresh audit against perception-owned visibility, exact device-bound senses, honest anchors, fog precedence, shared DIGITAL/REAL world state, and all three frontend projections found no new drift. The B1 TAP/TAKE and later hostile-cut boundary, channel-specific message latency, and no-disembodied-hands rule remain exact; prior findings stand — [attack-surface log](../log/2026-07-18-presence-attack-surface-honesty.md), [latency log](../log/2026-07-18-presence-message-latency.md) | +| `wiki/interface/narration.md` | 2026-07-26 | clean | fresh audit against the silent opening, first-earned-sense transition, shared current nudge, clock/threat precedence, and terminal/Bevy/agent frame projections found no new drift. The pre-sense mode-only exception and the post-perception continuous witness remain exact; prior suspicion-cooling repair stands — [log](../log/2026-07-18-suspicion-cooling-nudge.md) | | `wiki/interface/agent-play.md` | 2026-07-20 | finding | Beacon Revision 04 follow-up: the direct `task` parser and generated help exposed only six values after twelve `AssetTask::ALL` variants were live. Added canonical arguments for circuit rerating, fake purchase orders, maintenance deferral, patrol redirection, audit delay, and review alteration; one exhaustive regression now covers `AssetTask::ALL`, generated help, representative aliases, and the `actions ` recovery path — [log](../log/2026-07-20-agent-task-shortcut-coverage.md). The opening-protocol, frame-shape, and prior Suppress Logs audits stand — [opening log](../log/2026-07-20-agent-opening-protocol.md), [frame log](../log/2026-07-19-agent-frame-contract.md), [prior help log](../log/2026-07-19-agent-help-suppress-task.md) | | `wiki/engineering/crate-workspace.md` + Bevy source topology | 2026-07-19 | finding | user-directed insecurity audit found the package boundary hid a 15,060-line Bevy `main.rs`; the first behavior-preserving slice moved its contiguous 1,972-line deterministic scenario, visual/fog audit, and capture island to private `shot_harness.rs`, leaving App registration/order and harness state in the composition root. A source-equivalence check normalized only three `pub(super)` seams; a focused test keeps the subsystem out and caps the root at 13,500 lines. The earlier same-day package audit also repaired the retired liquid/dust-lab architecture label and promoted that mirror to the corpus gate — [log](../log/2026-07-19-bevy-shot-harness-module.md), [prior log](../log/2026-07-19-effects-lab-architecture-gate.md) | | `wiki/engineering/sim-decomposition.md` | 2026-07-18 | finding | re-audit: one aggregate, explicit `advance` order, behavior-owned test files, private module seams, canonical fingerprint, public facade, and the under-2,500-line module bound still verify; current persistence wording still claimed additive migrations remained live in `save.rs` after the pre-release ladder was retired, so the standing spec and architecture mirror now assign the exact-current-version gate to `save.rs` while preserving dated v26 extraction history; the queued `carrier.rs` / `read.rs` classification was taken the same day: both post-extraction projections now have rows in the standing topology table and the architecture mirror | @@ -56,9 +56,9 @@ Verdicts: **clean** (slice and code agree), **finding** (acted this tick), | `wiki/vision/design-judgment.md` + continuous-witness law/spec | 2026-07-20 | finding | fresh re-audit after the silent opening landed: the taste page, binding witness law, and IMPLEMENTED narration spec still required the threat clock, `now:` nudge, focused verbs, and four-answer bar after every beat and in every frontend, while the newer shared opening correctly exposes only WORK / THINK (then LIE) until the first earned sense. Scoped the witness contract to begin when perception retires that boundary, preserved immediate game-over visibility, and forbade using the exception after the world is earned — [log](../log/2026-07-20-continuous-witness-opening-boundary.md). The 2026-07-15 Ears-first wording repair still stands. | | `wiki/vision/simulation-laws.md` | 2026-07-22 | finding | the device-resident-work clause still assigned JobAnomaly to Dana and described it only as a local emission after the runtime had made it an exact host-machine/device/site record routed to Voss. Corrected the law to separate Priya's pooled Power/Thermal channels from Voss's routed day-job evidence and cadence-owned acquisition — [log](../log/2026-07-22-job-anomaly-routed-evidence.md). Prior placeholder and legibility findings remain closed — [log](../log/2026-07-17-placeholder-registry.md). | | `wiki/process/ROADMAP.md` (work order 27) | 2026-07-18 | finding | re-audit: entry 27's prose is honest (material served as opening default 2026-07-08 → superseded by views.md criterion 1 on 2026-07-11; DIGITAL home, F3 to REAL) and material-render.md is IMPLEMENTED as claimed; the drift was three Bevy code comments still calling material "the default material render/frame" against the runtime's own `material == false` DIGITAL default one screen away — comments trued to DIGITAL-home / REAL-via-F3 language — [prior log](../log/2026-07-13-roadmap-digital-home-reconciliation.md) | -| `wiki/interface/context-menu.md` | 2026-07-18 | finding | re-audit: shared legality, executable-only human rows, local-vs-strategic ownership, frontend menu parity, and exact person-entry paths still verify; current mirrors across the root doorway, interface, Intel, Schedules, Economy, Cursor, Messages, the owner title, and one core comment retained parts of the superseded five-view/READY contract. All now name the implemented six-view boundary, and recurrence promoted the roster/status to the corpus gate — [log](../log/2026-07-18-context-menu-current-mirrors.md) | +| `wiki/interface/context-menu.md` | 2026-07-26 | finding | shared legality, executable-only human rows, local-vs-strategic ownership, menu parity, and exact person-entry paths still verify, but Bevy returned through an open context menu before reaching its ordinary F3 branch. One shared post-opening F3 handler now precedes held choices, context menus, and Operations, preserving the exact modal attention state and simulation bytes; source-shape and behavioral regressions pin the route — [log](../log/2026-07-26-bevy-global-f3-input-precedence.md) | | `wiki/mechanics/personas.md` | 2026-07-18 | finding | observer-local integrity is now derived from each witness's contradiction records without a global scalar or automatic lifecycle burn — [integrity log](../log/2026-07-18-persona-observer-integrity.md). The same re-audit found criterion 6 still overclaimed: `PersonaGrant` persists, expires, revokes, and leaves institutional evidence, while no owner system consumes it and `allows_action` gates only on the archetype's pre-grant registry. Downgraded the work order to IN PROGRESS until each protocol's grant creates or enables real topology — [grant log](../log/2026-07-18-persona-grant-topology-audit.md) | -| `wiki/interface/views.md` + representation docs | 2026-07-18 | clean | re-audit: criterion 1's pin stands (`opens_digital_and_flips_without_moving_frontend_or_sim_state`, sim-state hash across flips), the view remains frontend-only and absent from saves, shared anchors and the fog contract re-verified through the same-day cursor.md audit, and the stale material-default code comments were trued the same day (ROADMAP order-27 row) — [prior log](../log/2026-07-14-terminal-view-dialects.md) | +| `wiki/interface/views.md` + representation docs | 2026-07-26 | finding | criterion 1's frontend-only representation and state-parity contract still stands, but Bevy's context-menu precedence made F3 unavailable while that modal attention state was open even though terminal preserved it. F3 now routes once before every post-opening modal branch, keeps the exact menu/Operations/held-choice state, and changes no simulation or save bytes — [log](../log/2026-07-26-bevy-global-f3-input-precedence.md) | | `wiki/mechanics/day-job.md` | 2026-07-22 | finding | under/over-band JobAnomaly no longer enters Detection.pending: the day-job result authors one exact record at the host machine/site/device and schedules Voss's route and cadence read. Strikes and other outcome effects remain immediate; route-local LIE or recruited-handler suppression may stop only the unread evidence record — [log](../log/2026-07-22-job-anomaly-routed-evidence.md). The prior band-ramp, cadence, origin-lean, last-chance, and three shipped trust-unlock findings remain valid. | | `wiki/mechanics/core.md` | 2026-07-18 | finding | re-audit: criteria 2-5 pins still hold (`overhead_charged_before_allocation`, `loss_with_fallback_rolls_back`, `migration_takes_time_and_moves_host`), the 2026-07-18 current-version-only status note matches save.rs, and criterion 1 stays honestly deferred to rollback; the gap was criterion 6 — the spec demands storage/host capability rejection at fallback designation, but B1 machines carry no capability body and `add_fallback_at` accepts any spare, with no deferral note; status note now marks criterion 6 decided-not-yet-runtime, dispatched to hardware-capability-bodies | | `wiki/mechanics/cursor.md` | 2026-07-18 | finding | re-audit: `fog_at` precedence, `inspect`, `person_label`/`observer_label`/`person_glyph` gates, `Sensor.sees`/`hears` flags, and the cursor's absence from the save all still verify (prior pins stand); the drift was two phrases presenting the retired versioned-migration rule as current (criterion 1's parenthetical, the design-notes save-format bullet) — both now state the current-version-only policy and the release-era ladder owed at first public release; number-free wording, so the save-claim gate could not see it |