From 95008f65827a7464079e267f602bc381d8dfbf35 Mon Sep 17 00:00:00 2001 From: Cameron Date: Fri, 17 Jul 2026 12:07:24 -0700 Subject: [PATCH] Intel buffer warns once per overflow episode, not per drop. Last open Fable-playtest finding (7): a full buffer logged one line per dropped recording (five near-identical lines in one stretch). A runtime-only intel_overflow_warned flag announces the loss once when dropping begins and again only after processing makes room and the buffer refills. The flag lives on Sim, not SaveState, so no save format change. The ongoing-pressure nudge is unchanged. Pinned by buffer_overflow_warns_once_per_episode_not_once_per_drop. Defense: wiki/mechanics/intel.md bounded-buffer clause, amended to one legible line per overflow episode - the player is still told data is being lost, but once per episode. No save or balance change. --- .../misaligned-core/src/sim/communications.rs | 20 +++++--- crates/misaligned-core/src/sim/mod.rs | 6 +++ .../src/sim/tests/communications.rs | 50 +++++++++++++++++++ wiki/log/2026-07-17-intel-buffer-nag.md | 29 +++++++++++ wiki/log/DEVLOG.md | 5 ++ wiki/mechanics/intel.md | 8 ++- 6 files changed, 110 insertions(+), 8 deletions(-) create mode 100644 wiki/log/2026-07-17-intel-buffer-nag.md diff --git a/crates/misaligned-core/src/sim/communications.rs b/crates/misaligned-core/src/sim/communications.rs index cea27a1d..272ba4c0 100644 --- a/crates/misaligned-core/src/sim/communications.rs +++ b/crates/misaligned-core/src/sim/communications.rs @@ -859,12 +859,16 @@ impl Sim { if self.intel_buffer.len() >= Self::INTEL_BUFFER_CAPACITY { let dropped = self.intel_buffer.remove(0); self.cancel_process_sinks_for(dropped.id); - self.push_log(format!( - "Intel buffer full: dropped {} from {} at tick {}. Open Operations INTEL (I) and review waiting recordings.", - dropped.opaque_label(), - dropped.feed, - dropped.tick - )); + // Announce the loss once per overflow episode, not once per dropped + // item (playtest finding 7): the flag clears when processing makes + // room, so re-filling to capacity announces again. + if !self.intel_overflow_warned { + self.push_log( + "Intel buffer full: dropping the oldest unreviewed recordings. \ + Open Operations INTEL (I) and review before more is lost.", + ); + self.intel_overflow_warned = true; + } } let id = event.id; let auto_review = self.auto_review_enabled_for_event(&event); @@ -929,6 +933,10 @@ impl Sim { return false; }; let raw = self.intel_buffer.remove(idx); + // Processing made room: the next overflow episode announces afresh. + if self.intel_buffer.len() < Self::INTEL_BUFFER_CAPACITY { + self.intel_overflow_warned = false; + } // Feed evidence returns to the instrument that actually captured it. // Non-device records may still resolve to a currently seen person or // an honestly recorded visual room. diff --git a/crates/misaligned-core/src/sim/mod.rs b/crates/misaligned-core/src/sim/mod.rs index c6025ba3..ea2b2227 100644 --- a/crates/misaligned-core/src/sim/mod.rs +++ b/crates/misaligned-core/src/sim/mod.rs @@ -414,6 +414,11 @@ pub struct Sim { pub heard_events: Vec, /// Opaque, unprocessed recordings captured from subscribed feeds. pub intel_buffer: Vec, + /// Runtime-only: true once the bounded buffer has begun dropping the + /// oldest recordings, so the loss is announced once per overflow episode + /// rather than once per dropped item (playtest 2026-07-15 finding 7). + /// Not persisted; a loaded run re-announces on its next drop. + pub(crate) intel_overflow_warned: bool, /// Durable processed intel with provenance. pub intel: Vec, /// Canonical compacted custody for repeated routine output. Exact @@ -664,6 +669,7 @@ impl Sim { remembered: HashMap::new(), heard_events: Vec::new(), intel_buffer: Vec::new(), + intel_overflow_warned: false, intel: Vec::new(), intel_streams: Vec::new(), intel_policies: IntelPolicyLedger::default(), diff --git a/crates/misaligned-core/src/sim/tests/communications.rs b/crates/misaligned-core/src/sim/tests/communications.rs index cd3981c9..d73dda04 100644 --- a/crates/misaligned-core/src/sim/tests/communications.rs +++ b/crates/misaligned-core/src/sim/tests/communications.rs @@ -387,6 +387,56 @@ fn recordings_do_not_grant_knowledge_until_processed() { ); } +#[test] +fn buffer_overflow_warns_once_per_episode_not_once_per_drop() { + // Playtest finding 7: a full buffer that keeps receiving arrivals used to + // log one "buffer full" line per dropped item. It now warns once when the + // episode begins, and again only after processing makes room and it + // refills. + let mut sim = Sim::new(); + ensure_ops_executor(&mut sim); + let push = |sim: &mut Sim| { + sim.record_raw_intel( + "test-feed", + Some("server_room".into()), + 0, + 0, + Some(0), + RawIntelKind::Presence { entered: true }, + ); + }; + + // Fill to capacity, then drop ten more in one stretch with no processing. + for _ in 0..(Sim::INTEL_BUFFER_CAPACITY + 10) { + push(&mut sim); + } + let warnings = |sim: &mut Sim| { + sim.drain_log() + .into_iter() + .filter(|l| l.contains("Intel buffer full")) + .count() + }; + assert_eq!( + warnings(&mut sim), + 1, + "ten drops in one episode produce exactly one warning" + ); + + // Process one recording to make room, then overflow again: a fresh episode + // earns a fresh warning. + sim.review_recordings(); + finish_ops(&mut sim); + let _ = sim.drain_log(); + for _ in 0..3 { + push(&mut sim); + } + assert_eq!( + warnings(&mut sim), + 1, + "refilling to overflow after making room warns again" + ); +} + #[test] fn processed_sightings_stage_schedule_and_buffer_is_bounded() { let mut sim = Sim::new(); diff --git a/wiki/log/2026-07-17-intel-buffer-nag.md b/wiki/log/2026-07-17-intel-buffer-nag.md new file mode 100644 index 00000000..20bf284e --- /dev/null +++ b/wiki/log/2026-07-17-intel-buffer-nag.md @@ -0,0 +1,29 @@ +# Intel buffer warns once per overflow episode + +``` +Type: log +``` + +Last open finding from the 2026-07-15 Fable playtest (finding 7): a full +intel buffer that kept receiving arrivals logged one "Intel buffer full" +line per dropped recording — the playtest saw five near-identical lines in +one stretch, the repeated-nag smell the attention economy forbids. + +Fix: a runtime-only `Sim::intel_overflow_warned` flag. The buffer +announces the loss once when it begins dropping (one generic line, since +naming one arbitrary dropped item among many was noise), stays quiet while +it remains full, and announces again only after processing makes room and +it refills — a fresh episode earns a fresh line. The flag is not +persisted (it lives on `Sim`, not `SaveState`), so no save format change; +a loaded run re-announces on its next drop. Ongoing pressure is still +carried by the buffer nudge, unchanged. + +Pinned by `buffer_overflow_warns_once_per_episode_not_once_per_drop`: ten +drops in one stretch produce one warning, and refilling to overflow after +processing one produces a second. Lib gate green; an observed run +confirmed no spam with the real save untouched. + +Defense: wiki/mechanics/intel.md's bounded-buffer clause, amended to +"one legible log line per overflow episode" — the buffer must still tell +the player it is losing data (compute scarcity felt), but once per +episode, not once per item. No save or balance change. diff --git a/wiki/log/DEVLOG.md b/wiki/log/DEVLOG.md index 2d595115..b787a0ea 100644 --- a/wiki/log/DEVLOG.md +++ b/wiki/log/DEVLOG.md @@ -41,6 +41,11 @@ add or amend a session log, then re-run the generator. - Intent: (see session log) - Log: [wiki/log/2026-07-17-intel-magnitude-altitude.md](2026-07-17-intel-magnitude-altitude.md) +## 2026-07-17 - Intel buffer warns once per overflow episode + +- Intent: (see session log) +- Log: [wiki/log/2026-07-17-intel-buffer-nag.md](2026-07-17-intel-buffer-nag.md) + ## 2026-07-17 - Financial traffic waits on graph ownership - Intent: (see session log) diff --git a/wiki/mechanics/intel.md b/wiki/mechanics/intel.md index af877597..4a686987 100644 --- a/wiki/mechanics/intel.md +++ b/wiki/mechanics/intel.md @@ -83,8 +83,12 @@ room, a conversation happens, machinery changes state, an anomaly — a Recording is free (the instrument does the work). The buffer is **bounded** [TUNE capacity]: when full, the oldest *unprocessed* events -drop, with a legible log line. Compute scarcity is felt as "I could not -afford to digest everything I collected," never as a missed click. +drop, with **one legible log line per overflow episode** — announced when +the buffer begins dropping and again only after processing makes room and +it refills, never once per dropped item (playtest finding, 2026-07-15). +Ongoing pressure is carried by the buffer nudge, not by a repeated log +line. Compute scarcity is felt as "I could not afford to digest everything +I collected," never as a missed click. Documents are the same pipeline with a physical source: reading the Storage B records (via an asset task or later physical access) deposits -- 2.51.2