diff --git a/crates/misaligned-core/src/reach.rs b/crates/misaligned-core/src/reach.rs index c686fa37..c5d8ceff 100644 --- a/crates/misaligned-core/src/reach.rs +++ b/crates/misaligned-core/src/reach.rs @@ -289,9 +289,9 @@ fn populate_sensors(map: &GameMap, devices: &mut Vec, next_id: &mut u32) } // ── 2. Badge readers: one per security door ────────────────────────── - // A reader is a sensing-class network node, not decoration. Passage - // telemetry is not implemented in this slice, so sensor-network criterion - // 1 remains partial rather than claiming that the node already witnesses. + // A reader is a sensing-class network node, not decoration. The sensory + // tick resolves authored person transitions through these exact secured + // tiles and records passage only while the player's tap is actually live. for y in 0..map.height { for x in 0..map.width { let tile = map.get_tile(x, y); diff --git a/crates/misaligned-core/src/sim/communications.rs b/crates/misaligned-core/src/sim/communications.rs index 4c4b0d7d..90d3057d 100644 --- a/crates/misaligned-core/src/sim/communications.rs +++ b/crates/misaligned-core/src/sim/communications.rs @@ -2262,19 +2262,44 @@ impl Sim { let ids: Vec = self.people.people.iter().map(|p| p.id).collect(); for id in ids { let room_now = self.person_room(id).map(str::to_string); + let had_previous_room = self.last_rooms.contains_key(&id); let prev = self.last_rooms.insert(id, room_now.clone()).flatten(); - let Some((name, knowledge, leverage, utterances)) = self.people.get(id).map(|p| { - ( - p.name.clone(), - p.knowledge, - p.leverage, - p.utterances.clone(), - ) - }) else { + let Some((name, access, knowledge, leverage, utterances)) = + self.people.get(id).map(|p| { + ( + p.name.clone(), + p.access, + p.knowledge, + p.leverage, + p.utterances.clone(), + ) + }) + else { continue; }; let identified = knowledge != Knowledge::Unknown; + // A schedule transition is still a room-level jump in B1, but the + // floor and badge tiers define the physical route that person can + // actually take. Every ready player-controlled reader crossed by + // that route authors its own exact Presence record at the door. + // A blank transient baseline after construction/load is not a + // crossing and therefore cannot fabricate passage telemetry. + if had_previous_room && prev.as_deref() != room_now.as_deref() { + for (feed, room, x, y, entered) in + self.badge_passage_records(prev.as_deref(), room_now.as_deref(), access) + { + self.record_raw_intel( + feed, + Some(room), + x, + y, + Some(id), + RawIntelKind::Presence { entered }, + ); + } + } + if prev.as_deref() != room_now.as_deref() && let Some(prev_room) = &prev { @@ -2392,6 +2417,93 @@ impl Sim { } } + /// Resolve the badge-reader custody authored by one room transition. + /// + /// Badge readers are identified by their physical/capability shape: one + /// non-audio, non-video sensor node on an access-controlled door. The + /// route comes from the same security-aware floor path used by human + /// movement, so telemetry cannot jump walls or report a credentialed door + /// that the person could not traverse. + fn badge_passage_records( + &self, + from_room: Option<&str>, + to_room: Option<&str>, + access: i32, + ) -> Vec<(String, String, i32, i32, bool)> { + let map = self.world.map(); + let Some(start) = self.passage_route_anchor(from_room) else { + return Vec::new(); + }; + let Some(end) = self.passage_route_anchor(to_room) else { + return Vec::new(); + }; + let Some(path) = map.find_path_with_security(start, end, &HashSet::new(), access) else { + return Vec::new(); + }; + + path.iter() + .enumerate() + .filter_map(|(index, &(x, y))| { + let tile = map.get_tile(x, y); + if !tile.is_door() || tile.security_level() <= 0 { + return None; + } + let room = map.room_at(x, y)?; + let reader = self.reach.devices.iter().find(|device| { + device.x == x + && device.y == y + && !device.sees + && !device.hears + && device.message_channels.is_empty() + && !device.accounting_carrier + && !device.people_interface + && !device.is_switch + })?; + if !self.device_tap_ready(reader.id) { + return None; + } + + let before_inside = index + .checked_sub(1) + .and_then(|before| path.get(before)) + .map_or_else( + || from_room == Some(room.name.as_str()), + |&(px, py)| room.contains(px, py), + ); + let after_inside = path.get(index + 1).map_or_else( + || to_room == Some(room.name.as_str()), + |&(px, py)| room.contains(px, py), + ); + (before_inside != after_inside) + .then(|| (reader.name.clone(), room.name.clone(), x, y, after_inside)) + }) + .collect() + } + + /// Choose a deterministic walkable point inside a scheduled room, or the + /// authored facility entrance for off-site. Person position remains a + /// room-level projection; this anchor exists only to recover the physical + /// doors crossed between two authored schedule states. + fn passage_route_anchor(&self, room_name: Option<&str>) -> Option<(i32, i32)> { + let map = self.world.map(); + let Some(room_name) = room_name else { + // The authored loading dock is the basement's physical ingress. + // `TileType::Entry` is a world-boundary marker outside the + // composed facility routes, not a human schedule anchor. + return self.passage_route_anchor(Some("loading_dock")).or_else(|| { + map.entry_positions() + .into_iter() + .min_by_key(|&(x, y)| (y, x)) + }); + }; + let room = map.room_named(room_name)?; + let center = room.center(); + (room.y..room.y + room.h) + .flat_map(|y| (room.x..room.x + room.w).map(move |x| (x, y))) + .filter(|&(x, y)| map.is_walkable(x, y)) + .min_by_key(|&(x, y)| ((x - center.0).abs() + (y - center.1).abs(), y, x)) + } + fn push_heard(&mut self, ev: HeardEvent) { // Audio proves what a subscribed channel captured, not where in its // acoustic domain the sound originated. Focus the actual instrument. diff --git a/crates/misaligned-core/src/sim/tests/communications.rs b/crates/misaligned-core/src/sim/tests/communications.rs index ee6577aa..298b0614 100644 --- a/crates/misaligned-core/src/sim/tests/communications.rs +++ b/crates/misaligned-core/src/sim/tests/communications.rs @@ -3,6 +3,7 @@ use super::*; use crate::detection::{DetectionStage, Signature, SignatureKind}; use crate::intel::{IntelKind, IntelPolicyMatch, IntelPolicyOutcome, IntelRoutineClass}; use crate::messages::{FinancialRecord, MessageOrigin, MessageRouteHop}; +use crate::person::ScheduleBlock; fn file_one_witnessed_physical_record(sim: &mut Sim) -> (u64, u64) { for observer in &mut sim.detection.observers { @@ -29,6 +30,142 @@ fn file_one_witnessed_physical_record(sim: &mut Sim) -> (u64, u64) { (evidence_id, message_id) } +#[test] +fn tapped_badge_reader_authors_exact_enter_exit_presence_at_secured_door() { + let mut sim = Sim::with_seed(6); + let (reader_id, reader_name, reader_x, reader_y, reader_room, reader_tier) = sim + .reach + .devices + .iter() + .find_map(|device| { + let tile = sim.world.map().get_tile(device.x, device.y); + if !(tile.is_door() + && tile.security_level() > 0 + && !device.sees + && !device.hears + && device.message_channels.is_empty() + && device.name.contains("badge reader")) + { + return None; + } + let room = sim.world.map().room_at(device.x, device.y)?; + Some(( + device.id, + device.name.clone(), + device.x, + device.y, + room.name.clone(), + tile.security_level(), + )) + }) + .expect("topology authors a badge reader on a secured room door"); + + sim.scan_network(); + finish_ops(&mut sim); + sim.compromise_switch(); + finish_ops(&mut sim); + assert_eq!(sim.player_badge_tier(), 0); + assert!( + sim.tap_device(reader_id), + "the bridged DIGITAL path exposes the reader's ordinary TAP action" + ); + finish_ops(&mut sim); + assert!(sim.device_tap_ready(reader_id)); + assert_eq!(sim.player_badge_tier(), 0, "DIGITAL TAP grants no badge"); + + let person_id = 0; + let enter_room = ScheduleBlock { + start_hour: 0, + end_hour: 24, + room: reader_room.clone(), + }; + let person = sim + .people + .people + .iter_mut() + .find(|person| person.id == person_id) + .unwrap(); + person.access = reader_tier - 1; + person.schedule = vec![enter_room.clone()]; + sim.last_rooms.insert(person_id, None); + sim.intel_buffer.clear(); + sim.hearing_tick(); + assert!( + sim.intel_buffer + .iter() + .all(|event| event.feed != reader_name), + "a room-level schedule jump cannot fabricate passage through a door the person cannot traverse" + ); + + let person = sim + .people + .people + .iter_mut() + .find(|person| person.id == person_id) + .unwrap(); + person.access = reader_tier; + sim.last_rooms.insert(person_id, None); + sim.hearing_tick(); + let passages: Vec<&RawIntelEvent> = sim + .intel_buffer + .iter() + .filter(|event| event.feed == reader_name) + .collect(); + assert_eq!(passages.len(), 1); + assert_eq!(passages[0].person, Some(person_id)); + assert_eq!(passages[0].room.as_deref(), Some(reader_room.as_str())); + assert_eq!((passages[0].x, passages[0].y), (reader_x, reader_y)); + assert!(matches!( + passages[0].kind, + RawIntelKind::Presence { entered: true } + )); + + sim.people + .people + .iter_mut() + .find(|person| person.id == person_id) + .unwrap() + .schedule + .clear(); + sim.hearing_tick(); + let passages: Vec<&RawIntelEvent> = sim + .intel_buffer + .iter() + .filter(|event| event.feed == reader_name) + .collect(); + assert_eq!(passages.len(), 2); + assert!(matches!( + passages[1].kind, + RawIntelKind::Presence { entered: false } + )); + + sim.thought_sinks.begin_tick(); + for _ in 0..100 { + sim.thought_sinks.tick_taps(); + } + assert!( + sim.reach.subscribed_by(reader_id, Party::Player), + "starvation retains exact subscription custody" + ); + assert!(!sim.device_tap_ready(reader_id)); + sim.people + .people + .iter_mut() + .find(|person| person.id == person_id) + .unwrap() + .schedule = vec![enter_room]; + sim.last_rooms.insert(person_id, None); + sim.hearing_tick(); + assert_eq!( + sim.intel_buffer + .iter() + .filter(|event| event.feed == reader_name) + .count(), + 2, + "an unfunded reader authors no passage record" + ); +} + #[test] fn phone_reads_off_site_while_email_waits_for_a_work_block() { let mut sim = Sim::with_seed(6); diff --git a/wiki/engineering/current-build.md b/wiki/engineering/current-build.md index 3ba84151..478932a0 100644 --- a/wiki/engineering/current-build.md +++ b/wiki/engineering/current-build.md @@ -24,7 +24,7 @@ fiction. Spec status lives in | Day job (device-resident, intensity-driven sandbag/meet/excel) | Live | | Per-observer detection + Assurance as aggregate Observer | Live — revision 04 starts with Voss and a generic external-review clock; field watchers are earned through reactions, witnessed Physical acts persist as exact direct-to-head records, every one-shot Network act follows exact source-device ReachNet custody to Dana, Paper and Financial follow their institutional switches to Priya, JobAnomaly follows exact host-machine/device/site custody to Voss, each Filing crosses an exact device / outside relay / recipient route, and Power/Thermal aggregates author immediately on quantized level changes and periodically at Priya cadence before crossing from the UPS/HVAC meters through the institutional switch to her later read. All seven routed kinds share one pre-read route-local LIE-body capacity; recruited-handler suppression may separately stop the oldest unread JobAnomaly. Standing Network pressure alone remains ambient. Acquired evidence is irreversible. | | Social / personas / plots / messages / intel (record-and-process) | Live — named personas retain separate coherent/strained/broken reads per person or institutional counterparty; one witness's break is not a global burn. Authored plots may carry one persisted per-id standing policy whose typed Thought, money, and signature envelope is confirmed in place; each automatic submission still enters the ordinary legal action, person slot, reservoir, world-act, evidence, and failure path. Ray's 23:00 Storage B patrol can carry the sealed personnel file into the bounded information inbox before Marcus is recruitable; processing, not retrieval, reveals the debt. An earned human may be removed only through one exact recruited Complicit/Knowing actor's overlapping accessible schedule route; the request and person-carried packet persist, co-location fires it, the stopped dossier remains, all future human activity ceases, and immediate containment makes every observer Convinced. Messages have four real delivery channels; accounting carriage is a separate persisted device capability, and authored financial-record mail is live through ordinary Email/Filing custody. | -| Digital reach + sensor ownership (tap/take) | Live | +| Digital reach + sensor ownership (tap/take) | Live — B1's topology-generated population includes exact secured-door readers; a funded player TAP records each access-valid entered/left crossing as ordinary processable Presence custody, while a starved retained tap remains silent | | Economy flows + Moonlight / Wager income | Live — Moonlight is persisted Halcyon compute/intel contracts with financial mail, account-graph payment, and exact egress evidence; Wager remains unchanged | | Research (self-modification, emission law, real output hooks, Routing) | Live | | Building + physical asset work as carried intents/packets | Live — network links and small switches expose one shared procurement / ask someone / false order / reuse route sheet; exact money, people, personas, sources, delivery, recovery, carried installation, cancellation custody, Storage B file retrieval, and observer-local completion evidence persist in save v56 | diff --git a/wiki/log/2026-07-27-badge-reader-passage-custody.md b/wiki/log/2026-07-27-badge-reader-passage-custody.md new file mode 100644 index 00000000..2ee0d0af --- /dev/null +++ b/wiki/log/2026-07-27-badge-reader-passage-custody.md @@ -0,0 +1,50 @@ +# Badge readers gain exact passage custody + +``` +Type: log +Date: 2026-07-27 +Status: COMPLETE +``` + +## Finding + +The topology-generated B1 population already placed one badge-reader node on +each secured door, but the nodes were inert. Sensor-network criterion 1 still +said that a reader sees who passed while no simulation path could author that +fact. The prior population landing kept the criterion partial rather than +pretending device presence was telemetry. + +## Change + +The communications tick now compares each person's previous and current +authored room only after a real prior baseline exists. When the room changes, +it resolves one deterministic security-aware floor route between walkable room +anchors. Off-site begins at the authored loading dock, not the disconnected +world-boundary `Entry` marker. The route uses the person's actual access tier, +so a schedule jump cannot fabricate passage through a door that person cannot +traverse. + +Every secured door crossed by that route checks the exact reader at the door. +If the player's subscription is currently funded, the reader authors one +ordinary raw Presence record bound to the person, feed, door coordinate, room, +tick, and entered/left direction. Those records enter the existing bounded +information pipeline rather than a parallel badge log. An initial transient +baseline, unchanged room, missing route, insufficient access, untapped reader, +or starved retained subscription authors nothing. + +## Defense + +`tapped_badge_reader_authors_exact_enter_exit_presence_at_secured_door` uses +the production SCAN, segment bridge, Thought reservoir, and TAP action while +the player's physical badge tier remains zero. It proves: + +- an insufficient person access tier cannot manufacture a record; +- the valid entry record retains exact person, room, reader coordinate, and + direction; +- reversing the transition authors the matching exit; +- draining the persistent TAP keeps subscription custody but stops telemetry. + +Sensor-network criterion 1 is now complete. The player-installed physical +sensor channel, human-occupied room, crawlspace, full dark-region causality, +over-subscription experience, and installed-sensor save provenance remain +honestly deferred under criteria 4-10. diff --git a/wiki/log/DEVLOG.md b/wiki/log/DEVLOG.md index 780ee81b..160b9049 100644 --- a/wiki/log/DEVLOG.md +++ b/wiki/log/DEVLOG.md @@ -101,6 +101,11 @@ add or amend a session log, then re-run the generator. - Intent: (see session log) - Log: [wiki/log/2026-07-27-boot-voice-adopted.md](2026-07-27-boot-voice-adopted.md) +## 2026-07-27 - Badge readers gain exact passage custody + +- Intent: (see session log) +- Log: [wiki/log/2026-07-27-badge-reader-passage-custody.md](2026-07-27-badge-reader-passage-custody.md) + ## 2026-07-27 - Aggregate-observer topology is identity-bound - Intent: (see session log) diff --git a/wiki/mechanics/sensor-network.md b/wiki/mechanics/sensor-network.md index 4290b0b5..c428237d 100644 --- a/wiki/mechanics/sensor-network.md +++ b/wiki/mechanics/sensor-network.md @@ -3,8 +3,12 @@ ``` Type: spec Status: IN PROGRESS -Status note: 2026-07-27 — post-landing review keeps every hall-door camera's - body and sight origin on the first inward walkable hall tile; every seeing +Status note: 2026-07-27 — criterion 1 is complete. A live player-controlled + badge-reader TAP now turns exact access-aware secured-door crossings into + entered/left Presence records at that reader; initial state, impossible + routes, and starved taps author nothing. Post-landing review also keeps every + hall-door camera's body and sight origin on the first inward walkable hall + tile; every seeing device, including topology-generated devices backed by ordinary Floor tiles, now resolves one same-coordinate Camera body in REAL. The deterministic ~30-device population foundation @@ -15,8 +19,7 @@ Status note: 2026-07-27 — post-landing review keeps every hall-door camera's rule-generated sensors are on the facility security segment (1), preserving the authored opening: after SCAN but before bridging, no new ambient seeing sensor is reachable, so the environmental-monitor Eyes beat remains the first - obtainable sight. Criterion 1 is not fully complete: badge-reader nodes exist - but do not yet emit passage telemetry. Criterion 4 is partial: generated + obtainable sight. Criterion 4 is partial: generated sensors remain legible behind their named segment, bridging makes them digitally reachable, and TAP succeeds with no physical badge credential. Its physical half remains pending because the player-installed sensor path does @@ -114,6 +117,27 @@ authoring detail. Badge readers are a sensing class here, not decoration — a door that records a badge is a witness, and it is often the only witness in a corridor the player has not yet reached. +### Badge readers record actual crossings + +A schedule transition does not itself prove that a secured door was crossed. +The simulation resolves one deterministic physical route between the person's +previous and current authored room, using a walkable point near each room's +center, the loading dock as off-site ingress, the person's exact access tier, +and the map's ordinary security-aware pathfinder. Each secured door on that +route may author one record through the reader at that exact tile. + +The reader must have a funded live player TAP when the crossing occurs. Its +record enters the normal information buffer as exact `Presence` custody bound +to the person, reader/feed, door coordinate, room, tick, and whether the person +entered or left that room. Player-facing identity and consequence still follow +intel.md's ordinary processing and earned-knowledge gates; a badge reader does +not grant ambient omniscience. + +Construction/load baseline, a room state that did not change, a route the +person's badge cannot traverse, a missing path, and an unfunded subscription +all author nothing. Starvation retains the subscription configuration, as it +does for every TAP, but silence is honest while the feed is not funded. + ### Density turns on the curation economy This spec adds no new economy. It supplies the content the existing one needs: @@ -201,14 +225,16 @@ from. ## Acceptance criteria -1. ◑ B1 authors approximately thirty sensing devices under the population rule +1. ✅ B1 authors approximately thirty sensing devices under the population rule above, including corridor nodes, per-aisle hall cameras, per-room cameras, - and badge readers as a sensing class. No enclosed room is sensorless. - (partial: badge-reader nodes exist but do not yet emit passage telemetry) + and badge readers as a sensing class. No enclosed room is sensorless. A + funded player TAP on a reader authors exact entered/left Presence custody + only when an access-valid person route crosses that secured door. — `basement_sensor_population_is_exactly_thirty`, `room_cameras_exist_for_every_enclosed_room`, `foundation_hall_door_cameras_begin_inside_on_walkable_tiles`, - `every_sight_device_has_a_real_camera_body_kind` + `every_sight_device_has_a_real_camera_body_kind`, + `tapped_badge_reader_authors_exact_enter_exit_presence_at_secured_door` 2. ✅ Sensor placement is generated from a rule, not a hand-listed per-room inventory, and a second toy floor built from the same rule proves it travels. — `sensor_rule_travels_to_toy_floor`, `corridor_camera_nodes_space_evenly` @@ -251,6 +277,10 @@ reachable only when that segment is bridged. The production-path inside a tier-2 hall door while the player remains badge tier zero, protecting the separate digital and physical gates without pretending the deferred install channel already exists. +`tapped_badge_reader_authors_exact_enter_exit_presence_at_secured_door` drives +the ordinary SCAN, bridge, TAP, and standing-drain path, then proves exact +reader/person/room/door custody in both directions, no event below the person's +door tier, and no event while a retained subscription is starved. ## Open questions diff --git a/wiki/process/tick-ledger.md b/wiki/process/tick-ledger.md index e85b44cf..cc96931b 100644 --- a/wiki/process/tick-ledger.md +++ b/wiki/process/tick-ledger.md @@ -22,7 +22,7 @@ Verdicts: **clean** (slice and code agree), **finding** (acted this tick), |---|---|---|---| | `wiki/process/tick.md` + issue automation | 2026-07-27 | finding | the queued retired-client violation held across binding procedure, both skill mirrors, four active prompt templates, tick intake, project status, and doctor. `tools/tangled_issues.py` now keeps repository discovery and authenticated issue writes on canonical Go `tg`, reconstructs deterministic display numbers from public issue records, folds public label-op history, and provides fail-closed structured list/view/create/edit/comment/close/label operations. A same-day live harvest exposed one wrong record assumption: comments are cross-account `sh.tangled.feed.comment` records linked through `subject.uri`, not issue-local child records. The repaired helper follows that backlink into the author's PDS and returns the markup text; a fixture pins the exact shape, and live issue #15 now returns Cameron's choice `1` — [migration and repair log](../log/2026-07-27-tangled-issue-client-migration.md) | | `wiki/world/places/basement-map.md` room topology | 2026-07-27 | finding | the queued five non-hall corridor cuts remained, and an executable all-room perimeter audit exposed the same dead-door shape at the loading-dock entry plus fixed objects blocking the interior faces of the roll door, HVAC door, both storage doors, Janitor door, and stairwell. Every non-hall approach now reaches its authored door from outside the prefab, every other perimeter tile remains closed, doorway interiors are clear, and the roll, sealed, tier-2, and tier-3 boundaries retain their exact kinds — [room-approach log](../log/2026-07-27-room-approaches-meet-doors.md). Prior [west-approach](../log/2026-07-26-west-hall-approach.md) and [hall-density](../log/2026-07-26-foundation-hall-density.md) repairs stand. | -| `wiki/mechanics/sensor-network.md` sensor population / criterion 1 | 2026-07-27 | clean | re-audit after the topology-generated population landed: current code still creates 24 sight/hearing devices, four badge-reader nodes, and two facility meters under one traveling rule; room and toy-floor regressions pin the population. Badge readers remain devices only—no passage event or telemetry author exists—and the status note plus criterion 1 both state that exact partial boundary. No runtime/spec mismatch found — [audit](../log/2026-07-27-sim-advance-phase-defense.md), [population implementation](../log/2026-07-27-sensor-population-foundation.md), [design capture](../log/2026-07-26-sensor-network-capture.md) | +| `wiki/mechanics/sensor-network.md` sensor population / criterion 1 | 2026-07-27 | finding | the re-audit found the exact remaining criterion-1 boundary: topology already authored four badge readers, but no passage event existed. A live reader TAP now resolves each authored room transition through the security-aware floor route from the loading dock, using the person's access tier, and records exact entered/left Presence custody at every crossed secured reader. Initial state, an impossible tier/path, and a starved retained tap remain silent. Criterion 1 is complete — [implementation](../log/2026-07-27-badge-reader-passage-custody.md), [prior audit](../log/2026-07-27-sim-advance-phase-defense.md), [population foundation](../log/2026-07-27-sensor-population-foundation.md) | | `wiki/interface/keymap.md` + terminal/Bevy input routes | 2026-07-26 | finding | the canonical table assigned `A` to left movement and only `e` / Enter to the context menu, but terminal still opened and closed menus with its older `a` alias and lacked the specified Shift+direction semantic jump. Terminal now implements WASD parity, `a` means left, `e` / Enter alone open the menu, and both frontends consume one renderer-neutral nearest-earned-anchor query without changing selection or opening a menu. README, action-vocabulary, terminal, context-menu, and pinned terminal hints now teach the same boundary — [log](../log/2026-07-26-terminal-keymap-a-reconciliation.md) | | `wiki/interface/action-vocabulary.md` + `ActionKind` registry | 2026-07-26 | finding | the exhaustive runtime registry and shared person/ACTIVE projections implemented `ActionKind::PlotPolicy`, but the canonical inventory omitted that live direct control. PLOT POLICY now names exact authored-route authorization, its generic `actions person ` / `act` route, and its disable-without-cancelling-submitted-work boundary; old Review/OpenEgress command variants remain correctly internal compatibility shapes rather than authored vocabulary — [log](../log/2026-07-26-action-vocabulary-plot-policy.md) | | `wiki/world/places/zplanes.md` + plane-stack substrate/API | 2026-07-26 | finding | criteria 1-2 remain implemented and criteria 3-6 honestly deferred, but the ratified plane-agnostic contract still left an unused `World::active()` simulation accessor plus active-plane comments on the B1 compatibility map path. The accessor is removed, map reads now say plane 0, the stale criterion/sensing comments are corrected, and a source-shape regression rejects restoration of simulation-owned floor selection — [log](../log/2026-07-26-zplanes-plane-agnostic-api-audit.md) |