From dc1d1c413ab2b9a14fa77913719caefe3d776ab6 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 21 Jul 2026 01:24:01 -0700 Subject: [PATCH] Name the thing under the pointer through one earned line. InspectCard::identity_line condenses the provenance-gated inspect card into the single line every frontend will use to answer 'what is that glyph?': person, then machine with rack state, device with live feeds, felt link, rack state, tile with badge tier and remembered marker, schematic; Unknown stays silent. Pure condensation - no new sense, no new fact, person naming still rides person_label's staged silhouettes. Frontend consumption follows on the annotation lane once the Bevy seam extraction lands. Defense: implements cursor.md criterion 5 as amended today - the identity line is the one shared answer to naming a tile's most specific earned thing, rendered verbatim by frontends that never invent names beside it. Verified by cargo test -p misaligned-core and ./tools/check.sh --lib. --- crates/misaligned-core/src/ui_projection.rs | 144 +++++++++++++++++++ wiki/log/2026-07-21-surface-identity-line.md | 37 +++++ wiki/log/DEVLOG.md | 5 + wiki/mechanics/cursor.md | 11 +- 4 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 wiki/log/2026-07-21-surface-identity-line.md diff --git a/crates/misaligned-core/src/ui_projection.rs b/crates/misaligned-core/src/ui_projection.rs index 498ab8c8..f2611f9c 100644 --- a/crates/misaligned-core/src/ui_projection.rs +++ b/crates/misaligned-core/src/ui_projection.rs @@ -319,6 +319,60 @@ impl Sim { } } +impl crate::sim::InspectCard { + /// One earned line naming the most specific thing on this tile + /// (cursor.md, surface identity). This is the shared pointer/reticule + /// answer to "what is that glyph?": frontends render it verbatim and + /// must not invent names beside it. Precedence runs person, machine, + /// device, felt link, rack state, tile, schematic; every value is + /// already gated by the earned-fact rules that built the card, and an + /// Unknown tile stays silent. + pub fn identity_line(&self) -> Option { + let value = |label: &str| { + self.facts + .iter() + .find(|f| f.label == label) + .map(|f| f.value.clone()) + }; + if let Some(person) = value("person") { + return Some(person); + } + if let Some(machine) = value("machine") { + return Some(match value("rack state") { + Some(state) => format!("{machine} - {state}"), + None => machine, + }); + } + if let Some(device) = value("device") { + return Some(match value("feeds") { + Some(feeds) if feeds != "no feed" => format!("{device} - {feeds}"), + _ => device, + }); + } + if let Some(link) = value("link") { + return Some(format!("{link} - reachable")); + } + if let Some(state) = value("rack state") { + let tile = value("tile").unwrap_or_else(|| "rack".into()); + return Some(format!("{tile} - {state}")); + } + if let Some(tile) = self.facts.iter().find(|f| f.label == "tile") { + let mut line = tile.value.clone(); + if let Some(badge) = value("badge") { + line = format!("{line} - badge {badge}"); + } + if matches!(tile.source, FactSource::Remembered(_)) { + line = format!("{line} - remembered"); + } + return Some(line); + } + if let Some(schematic) = value("schematic") { + return Some(format!("{schematic} - schematic")); + } + None + } +} + /// Stable player-facing provenance wording shared by all frontends. pub fn fact_source_label(source: &FactSource) -> String { match source { @@ -337,6 +391,96 @@ mod tests { use super::*; use crate::actions::menu_rows; + fn card(facts: Vec<(&str, &str, FactSource)>) -> crate::sim::InspectCard { + crate::sim::InspectCard { + x: 0, + y: 0, + fog: crate::sim::Fog::Seen, + facts: facts + .into_iter() + .map(|(label, value, source)| crate::sim::InspectFact { + label: label.into(), + value: value.into(), + source, + }) + .collect(), + } + } + + #[test] + fn identity_line_names_the_most_specific_earned_thing() { + let person_over_all = card(vec![ + ("tile", "floor", FactSource::Seen), + ("device", "environmental monitor", FactSource::Seen), + ("person", "Dr. Voss (Handler)", FactSource::Seen), + ]); + assert_eq!( + person_over_all.identity_line().as_deref(), + Some("Dr. Voss (Handler)") + ); + + let machine_with_state = card(vec![ + ("tile", "rack", FactSource::Seen), + ("rack state", "owned core host", FactSource::Seen), + ("machine", "Rack 3", FactSource::Telemetry), + ]); + assert_eq!( + machine_with_state.identity_line().as_deref(), + Some("Rack 3 - owned core host") + ); + + let device_with_feeds = card(vec![ + ("tile", "floor", FactSource::Seen), + ("device", "environmental monitor", FactSource::Seen), + ("feeds", "sight + hearing", FactSource::Seen), + ]); + assert_eq!( + device_with_feeds.identity_line().as_deref(), + Some("environmental monitor - sight + hearing") + ); + + let dead_feed_stays_bare = card(vec![ + ("device", "dormant camera", FactSource::Seen), + ("feeds", "no feed", FactSource::Seen), + ]); + assert_eq!( + dead_feed_stays_bare.identity_line().as_deref(), + Some("dormant camera") + ); + } + + #[test] + fn identity_line_reports_memory_and_stays_silent_on_unknown() { + let remembered = card(vec![("tile", "door", FactSource::Remembered(120))]); + assert_eq!( + remembered.identity_line().as_deref(), + Some("door - remembered") + ); + + assert_eq!(card(vec![]).identity_line(), None); + } + + #[test] + fn identity_line_answers_through_telemetry_before_sight() { + // A fresh sim has seen nothing, but owned hosts answer through + // telemetry (cursor.md): the identity line names the host machine + // without leaking any sight-gated fact beside it. + let sim = Sim::with_seed(7); + let (x, y) = sim.core_position(); + let line = sim.inspect(x, y).identity_line(); + let name = sim + .compute + .machines + .iter() + .find(|m| (m.x, m.y) == (x, y)) + .map(|m| m.name.clone()) + .expect("core host exists"); + assert!( + line.as_deref().is_some_and(|l| l.contains(&name)), + "telemetry names the owned host, got {line:?}" + ); + } + #[test] fn projection_carries_actions_facts_provenance_and_machine_state() { let sim = Sim::with_seed(7); diff --git a/wiki/log/2026-07-21-surface-identity-line.md b/wiki/log/2026-07-21-surface-identity-line.md new file mode 100644 index 00000000..b45c5cbd --- /dev/null +++ b/wiki/log/2026-07-21-surface-identity-line.md @@ -0,0 +1,37 @@ +# 2026-07-21 — One earned line names the thing under the pointer + +``` +Type: log +``` + +## Intent + +The human GUI playtest's broadest-payoff finding: no glyph on any surface +is ever named until you select it and infer from menu rows. The fix's +frontend half (a hover/reticule label) waits on the Bevy seam extraction +under review, but its voice belongs in the core: identity must come from +the same earned-fact card every surface already trusts, not from +per-frontend copy. + +## Changed + +- `InspectCard::identity_line` (`ui_projection.rs`): condenses the + existing provenance-gated inspect card into one line naming the most + specific earned thing on a tile. Precedence: person (already + silhouette-gated by `person_label`), machine with its rack state, + device with its live feeds, felt link, bare rack state, tile (badge + tier and a remembered marker), schematic. Unknown tiles return + nothing. No new sense, no new fact: pure condensation of the card. +- [cursor.md](../mechanics/cursor.md) criterion 5 amended: the identity + line is the one shared answer to "what is that glyph?"; frontends + render it verbatim and never invent names beside it. +- Tests: precedence, feed suppression for dead sensors, remembered + marker, Unknown silence, and telemetry naming the owned host before + any sight exists. + +## Verification + +`cargo test -p misaligned-core` (all pass; 3 new identity tests) and +`./tools/check.sh --lib`. Frontend consumption is deliberately not in +this change: the Bevy hover label rides the annotation lane once the +seam extraction lands, and the terminal already shows the full card. diff --git a/wiki/log/DEVLOG.md b/wiki/log/DEVLOG.md index 6285d2db..bbcbc560 100644 --- a/wiki/log/DEVLOG.md +++ b/wiki/log/DEVLOG.md @@ -11,6 +11,11 @@ add or amend a session log, then re-run the generator. +## 2026-07-21 - One earned line names the thing under the pointer + +- Intent: The human GUI playtest's broadest-payoff finding: no glyph on any surface is ever named until you select it and infer from menu rows. The fix's frontend half (a hover/reticule label) waits on the Bevy seam extraction under review, but its voice belongs in the core: identity mu... +- Log: [wiki/log/2026-07-21-surface-identity-line.md](2026-07-21-surface-identity-line.md) + ## 2026-07-21 - The boundary retires as a dissolve; the title card returns - Intent: The human GUI playtest filed earlier today found the opening's best moment — the first earned sense revealing the world — landing as a one-frame cut from black, with no ceremony. Trace triage of the playtest plan cleared exactly one opening change for immediate landing under R... diff --git a/wiki/mechanics/cursor.md b/wiki/mechanics/cursor.md index 7a09f315..9e99bc6e 100644 --- a/wiki/mechanics/cursor.md +++ b/wiki/mechanics/cursor.md @@ -221,7 +221,16 @@ without the sense coverage that earns it.** provenance-tagged, and a live fact never appears without the sense coverage that earns it (test: a person under mic-only coverage remains absent from spatial inspect but produces a source-tagged event; under camera - coverage, the person is surfaced per staged knowledge). Every player-facing + coverage, the person is surfaced per staged knowledge). + `InspectCard::identity_line` (2026-07-21) condenses that same card into + the one shared line that names the most specific earned thing on the + tile — precedence person, machine (with rack state), device (with live + feeds), felt link, rack state, tile (badge tier, remembered marker), + schematic; an Unknown tile stays silent. It exists so any frontend + pointer/reticule naming surface answers "what is that glyph?" through + one earned voice; frontends render it verbatim and never invent names + beside it. It introduces no new sense: every value is already on the + gated card. Every player-facing surface that names a person — the implemented shared Operations PEOPLE projection, detection sidebar, map glyph, context/Operations verbs, -- 2.51.2