diff --git a/crates/misaligned-bevy/src/main.rs b/crates/misaligned-bevy/src/main.rs index 0658b441..3baa26e8 100644 --- a/crates/misaligned-bevy/src/main.rs +++ b/crates/misaligned-bevy/src/main.rs @@ -4850,28 +4850,22 @@ mod menu_focus_tests { magnitude: None, text: text.into(), }; - // Six same-cause starving sinks collapse to one counted line; a - // held sentence and a two-sink cause stay verbatim. + // Four same-cause starving lines collapse to one counted line; a + // held sentence and a one-sink cause stay verbatim. let rail = vec![ mk("QUESTION - choose someone", ReadClass::Held), + mk("EARS · no machine is thinking", ReadClass::Starving), + mk("TAP SWITCH · no machine is thinking", ReadClass::Starving), mk( - "EARS · open 0.0/3.0 · nothing thinking", + "TAP DOCK CAMERA · no machine is thinking", ReadClass::Starving, ), mk( - "TAP SWITCH · open 0.0/0.5 · nothing thinking", + "TAP BADGE CONTROLLER · no machine is thinking", ReadClass::Starving, ), mk( - "TAP DOCK CAMERA · open 0.0/0.5 · nothing thinking", - ReadClass::Starving, - ), - mk( - "TAP BADGE CONTROLLER · open 0.0/0.5 · nothing thinking", - ReadClass::Starving, - ), - mk( - "EYES · open 0.2/12.0 · flow not arriving", + "EYES · 2% full · thought isn't reaching it", ReadClass::Starving, ), ]; @@ -4880,8 +4874,8 @@ mod menu_focus_tests { lines, vec![ "- QUESTION - choose someone".to_string(), - "- 4 sinks starving · nothing thinking (EARS +3 more)".to_string(), - "- EYES · open 0.2/12.0 · flow not arriving".to_string(), + "- 4 waiting for thought · no machine is thinking (EARS +3 more)".to_string(), + "- EYES · 2% full · thought isn't reaching it".to_string(), ], "held stays verbatim, big group counts, small cause stays verbatim" ); diff --git a/crates/misaligned-bevy/src/rail_ui.rs b/crates/misaligned-bevy/src/rail_ui.rs index 3eed4dcb..7f42060d 100644 --- a/crates/misaligned-bevy/src/rail_ui.rs +++ b/crates/misaligned-bevy/src/rail_ui.rs @@ -792,7 +792,11 @@ pub(super) fn collapse_read_lines(rail: &[misaligned::sim::read::ReadSentence]) lines.push(format!("- {}", s.text)); } } - for cause in ["nothing thinking", "flow not arriving"] { + for cause in [ + "no machine is thinking", + "research is taking the thought", + "thought isn't reaching it", + ] { let group: Vec<&&str> = starving.iter().filter(|t| t.ends_with(cause)).collect(); match group.len() { 0 => {} @@ -800,7 +804,7 @@ pub(super) fn collapse_read_lines(rail: &[misaligned::sim::read::ReadSentence]) n => { let first = group[0].split(" · ").next().unwrap_or(group[0]).to_string(); lines.push(format!( - "- {n} sinks starving · {cause} ({first} +{} more)", + "- {n} waiting for thought · {cause} ({first} +{} more)", n - 1 )); } @@ -1091,7 +1095,7 @@ fn sidebar_cycle_rows_text(sim: &Sim, selected: usize) -> String { .unwrap_or(0), )); s.push_str(&format!( - "thought reservoirs {} open", + "{} waiting for thought", sim.sink_readouts().len(), )); if let Some(stack) = sim.work_stack_for_machine(sim.core.host_machine) { diff --git a/crates/misaligned-core/src/operations_projection.rs b/crates/misaligned-core/src/operations_projection.rs index f3c92afa..218e5b89 100644 --- a/crates/misaligned-core/src/operations_projection.rs +++ b/crates/misaligned-core/src/operations_projection.rs @@ -1226,7 +1226,7 @@ impl Sim { provenance: vec![format!("{} @ tick {}", recording.feed, recording.tick)], facts, progress: if processing { - vec!["PROCESSING: THOUGHT RESERVOIR FILLING".into()] + vec!["THINKING TO PROCESS".into()] } else { Vec::new() }, @@ -2367,7 +2367,7 @@ impl Sim { ], progress: vec![ format!("filling: {:.0}%", pct), - "wait: thought reservoir filling".into(), + "wait: still thinking".into(), ], related: vec![OperationsLink { relation: "TARGET", diff --git a/crates/misaligned-core/src/sim/communications.rs b/crates/misaligned-core/src/sim/communications.rs index b95da5d8..da235192 100644 --- a/crates/misaligned-core/src/sim/communications.rs +++ b/crates/misaligned-core/src/sim/communications.rs @@ -1781,10 +1781,7 @@ impl Sim { self.thought_sinks .open_reservoir(node, &format!("PROCESS {raw_id}"), tokens, effect); self.ensure_sink_ingress(); - self.push_log(format!( - "Opened processing sink: {:.2} Thought on host for information #{raw_id}. THINK to fill it.", - tokens - )); + self.push_log("Processing a recording on the host — think to finish it."); true } diff --git a/crates/misaligned-core/src/sim/read.rs b/crates/misaligned-core/src/sim/read.rs index 4d79e1b0..4c5d57b4 100644 --- a/crates/misaligned-core/src/sim/read.rs +++ b/crates/misaligned-core/src/sim/read.rs @@ -232,13 +232,12 @@ impl Sim { } } - /// Open sinks that no flow reached last tick. A starving sentence names - /// what is taking the flow, not only that flow is absent: "nothing - /// thinking" (no online THINK machine anywhere), "the core is drawing it - /// to research" (Thought arrived, but the passive core draw of last - /// resort took it — the competitor a player cannot otherwise see), or - /// the bare "flow not arriving" when neither is true. Every branch reads - /// facts this tick already recorded; none invents state. + /// Open sinks that no Thought reached last tick. Human wording only: + /// name the action, optional progress, and what is taking the thought. + /// Causes: "no machine is thinking", "research is taking the thought" + /// (core's passive draw — the competitor a player cannot otherwise see), + /// or "thought isn't reaching it" when no rival is identifiable. Every + /// branch reads facts this tick already recorded; none invents state. fn read_starving(&self, out: &mut Vec) { let thinking = self .compute @@ -255,20 +254,25 @@ impl Sim { continue; } let why = if !thinking { - "nothing thinking" + "no machine is thinking" } else if core_drew_thought { - "the core is drawing it to research" + "research is taking the thought" } else { - "flow not arriving" + "thought isn't reaching it" + }; + let text = if sink.threshold > f32::EPSILON && sink.fill > f32::EPSILON { + let pct = ((sink.fill / sink.threshold) * 100.0) + .round() + .clamp(1.0, 99.0) as i32; + format!("{} · {pct}% full · {why}", sink.label) + } else { + format!("{} · {why}", sink.label) }; out.push(ReadSentence { class: ReadClass::Starving, anchor: None, magnitude: None, - text: format!( - "{} · open {:.1}/{:.1} · {why}", - sink.label, sink.fill, sink.threshold - ), + text, }); } } diff --git a/crates/misaligned-core/src/sim/tests/read.rs b/crates/misaligned-core/src/sim/tests/read.rs index a45fb1dd..155bd753 100644 --- a/crates/misaligned-core/src/sim/tests/read.rs +++ b/crates/misaligned-core/src/sim/tests/read.rs @@ -11,10 +11,10 @@ use crate::plot::PlotState; use crate::sim::read::ReadClass; use crate::work_grid::MachineMode; -/// A starving sentence must name what is taking the flow. The core is the +/// A starving sentence must name what is taking the thought. The core is the /// passive draw of last resort, so Thought landing there is the competitor a -/// player has no other way to see — "flow not arriving" alone states a -/// symptom the player cannot act on. +/// player has no other way to see — "thought isn't reaching it" alone states +/// a symptom the player cannot act on. #[test] fn a_starving_sink_names_the_core_draw_that_outcompetes_it() { let mut sim = Sim::new(); @@ -38,7 +38,7 @@ fn a_starving_sink_names_the_core_draw_that_outcompetes_it() { assert!( starving .iter() - .all(|s| s.text.contains("the core is drawing it to research")), + .all(|s| s.text.contains("research is taking the thought")), "a sink starved while the core drew Thought must name that draw: {starving:?}" ); named = true; @@ -55,7 +55,7 @@ fn fresh_sim_reads_exactly_the_starving_opening_sink() { assert!( sentences.iter().any(|s| s.class == ReadClass::Starving && s.text.contains("EARS") - && s.text.contains("nothing thinking")), + && s.text.contains("no machine is thinking")), "the pre-opened Ears sink is starving at tick 0: {sentences:?}" ); assert!( diff --git a/crates/misaligned-terminal/src/agent.rs b/crates/misaligned-terminal/src/agent.rs index 8bbae4a7..ad8fec83 100644 --- a/crates/misaligned-terminal/src/agent.rs +++ b/crates/misaligned-terminal/src/agent.rs @@ -1999,7 +1999,7 @@ fn help_lines() -> Vec { ("reach", "inspect the earned device graph"), ( "read", - "list every standing read sentence: held choices with their exact option ids, starving sinks, trace debt, warmed standing emissions", + "list every standing read sentence: held choices with their exact option ids, actions waiting for thought, trace debt, warmed standing emissions", ), ( "intel", @@ -2627,7 +2627,7 @@ fn render_sidebar(sim: &Sim, cursor: (i32, i32)) -> Vec { ); line( &mut lines, - &format!("thought reservoirs {} open", sim.sink_readouts().len()), + &format!("{} waiting for thought", sim.sink_readouts().len()), ); if let Some(stack) = sim.work_stack_for_machine(sim.core.host_machine) { line( @@ -3229,7 +3229,7 @@ fn render_reach(sim: &Sim) -> String { lines.push(panel_title("REACH")); panel_story_spine(&mut lines, sim); lines.push(panel_line(&format!( - "thought reservoirs {} open · reach spreads from what you control", + "{} waiting for thought · reach spreads from what you control", sim.sink_readouts().len() ))); lines.push(panel_line("DEVICE SEGMENT REACH")); diff --git a/crates/misaligned-terminal/src/ui.rs b/crates/misaligned-terminal/src/ui.rs index 5dbe9796..3f6e2319 100644 --- a/crates/misaligned-terminal/src/ui.rs +++ b/crates/misaligned-terminal/src/ui.rs @@ -1451,7 +1451,7 @@ impl UI { line( stdout, &mut row, - &format!("thought reservoirs {} open", sim.sink_readouts().len()), + &format!("{} waiting for thought", sim.sink_readouts().len()), pal::FAINT, )?; // Parity of legibility (computer-visual-language.md criterion 7): diff --git a/tools/check.sh b/tools/check.sh index 34370648..dfc4289e 100755 --- a/tools/check.sh +++ b/tools/check.sh @@ -303,7 +303,7 @@ run_agent_smoke() { cmp -s "$tmp_a" "$tmp_b" || { echo "FAIL: same --seed agent runs differ"; smoke_fail=1; } cmp -s "$tmp_a" "$tmp_c" && { echo "FAIL: different --seed agent runs matched"; smoke_fail=1; } LC_ALL=C grep -q $'\033' "$tmp_a" && { echo "FAIL: agent mode emitted ANSI escapes"; smoke_fail=1; } - for pat in "MISALIGNED" "PEOPLE" "RESEARCH" "intensity " "help: wait N" "Opened processing sink" "-- ok tick:"; do + for pat in "MISALIGNED" "PEOPLE" "RESEARCH" "intensity " "help: wait N" "Processing a recording on the host" "-- ok tick:"; do grep -q -- "$pat" "$tmp_a" || { echo "FAIL: agent mode output missing '$pat'"; smoke_fail=1; } done rm -f "$tmp_a" "$tmp_b" "$tmp_c" diff --git a/wiki/interface/digital-read.md b/wiki/interface/digital-read.md index 95a26164..feda0c4a 100644 --- a/wiki/interface/digital-read.md +++ b/wiki/interface/digital-read.md @@ -12,10 +12,12 @@ Status note: IMPLEMENTED. Adopted 2026-07-15 from the Fable playtest is deferred (presence.md). Current state, criterion by criterion: - **The projection (1).** `Sim::read_sentences` (`sim/read.rs`) is the one renderer-neutral producer: Held choices (carrying each option's exact - bound `choose` command), Starving sinks, TraceDebt, and Standing - emissions (which surface only once their sampler warms above Cold — the - attention economy). Every frontend prints these; none composes its own - causal prose. The agent `read` verb prints the same sentences. + bound `choose` command), Starving actions waiting for thought (plain + English cause, optional percent progress — never reservoir fractions), + TraceDebt, and Standing emissions (which surface only once their sampler + warms above Cold — the attention economy). Every frontend prints these; + none composes its own causal prose. The agent `read` verb prints the same + sentences. - **Reticle receipt (2).** The hover verb bar shows the focused verb's cost + `ExpectedSignature` (observer + band) straight off `ActionDesc`, or no added attention. `ActionDesc::receipt_read` expands those fields for @@ -131,22 +133,21 @@ that perception rendered. - **Standing anchor sentences (glance tier).** Every earned anchor in view wears at most one short live sentence naming its current relationship to you: the switch carries `egress hum · Dana samples this · Curious`; a - starving reservoir carries `open · nothing thinking`; a held plot + starving action carries `TAP CAMERA · no machine is thinking`; a held plot carries its question. Stable-and-quiet state says nothing — **the attention economy is law**: starvation, deadlines, contradictions, held choices, and band motion rise; nominal state stays dark. A world made of language must not become every system continuously talking. - **A starving sentence names its competitor (binding, 2026-07-24).** - Where a rising state has an identifiable rival for the same flow, the - sentence names the rival rather than only the absence: an open sink that - Thought did not reach while the core's passive draw took Thought reads - `the core is drawing it to research`, not the inert `flow not arriving`. - The distinction comes only from facts the tick already recorded (the - work-consumption readout), never invented state, and the bare form - remains for the case where no rival is identifiable. Filed after the - 2026-07-24 agent playtest watched `flow not arriving` sit unchanged for - 1200 ticks while the research ladder consumed every thought: a status - the player cannot act on is attention spent for nothing. + **A starving sentence speaks plain English (binding, 2026-07-24).** It + names the waiting action, optional percent progress when partially filled, + and what is taking the thought — never reservoir fill fractions + (`open 0.3/0.5`), never "flow not arriving," never "nothing thinking." + Causes are: `no machine is thinking`, `research is taking the thought` + (core's passive draw — the competitor a player cannot otherwise see), or + `thought isn't reaching it` when no rival is identifiable. The distinction + comes only from facts the tick already recorded (the work-consumption + readout), never invented state. Filed after playtests watched protocol + English sit for thousands of ticks without composing a decision. - **Reticle receipts (focus tier).** Focusing any verb (hover, controller focus, or agent `actions` listing — same rows) renders the full receipt at the reticle: `cost · ExpectedSignature::label() · [band]`, DIM diff --git a/wiki/log/2026-07-24-plain-starving-copy.md b/wiki/log/2026-07-24-plain-starving-copy.md new file mode 100644 index 00000000..32d13e3e --- /dev/null +++ b/wiki/log/2026-07-24-plain-starving-copy.md @@ -0,0 +1,37 @@ +# 2026-07-24 — Starving and process lines speak plain English + +``` +Type: log +``` + +## Intent + +Player-facing status for open Thought work still read like a protocol +dump: `TAP ENVIRONMENTAL MONITOR · open 0.3/0.5 · flow not arriving`, +`nothing thinking`, `Opened processing sink: 0.40 Thought on host for +information #7`, and `thought reservoirs N open`. Playtests kept +quoting those lines as legibility failures. + +## Changed + +- Starving sentences: action name, optional percent full, and a plain + cause — `no machine is thinking`, `research is taking the thought`, + or `thought isn't reaching it`. No `open a/b` fractions. +- Process start log: `Processing a recording on the host — think to + finish it.` +- Operations progress: `THINKING TO PROCESS` / `wait: still thinking` + instead of "thought reservoir." +- Terminal, agent, and Bevy chrome: `N waiting for thought` instead of + "thought reservoirs N open." +- Rail collapse groups on the new causes and says `waiting for thought`. + +## Spec + +Amends digital-read.md's starving-sentence clause to bind the plain +English form. Clears the 2026-07-24 starving findings-queue line (the +competitor-naming intent is absorbed and the protocol English is gone). + +## Verification + +Core read tests and Bevy rail-collapse test updated and run under the +lib gate. diff --git a/wiki/log/DEVLOG.md b/wiki/log/DEVLOG.md index cbfdcff7..8f87be77 100644 --- a/wiki/log/DEVLOG.md +++ b/wiki/log/DEVLOG.md @@ -26,6 +26,11 @@ add or amend a session log, then re-run the generator. - Intent: Reported from play: "it wasn't really obvious why we were clearing Marcus's gambling debts." The row read `plot: Debt Settled — Settle Marcus's gambling debt through a negotiator persona before the creditor's deadline` — an act, a persona, a deadline, and no stated return for... - Log: [wiki/log/2026-07-24-plot-rows-name-their-payoff.md](2026-07-24-plot-rows-name-their-payoff.md) +## 2026-07-24 - Starving and process lines speak plain English + +- Intent: Player-facing status for open Thought work still read like a protocol dump: `TAP ENVIRONMENTAL MONITOR · open 0.3/0.5 · flow not arriving`, `nothing thinking`, `Opened processing sink: 0.40 Thought on host for information #7`, and `thought reservoirs N open`. Playtests kept qu... +- Log: [wiki/log/2026-07-24-plain-starving-copy.md](2026-07-24-plain-starving-copy.md) + ## 2026-07-24 - One keyboard map, and a jump to what you earned - Intent: Two asks from play. First: put the keyboard map in the spec. Second: bind `Shift`+arrow, after a survey showed the strongest unclaimed need was not region select but *getting back to a machine you lost* — the Co playtest's core unresolved complaint ("Rack 3 moved out of view a... diff --git a/wiki/process/tick-ledger.md b/wiki/process/tick-ledger.md index 4c7cea16..df9fb517 100644 --- a/wiki/process/tick-ledger.md +++ b/wiki/process/tick-ledger.md @@ -92,4 +92,3 @@ question, bug, insecurity — plus `gate` for a checker owed to the recurrence-promotes-to-the-gate rule. - 2026-07-23 · bug · Operations workspace selection · the unpaused live object rail still binds selection only to a row index, so reorder or disappearance can silently retarget before confirmation or an immediate action; Bevy also lacks the promised pointer back path. Reimplement the intent of stranded commit `308f98cf` against the current modular UI rather than replaying its obsolete diff. -- 2026-07-24 · insecurity · starving read sentences · a starving sink names its symptom and never its competitor: `TAP ENVIRONMENTAL MONITOR · open 0.3/0.5 · flow not arriving` stayed pinned for 1200 ticks while the Research ladder consumed every thought, and switching to WORK only changed it to `nothing thinking`. Blocked and starving sentences should name what is taking the flow, so the line composes a decision rather than a status — [report](../playtests/2026-07-24-playtest-fable-agent-headless.md).