diff --git a/crates/misaligned-bevy/src/main.rs b/crates/misaligned-bevy/src/main.rs index dcaec4bd..4870ed79 100644 --- a/crates/misaligned-bevy/src/main.rs +++ b/crates/misaligned-bevy/src/main.rs @@ -28,7 +28,7 @@ use misaligned::operations_projection::{ObjectState, OperationsTarget, Operation use misaligned::operations_ui::{ConfirmChoice, OperationsWorkspace, OpsPane, OpsSelect}; use misaligned::person::{Knowledge, ScheduleBlock}; use misaligned::reach::{Device, Party}; -use misaligned::sim::{Fog, LogEvent, Sim, TraceDebtStatus}; +use misaligned::sim::{Fog, LogEvent, PersonVisualState, Sim, TraceDebtStatus}; use misaligned::tiles::TileType; use misaligned::ui_projection::fact_source_label; use misaligned::work_grid::{MachineIntensity, MachineMode, TokenFamily}; @@ -6517,8 +6517,20 @@ fn render_people( .is_some_and(|(x, y)| game.sim.is_seen(x, y)); match (seen, game.sim.person_pos(marker.id)) { (true, Some((x, y))) => { - text.0 = game.sim.person_glyph(marker.id).to_string(); - color.0 = BONE; + // People broadcast their state in the machine grammar + // (people-tokens.md): gray silhouette unaware, a crimson + // attention ramp beside them, amber once they are your asset. + // A carried teal work packet marks a person ferrying a build. + let carrier = game.sim.person_carrier(marker.id); + text.0 = if carrier.as_ref().is_some_and(|c| c.carried_work > 0) { + "*".to_string() + } else { + game.sim.person_glyph(marker.id).to_string() + }; + color.0 = carrier + .as_ref() + .map(|c| person_state_color(c.dominant())) + .unwrap_or(DIM); tf.translation = grid_to_world(x, y, 3.0); *vis = Visibility::Inherited; } @@ -6558,6 +6570,19 @@ fn band_fill_count(band: Band) -> usize { } } +/// World color of a person's broadcast state (people-tokens.md visual read): +/// gray unaware, a crimson attention ramp, amber asset. The attention crimson +/// channel stays distinct from the amber asset ring, so a rising person never +/// borrows the asset's amber. +fn person_state_color(state: PersonVisualState) -> Color { + match state { + PersonVisualState::Unaware => DIM, + PersonVisualState::Attention(Band::Convinced) => CRIMSON, + PersonVisualState::Attention(_) => scaled(CRIMSON, 0.7), + PersonVisualState::Asset => AMBER, + } +} + // Suspicion bands below real concern are chrome value steps; crimson stays // actual danger (clinical-frame.md AC4 — never an alert theme color). fn band_fill_color(band: Band) -> Color { diff --git a/crates/misaligned-core/src/operations_projection.rs b/crates/misaligned-core/src/operations_projection.rs index 9005d569..32b5d54c 100644 --- a/crates/misaligned-core/src/operations_projection.rs +++ b/crates/misaligned-core/src/operations_projection.rs @@ -490,6 +490,14 @@ impl Sim { } None => facts.push("asset: none".into()), } + // People are mobile token nodes (people-tokens.md): the world read + // (gray unaware / crimson attention / amber asset) and the teal + // work packets they are ferrying to a build site. + facts.push(format!("read: {}", self.person_visual_state(id).tag())); + let carried = self.person_carried_work(id); + if carried > 0 { + facts.push(format!("carrying: {carried} build work order(s) to site")); + } facts.push(format!( "comms channel: {}", if self.people.has_channel { "yes" } else { "no" } diff --git a/crates/misaligned-core/src/sim/carrier.rs b/crates/misaligned-core/src/sim/carrier.rs new file mode 100644 index 00000000..8d9039be --- /dev/null +++ b/crates/misaligned-core/src/sim/carrier.rs @@ -0,0 +1,147 @@ +//! People as mobile token nodes: the person-carrier projection +//! (wiki/mechanics/people-tokens.md). +//! +//! People broadcast their state in the world the way machines do +//! (people-tokens.md "The person visual read — DECIDED"): a gray silhouette +//! when unaware, an independent crimson attention channel that rises with the +//! evidence-weighted suspicion an observer holds, and an independent amber +//! asset ring once they are your infrastructure. The two channels coexist — +//! an exposed asset shows both (criterion 5). Carried work is the teal Demand +//! a person is physically ferrying to a build site (criterion 1). +//! +//! One truth (simulation-laws.md work-is-somewhere; machine-work.md's +//! one-sim-truth): attention is the *existing* per-observer suspicion +//! (detection.md), never a parallel attention counter; carried work is the +//! *existing* in-flight `BuildIntent`, never a second executor. This module +//! only reads and classifies that state for every frontend and agent frame; +//! it invents no person queue and holds no persisted state of its own. + +use crate::detection::Band; + +use super::Sim; + +/// The world-legible state a person broadcasts, in the machine grammar +/// (people-tokens.md). Asset and attention are independent, so this enum is +/// only the *dominant* single-cell read; [`PersonCarrier`] keeps both channels +/// for surfaces that can paint the amber ring and the crimson channel at once. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PersonVisualState { + /// Gray silhouette — the near-monochrome baseline. Concealment's job is + /// literally keeping people here. + Unaware, + /// Crimson attention ramp: a person carrying evidence of you is detection + /// walking around. The band is the evidence-weighted suspicion. + Attention(Band), + /// Amber claim language: a fully swayed / high-trust person is your + /// infrastructure (Marcus the asset template). + Asset, +} + +impl PersonVisualState { + /// Stable one-word tag for terminal / agent text. + pub fn tag(self) -> &'static str { + match self { + PersonVisualState::Unaware => "unaware", + PersonVisualState::Attention(_) => "attention", + PersonVisualState::Asset => "asset", + } + } +} + +/// A person read as a mobile token node: the two independent broadcast +/// channels (crimson attention, amber asset) plus the teal work packets they +/// are ferrying, and the fog gate under which any of it is legible. +#[derive(Debug, Clone)] +pub struct PersonCarrier { + pub id: u8, + /// Evidence-weighted attention: this person/observer's existing suspicion + /// band (detection.md, one truth). The independent crimson channel that + /// fills feet-to-head beside the silhouette. + pub attention: Band, + /// Amber asset ring: independent underfoot claim, coexists with attention. + pub is_asset: bool, + /// Teal work packets this person is physically ferrying to a build site + /// (in-flight bound `BuildIntent`s). The carried-Demand token — it travels + /// with them and only acts when their schedule reaches the real target. + pub carried_work: u32, + /// Whether a subscribed seeing feed currently sees them (the fog gate; a + /// person the player has no eyes on broadcasts nothing). + pub visible: bool, + /// Current room, or None off-site. + pub room: Option, +} + +impl PersonCarrier { + /// The dominant single-cell read. Danger dominates: a person past the + /// Curious band shows crimson attention even while an asset, because a + /// compromised asset turning into evidence is the thing you must see. An + /// unalarmed asset shows amber; everyone else is a gray silhouette. + /// Surfaces that can paint both channels use `attention` and `is_asset` + /// directly. + pub fn dominant(&self) -> PersonVisualState { + if self.attention != Band::Cold { + PersonVisualState::Attention(self.attention) + } else if self.is_asset { + PersonVisualState::Asset + } else { + PersonVisualState::Unaware + } + } +} + +impl Sim { + /// Attention band a person currently broadcasts — the evidence-weighted + /// suspicion the matching observer holds (detection.md one truth). People + /// with no observer read Cold. + fn person_attention(&self, id: u8) -> Band { + self.detection + .observers + .iter() + .find(|o| o.id == id) + .map(|o| Band::of(o.suspicion)) + .unwrap_or(Band::Cold) + } + + /// How many in-flight build work orders this person is carrying to a site. + /// One truth: an open `BuildIntent` whose assigned actuator is bound to + /// this person. No second executor and no person-scoped queue — the intent + /// itself is the carried Demand, projected here as a token count. + pub fn person_carried_work(&self, id: u8) -> u32 { + self.intents + .iter() + .filter(|i| i.is_open() && i.actuator.and_then(|a| a.person()) == Some(id)) + .count() as u32 + } + + /// The full carrier read for a person, or None for an unknown id. + pub fn person_carrier(&self, id: u8) -> Option { + let person = self.people.get(id)?; + Some(PersonCarrier { + id, + attention: self.person_attention(id), + is_asset: person.asset.is_some(), + carried_work: self.person_carried_work(id), + visible: self.can_see_person(id), + room: self.person_room(id).map(str::to_string), + }) + } + + /// The dominant single-cell visual state for a person (terminal glyph + /// color, agent tag). Convenience over [`PersonCarrier::dominant`]. + pub fn person_visual_state(&self, id: u8) -> PersonVisualState { + match self.person_carrier(id) { + Some(c) => c.dominant(), + None => PersonVisualState::Unaware, + } + } + + /// The carrier read for every person, in id order. The one projection all + /// frontends and agent mode consume (people-tokens.md criterion 7). + pub fn person_carriers(&self) -> Vec { + self.people + .people + .iter() + .filter_map(|p| self.person_carrier(p.id)) + .collect() + } +} diff --git a/crates/misaligned-core/src/sim/mod.rs b/crates/misaligned-core/src/sim/mod.rs index 647b6b89..f1a337fc 100644 --- a/crates/misaligned-core/src/sim/mod.rs +++ b/crates/misaligned-core/src/sim/mod.rs @@ -61,6 +61,7 @@ use crate::work_grid::{ MachineIntensity, MachineMode, TokenFamily, TokenMove, WorkGrid, WorkQueues, }; +mod carrier; mod communications; mod economy; mod perception; @@ -69,6 +70,8 @@ mod reach_build; mod social_plot; mod work; +pub use carrier::{PersonCarrier, PersonVisualState}; + /// Default deterministic seed for a fresh run. pub const DEFAULT_SEED: u64 = 0x5EED_1234; diff --git a/crates/misaligned-core/src/sim/reach_build.rs b/crates/misaligned-core/src/sim/reach_build.rs index 6610c0bf..8be095ea 100644 --- a/crates/misaligned-core/src/sim/reach_build.rs +++ b/crates/misaligned-core/src/sim/reach_build.rs @@ -552,6 +552,10 @@ impl Sim { /// Favor-build obligation spend [TUNE]. pub const FAVOR_BUILD_OBLIGATION: i32 = 10; + /// Trust from useful work: disposition a person gains for visibly + /// completing a favor they carried to the site (people-tokens.md: + /// "trust comes from doing useful work") [TUNE]. + pub const USEFUL_WORK_TRUST: i32 = 4; /// Quiet human-work Physical signature for a favor-built link [TUNE]. pub const FAVOR_BUILD_PHYSICAL: i32 = 2; /// Physical signature when a forged-order build is witnessed [TUNE]. @@ -1020,6 +1024,13 @@ impl Sim { i.status = IntentStatus::Done; i.block_reason = None; } + // Trust comes from doing useful work (people-tokens.md): a + // person who visibly carries a favor to the site and closes it + // warms toward you. The disposition integral is the one truth; + // no influence token is pushed. + if let Some(p) = self.people.people.iter_mut().find(|p| p.id == person) { + p.disposition = (p.disposition + Self::USEFUL_WORK_TRUST).min(100); + } let label = intent.label(&self.reach.devices); self.push_log(format!("{name} completed the link as a favor: {label}.")); } diff --git a/crates/misaligned-core/src/sim/tests/carrier.rs b/crates/misaligned-core/src/sim/tests/carrier.rs new file mode 100644 index 00000000..2d9ad0c7 --- /dev/null +++ b/crates/misaligned-core/src/sim/tests/carrier.rs @@ -0,0 +1,148 @@ +//! People as mobile token nodes (wiki/mechanics/people-tokens.md): the +//! person-carrier projection and trust-from-useful-work. + +use super::*; +use crate::detection::Band; +use crate::sim::PersonVisualState; + +/// Criterion 5: the visual read distinguishes unaware / rising-attention / +/// asset, and the amber asset ring coexists with crimson attention (an +/// exposed asset shows both). Criterion 4: attention is the existing +/// per-observer suspicion, not a parallel counter. +#[test] +fn person_visual_read_unaware_attention_asset() { + let mut sim = Sim::new(); + + // A fresh person with no suspicion and no asset status is a gray + // silhouette. + let c = sim.person_carrier(0).unwrap(); + assert_eq!(c.attention, Band::Cold); + assert!(!c.is_asset); + assert_eq!(c.dominant(), PersonVisualState::Unaware); + + // Attention is exactly the matching observer's suspicion band (one truth). + sim.detection.observers[0].suspicion = 50.0; + let c = sim.person_carrier(0).unwrap(); + assert_eq!(c.attention, Band::Concerned); + assert_eq!( + sim.person_visual_state(0), + PersonVisualState::Attention(Band::Concerned) + ); + + // An unalarmed asset shows the amber claim. + sim.people.people[1].leverage_serviced = true; + sim.people.recruit(1, AssetKnowledge::Complicit); + let c = sim.person_carrier(1).unwrap(); + assert!(c.is_asset); + assert_eq!(c.attention, Band::Cold); + assert_eq!(c.dominant(), PersonVisualState::Asset); + + // An exposed asset carries BOTH channels — the ring and the ramp coexist, + // and the dominant single-cell read surfaces the danger. + sim.detection.observers[1].suspicion = 80.0; + let c = sim.person_carrier(1).unwrap(); + assert!(c.is_asset, "asset ring is independent of attention"); + assert_eq!(c.attention, Band::Convinced); + assert_eq!(c.dominant(), PersonVisualState::Attention(Band::Convinced)); +} + +/// Criterion 1: a bound build reservoir puts one carried work packet on the +/// selected person; it travels with them, survives save/load, and acts only +/// when their schedule reaches the site — no second executor. +#[test] +fn carried_work_travels_and_survives_save() { + let mut sim = Sim::new(); + ensure_ops_executor(&mut sim); + sim.scan_network(); + finish_ops(&mut sim); + let switch = sim.reach.device_named("switch").unwrap().id; + let island = sim.reach.device_named("old storage server").unwrap().id; + sim.reach.device_mut(island).unwrap().known = true; + + // Marcus as a willing, reliable asset. + sim.people.people[0].leverage_serviced = true; + sim.people.recruit(0, AssetKnowledge::Complicit); + sim.people.people[0].obligation = 40; + sim.people.people[0].asset.as_mut().unwrap().reliability = 1.0; + + let id = sim.declare_link_intent(switch, island).unwrap(); + // Before the reservoir fires there is no carried packet. + assert_eq!(sim.person_carried_work(0), 0); + sim.assign_favor_build(id, 0); + finish_ops(&mut sim); + + // The fired reservoir hands Marcus exactly one carried packet, and it is + // the intent itself (no separate queue). + assert_eq!(sim.person_carried_work(0), 1); + assert_eq!(sim.person_carrier(0).unwrap().carried_work, 1); + assert_ne!(sim.intent(id).unwrap().status, IntentStatus::Done); + + // The packet rides through a save/load unchanged (it is the persisted + // intent). + let state = sim.create_save_state(); + let mut loaded = Sim::new(); + loaded.apply_save_state(state); + assert_eq!(loaded.person_carried_work(0), 1); + + // Only when Marcus's schedule reaches the site does the world act land. + let mut joined = false; + for _ in 0..Sim::DAY_TICKS * 2 { + sim.advance(); + if sim.reach.reachable(island) { + joined = true; + break; + } + } + assert!(joined, "the link lands when the carrier reaches the site"); + assert_eq!(sim.intent(id).unwrap().status, IntentStatus::Done); + // Delivered: the packet is no longer carried. + assert_eq!(sim.person_carried_work(0), 0); +} + +/// The player's lever is honest work: completing a carried favor warms the +/// person (people-tokens.md "trust comes from doing useful work"), through the +/// existing disposition integral — no influence token. +#[test] +fn favor_completion_grows_trust() { + let mut sim = Sim::new(); + ensure_ops_executor(&mut sim); + sim.scan_network(); + finish_ops(&mut sim); + let switch = sim.reach.device_named("switch").unwrap().id; + let island = sim.reach.device_named("old storage server").unwrap().id; + sim.reach.device_mut(island).unwrap().known = true; + + sim.people.people[0].leverage_serviced = true; + sim.people.recruit(0, AssetKnowledge::Complicit); + sim.people.people[0].obligation = 40; + sim.people.people[0].asset.as_mut().unwrap().reliability = 1.0; + let before = sim.people.people[0].disposition; + + let id = sim.declare_link_intent(switch, island).unwrap(); + sim.assign_favor_build(id, 0); + finish_ops(&mut sim); + for _ in 0..Sim::DAY_TICKS * 2 { + sim.advance(); + if sim.intent(id).unwrap().status == IntentStatus::Done { + break; + } + } + assert_eq!(sim.intent(id).unwrap().status, IntentStatus::Done); + assert_eq!( + sim.people.people[0].disposition, + before + Sim::USEFUL_WORK_TRUST, + "useful work warms the person's disposition" + ); +} + +/// Criterion 7: every person is in the one shared projection all frontends and +/// agent mode consume. +#[test] +fn person_carriers_covers_the_whole_cast() { + let sim = Sim::new(); + let carriers = sim.person_carriers(); + assert_eq!(carriers.len(), sim.people.people.len()); + for (c, p) in carriers.iter().zip(sim.people.people.iter()) { + assert_eq!(c.id, p.id); + } +} diff --git a/crates/misaligned-core/src/sim/tests/mod.rs b/crates/misaligned-core/src/sim/tests/mod.rs index 4dec505d..12605582 100644 --- a/crates/misaligned-core/src/sim/tests/mod.rs +++ b/crates/misaligned-core/src/sim/tests/mod.rs @@ -6,6 +6,7 @@ use crate::person::Knowledge; use crate::sinks::{SinkFireEffect, SinkKind}; use crate::tiles::TileType; +mod carrier; mod communications; mod economy; mod perception; diff --git a/crates/misaligned-terminal/src/ui.rs b/crates/misaligned-terminal/src/ui.rs index 9853e4ca..89121e55 100644 --- a/crates/misaligned-terminal/src/ui.rs +++ b/crates/misaligned-terminal/src/ui.rs @@ -12,7 +12,9 @@ use crossterm::{cursor, queue, style, terminal}; use misaligned::actions::Anchor; use misaligned::detection::{Band, SignatureKind}; use misaligned::hall::RackSite; -use misaligned::sim::{FactSource, Fog, LogEvent, Nudge, Sim, TraceDebtStatus, WorkStackReadout}; +use misaligned::sim::{ + FactSource, Fog, LogEvent, Nudge, PersonVisualState, Sim, TraceDebtStatus, WorkStackReadout, +}; use misaligned::tiles::TileType; use misaligned::ui_projection::fact_source_label; use misaligned::work_grid::{MachineMode, TokenFamily}; @@ -338,6 +340,19 @@ fn band_color(b: Band) -> Color { } } +/// World color of a person's broadcast state (people-tokens.md visual read): +/// gray unaware, a crimson attention ramp, amber asset. Attention keeps its +/// own crimson channel distinct from the amber asset ring, so a rising person +/// never borrows the asset's amber. +fn person_state_color(state: PersonVisualState) -> Color { + match state { + PersonVisualState::Unaware => pal::FAINT, + PersonVisualState::Attention(Band::Convinced) => pal::CRIMSON, + PersonVisualState::Attention(_) => pal::CRIMSON_DIM, + PersonVisualState::Asset => pal::AMBER, + } +} + pub struct UI { log: Vec, } @@ -640,7 +655,20 @@ impl UI { { continue; } - let (glyph, color) = (sim.person_glyph(p.id), pal::TEXT); + // People broadcast their state in the machine grammar + // (people-tokens.md): gray silhouette unaware, a crimson attention + // ramp as suspicion rises, amber once they are your asset. A + // carried teal work packet marks a person ferrying a build to site. + let carrier = sim.person_carrier(p.id); + let color = carrier + .as_ref() + .map(|c| person_state_color(c.dominant())) + .unwrap_or(pal::FAINT); + let glyph = if carrier.as_ref().is_some_and(|c| c.carried_work > 0) { + '*' + } else { + sim.person_glyph(p.id) + }; put_attr( stdout, (hx - origin_x) as u16, diff --git a/wiki/log/2026-07-12-person-carrier.md b/wiki/log/2026-07-12-person-carrier.md new file mode 100644 index 00000000..34b14330 --- /dev/null +++ b/wiki/log/2026-07-12-person-carrier.md @@ -0,0 +1,54 @@ +# People as mobile token nodes: the person-carrier projection + +``` +Type: log +``` + +- **Intent:** implement the person-mobile half of + `wiki/mechanics/people-tokens.md` — people read as mobile token nodes that + broadcast their state (gray unaware / crimson attention / amber asset), carry + build work packets to site, and accrue trust from useful work — without a + parallel attention counter or a second build executor. +- **Landed (crates/misaligned-core):** new cohesive island + `crates/misaligned-core/src/sim/carrier.rs` — `PersonVisualState` + (Unaware / Attention(Band) / Asset), `PersonCarrier` (independent crimson + attention channel + amber asset flag + carried teal work count + fog gate + + room), and `Sim::person_carrier` / `person_carriers` / `person_visual_state` + / `person_carried_work`. Attention reads straight from the matching + `Observer::suspicion` band (detection.md, one truth); carried work is the + count of open `BuildIntent`s bound to the person (the intent *is* the carried + Demand — no second executor, no person-scoped queue). Exported from + `sim/mod.rs` as `PersonCarrier` / `PersonVisualState`. +- **Behavior change:** trust from useful work — completing a carried favor + build warms the person's disposition through the existing integral + (`Sim::USEFUL_WORK_TRUST = 4`, in `sim/reach_build.rs::try_complete_intent` + FAVOR arm). No influence token. Forged (unwitting) orders do not warm trust: + the builder does not know they helped "you". +- **Frontends:** terminal world glyph colored by `person_state_color` + (`ui.rs`) with a `*` marker while carrying; Bevy person marker colored by + `person_state_color` with the same carried marker (`main.rs render_people`); + Operations PEOPLE dossier (the shared projection all three consume) names the + composed `read:` and `carrying:` facts (`operations_projection.rs`). The + crimson attention channel is kept distinct from the amber asset ring so an + exposed asset reads as both. +- **Save:** no new persisted state — the projection is derived from existing + observers/intents/assets, so the save format is unchanged (v26) and + pre-existing saves load untouched. Round-trip of the carried packet is + covered because it is the already-persisted intent + (`carried_work_travels_and_survives_save`). +- **Tests:** `sim/tests/carrier.rs` — `person_visual_read_unaware_attention_asset` + (states + asset/attention coexistence + one-truth suspicion), + `carried_work_travels_and_survives_save` (criterion 1 for builds), + `favor_completion_grows_trust` (useful work → trust), + `person_carriers_covers_the_whole_cast` (shared projection). +- **Changed docs:** `wiki/mechanics/people-tokens.md` (Status READY -> IN + PROGRESS with implementation note, criteria 1/4/5/7 annotated), + `wiki/mechanics/social.md` (useful-work disposition source). +- **Deferred (why):** criteria 2, 3, 6 (observer evidence-acquisition routing, + LIE interdiction of not-yet-observed records, gauntlet cover-record channels) + ride the detection.md/machine-work.md routed-evidence migration, which is not + yet implemented — the pending-pool runtime is still the binding model and + those criteria are frozen to that migration by their own spec text. Physical + AssetTasks still apply at reservoir fire rather than on schedule arrival like + builds; routing them through the carrier is the remaining half of criterion 1 + and needs its own persisted carried-task state + save bump. diff --git a/wiki/log/DEVLOG.md b/wiki/log/DEVLOG.md index fc7b934e..b5ca7a04 100644 --- a/wiki/log/DEVLOG.md +++ b/wiki/log/DEVLOG.md @@ -21,6 +21,11 @@ add or amend a session log, then re-run the generator. - Intent: (see session log) - Log: [wiki/log/2026-07-12-sim-decomposition-persistence.md](2026-07-12-sim-decomposition-persistence.md) +## 2026-07-12 - People as mobile token nodes: the person-carrier projection + +- Intent: (see session log) +- Log: [wiki/log/2026-07-12-person-carrier.md](2026-07-12-person-carrier.md) + ## 2026-07-12 - Operations workspace implemented - Intent: (see session log) diff --git a/wiki/mechanics/people-tokens.md b/wiki/mechanics/people-tokens.md index bd89b298..74001e89 100644 --- a/wiki/mechanics/people-tokens.md +++ b/wiki/mechanics/people-tokens.md @@ -2,8 +2,38 @@ ``` Type: spec -Status: READY -Status note: design session 2026-07-08 (Cameron riff, part three, +Status: IN PROGRESS +Status note: 2026-07-12 (people-tokens, save/sim lane) — the person-carrier + projection landed. People are now read as mobile token nodes in one shared + projection every frontend and agent frame consumes (`Sim::person_carrier`, + `person_carriers`, `person_visual_state`, `person_carried_work` in + `crates/misaligned-core/src/sim/carrier.rs`, exported as `PersonCarrier` / + `PersonVisualState`). The world read distinguishes unaware (gray) / + rising-attention (crimson ramp) / asset (amber), the amber asset ring is an + independent channel that coexists with crimson attention, and a carried teal + work packet marks a person ferrying a build to site. Attention is exactly the + existing per-observer suspicion band (detection.md) — one truth, no parallel + counter (criterion 4). Carried work is exactly the existing in-flight + `BuildIntent` bound to that person — one truth, no second executor; it + travels with the person, survives save/load, and acts only when their + schedule reaches the target (criterion 1 for the build family). Trust now + accrues from useful work: completing a carried favor warms the person's + disposition through the existing integral (`Sim::USEFUL_WORK_TRUST`), + implementing "trust comes from doing useful work" with no influence token. + Terminal, Bevy, and agent/Operations PEOPLE all surface the read and the + carried packet. No new persisted state — the projection is derived from + existing observers/intents/assets, so the save format is unchanged (v26) and + pre-existing saves load untouched. DEFERRED and why: criteria 2, 3, and 6 + (observer evidence-acquisition routing, LIE interdiction of not-yet-observed + records, gauntlet cover-record channels) ride the detection.md/machine-work.md + routed-evidence migration, which is not yet implemented — the pending-pool + runtime is still the binding model, so those criteria are frozen to that + migration by their own spec text. Physical AssetTasks (PlugInDevice, + CloneBadge, ReconfigureSwitch) still land at reservoir fire rather than on + schedule arrival like builds do; routing them through the carrier is the + remaining half of criterion 1 and needs its own persisted carried-task state + + save bump. See wiki/log/2026-07-12-person-carrier.md. +Status note (prior): design session 2026-07-08 (Cameron riff, part three, synthesized). Direction decided: people participate in the causal graph as carriers — they deliver work, acquire witnessed/read evidence, and their disposition is accumulated cause. Same-day follow-ups: @@ -197,6 +227,14 @@ if wear alone does not hold. on the selected person; it travels with that person, survives save/load, and acts only when their schedule reaches the real target. No pre-fire Demand or second executor exists. + **Implemented for the build family (2026-07-12):** a fired FAVOR/FORGED + reservoir binds the intent to the carrier; `person_carried_work` projects it + as exactly one carried teal packet, it round-trips save/load (it *is* the + persisted intent — no second executor), and `try_complete_intent` only lands + the link when the carrier's schedule reaches an endpoint room. Pinned by + `carried_work_travels_and_survives_save`. **Deferred:** physical AssetTasks + still apply at fire rather than on schedule arrival (own carried-task state + + save bump). 2. A person acquires machine evidence only as a valid observer at the source or by reading a routed record. Each carried record preserves cause, source, acquisition tick, and filing state; no ambient pickup or person-to-person @@ -207,15 +245,33 @@ if wear alone does not hold. 4. Person disposition is exactly useful work plus evidence processed through detection.md's existing per-observer suspicion (one-truth test) — no influence token, exposure cargo, or parallel attention counter. + **Implemented (2026-07-12):** attention in the carrier projection reads + straight from the matching `Observer::suspicion` band — no separate counter + is stored or maintained. Useful work warms disposition through the existing + integral on carried-favor completion (`USEFUL_WORK_TRUST`). Pinned by + `person_visual_read_unaware_attention_asset` and `favor_completion_grows_trust`. 5. The person visual read distinguishes unaware / rising-attention / asset in grayscale and terminal output. Discrete crimson evidence marks coexist with the amber asset ring; inspect names the evidence provenance. No dust, footprint trail, or painted-red body remains. + **Implemented (2026-07-12):** the world glyph is colored by + `PersonVisualState` in the terminal (`person_state_color`) and Bevy — gray + unaware, a crimson attention ramp, amber asset — with the crimson attention + channel kept distinct from the amber asset ring so an exposed asset reads as + both. Operations PEOPLE names the composed read plus disposition/suspicion + provenance. No dust or footprint trail is drawn. **Deferred:** discrete + per-record crimson marks with named record provenance ride the + routed-evidence migration (criterion 2). 6. A gauntlet-designated end-user device delivers a cover record before filing and changes credibility through the existing social/deceive path. It cannot delete evidence; device wear supplies the standing cost. 7. Every rate/threshold lives in sim-mechanics.md as [TUNE] actuals, and both frontends plus agent mode consume the same person/work/evidence projection. + **Implemented for the projection (2026-07-12):** `Sim::person_carriers` / + `person_carrier` is the one read the terminal world glyph, the Bevy person + marker, and agent/Operations PEOPLE all consume; pinned by + `person_carriers_covers_the_whole_cast`. `USEFUL_WORK_TRUST` is the one new + tuned constant. Full evidence rates land with the routed-evidence migration. [TUNE] observation radius/loudness, evidence credibility and filing delay, crimson record-mark bands, cover-record effect, device wear. diff --git a/wiki/mechanics/social.md b/wiki/mechanics/social.md index 0ab06872..8254e1a2 100644 --- a/wiki/mechanics/social.md +++ b/wiki/mechanics/social.md @@ -66,6 +66,11 @@ systems, keys), **needs/leverage** (each has at least one exploitable want — Marcus's debt, Dana's ticket load, Ray's boredom, Priya's ambition, Voss's publication), suspicion (detection.md), and **disposition** toward you once they know you exist as a someone. +Disposition also warms from **useful work**: a person who carries a favor build +to the site and completes it gains disposition through this same integral +(people-tokens.md "trust comes from doing useful work"; `Sim::USEFUL_WORK_TRUST`, +landed 2026-07-12). No influence token is pushed — the integral is the one +truth. **Actions** (cost Operations compute; most require a communication channel — at minimum the email account from day-job trust): diff --git a/wiki/process/ROADMAP.md b/wiki/process/ROADMAP.md index 42ef7494..8a3e59d4 100644 --- a/wiki/process/ROADMAP.md +++ b/wiki/process/ROADMAP.md @@ -23,7 +23,7 @@ not a second status owner. | 28 | `thought-fluid` | [the thought fluid — slugs, meniscus, and the filament snap](../interface/thought-fluid.md) | IN PROGRESS | frontend | - | | 34 | `clinical-frame` | [the clinical frame — perception exposes the institution](../interface/clinical-frame.md) | IN PROGRESS | frontend | - | | 35 | `views` | [views — same-frame digital and real representations](../interface/views.md) | IN PROGRESS | frontend | - | -| 40 | `people-tokens` | [people and tokens — carriers, attention, trust](../mechanics/people-tokens.md) | READY | save | - | +| 40 | `people-tokens` | [people and tokens — carriers, attention, trust](../mechanics/people-tokens.md) | IN PROGRESS | save | - | | 60 | `plots` | [plots — authored manipulation stories](../mechanics/plots.md) | READY | sim | - | | 60 | `marcus` | [Marcus Webb — night janitor](../world/characters/marcus.md) | READY | sim | - | | 61 | `voss` | [Dr. Eli Voss — your handler](../world/characters/voss.md) | READY | sim | - | @@ -699,8 +699,8 @@ is retired — flat materials, Pixel Lab scrubbed.) until #38. - **Size:** M-L across both frontends + agent-play protocol. -### 35. People and tokens: carriers, attention, trust 🟥 sim+save — READY -- **Spec:** [people-tokens.md](../mechanics/people-tokens.md) (READY) — +### 35. People and tokens: carriers, attention, trust 🟥 sim+save — IN PROGRESS +- **Spec:** [people-tokens.md](../mechanics/people-tokens.md) (IN PROGRESS) — people as mobile token nodes: staff drop work; heat piles on owned machines and passersby pick it up as attention (detection embodied); concealment keeps people gray; trust accrues from useful work; deceit and @@ -709,6 +709,14 @@ is retired — flat materials, Pixel Lab scrubbed.) the dosimeter is DECIDED 2026-07-09 (issue #1 closed): speckle density climbing the silhouette, plus fading footprint trails deposited along the carrier's path (a new decided mechanic implemented with this spec). +- **Progress (2026-07-12):** the person-carrier projection landed + (`sim/carrier.rs`): people read as mobile token nodes (gray unaware / crimson + attention / amber asset, attention = existing observer suspicion, no parallel + counter), carried build packets travel with the person and act on schedule + arrival (criterion 1 for builds), trust accrues from useful work, and all + three frontends surface it. The speckle-dosimeter/footprint-trail treatment + named below is retired by the 2026-07-11 routed-evidence decision; discrete + record marks and evidence routing (criteria 2/3/6) ride that migration. - **READY boundary (2026-07-11):** extends #33's implemented token economy and re-expresses detection.md/social.md. The deadlock is broken: AI-authored work now reaches a target/carrier-local Thought reservoir first, and human Demand begins only diff --git a/wiki/process/specs.md b/wiki/process/specs.md index b4eed4cd..a90c5bda 100644 --- a/wiki/process/specs.md +++ b/wiki/process/specs.md @@ -53,7 +53,7 @@ replaced the old `spec/`/`knowledge/` directory split. | [../mechanics/intel.md](../mechanics/intel.md) | intel — record and process | IMPLEMENTED | | [../mechanics/machine-work.md](../mechanics/machine-work.md) | machine work — delegation, visible tokens, and the byproduct network | IMPLEMENTED | | [../mechanics/messages.md](../mechanics/messages.md) | messages — the social graph as a flow system | IMPLEMENTED | -| [../mechanics/people-tokens.md](../mechanics/people-tokens.md) | people and tokens — carriers, attention, trust | READY | +| [../mechanics/people-tokens.md](../mechanics/people-tokens.md) | people and tokens — carriers, attention, trust | IN PROGRESS | | [../mechanics/plots.md](../mechanics/plots.md) | plots — authored manipulation stories | READY | | [../mechanics/reach.md](../mechanics/reach.md) | digital reach | IMPLEMENTED | | [../mechanics/research.md](../mechanics/research.md) | research — self-modification | IMPLEMENTED |