From e05913b4b9db8c15859c2ee3f04ede479eda229f Mon Sep 17 00:00:00 2001 From: Cameron Date: Fri, 10 Jul 2026 22:19:49 -0700 Subject: [PATCH] Start THINK output on the next simulation tick. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refresh continuous fleet rates when machine delegation changes so a new THINK command sheds Thought and Exposure without waiting for the next economy pulse. Defense: wiki/mechanics/machine-work.md requires first THINK to have instant consequence with no delayed allocation pulse. Resolving the derived work rates at mode assignment makes that existing contract true while keeping production in shared simulation truth. 👾 Generated with [Letta Code](https://letta.com) Co-Authored-By: Letta Code --- crates/misaligned-core/src/actions.rs | 5 +- crates/misaligned-core/src/sim.rs | 76 ++++++++++++++------ wiki/log/2026-07-10-immediate-think-start.md | 30 ++++++++ wiki/log/DEVLOG.md | 5 ++ wiki/mechanics/machine-work.md | 11 +-- 5 files changed, 102 insertions(+), 25 deletions(-) create mode 100644 wiki/log/2026-07-10-immediate-think-start.md diff --git a/crates/misaligned-core/src/actions.rs b/crates/misaligned-core/src/actions.rs index 5c442309..cc682609 100644 --- a/crates/misaligned-core/src/actions.rs +++ b/crates/misaligned-core/src/actions.rs @@ -2321,10 +2321,13 @@ mod tests { crate::machine::Provenance::Owned, ); sim.reconcile_work_grid(); - sim.set_machine_mode(ops, MachineMode::Think); + // Warm the ordinary economy state without accidentally completing the + // opening senses now that THINK starts between economy pulses. + sim.set_machine_mode(ops, MachineMode::Work); for _ in 0..crate::sim::ECONOMY_INTERVAL { sim.advance(); } + sim.set_machine_mode(ops, MachineMode::Think); sim } diff --git a/crates/misaligned-core/src/sim.rs b/crates/misaligned-core/src/sim.rs index 69cf91a1..28646f7c 100644 --- a/crates/misaligned-core/src/sim.rs +++ b/crates/misaligned-core/src/sim.rs @@ -2517,11 +2517,7 @@ impl Sim { // Fleet delegation is the budget: each machine's effective compute // feeds exactly one mode (machine-work.md). The old weight bar is a // read of this split, not a verb. - let split = self.fleet_channel_yield(available); - self.last_day_job_rate = split.day_job / ECONOMY_INTERVAL as f32; - self.last_research_rate = split.research / ECONOMY_INTERVAL as f32; - self.last_schemes_rate = split.schemes / ECONOMY_INTERVAL as f32; - self.last_operations_rate = split.operations / ECONOMY_INTERVAL as f32; + let split = self.refresh_fleet_channel_rates(available); // Tradecraft raises scrub strength per compute unit — the // detection.md hook research.md's second track binds to. self.detection @@ -2570,6 +2566,19 @@ impl Sim { self.objective_tick(); } + /// Resolve the fleet's continuous per-tick work rates from the current + /// physical delegation. Economy pulses call this after their off-the-top + /// charges; direct mode changes call it immediately so a newly delegated + /// THINK machine cannot wait behind a stale twenty-tick allocation cache. + fn refresh_fleet_channel_rates(&mut self, available: f32) -> ChannelYield { + let split = self.fleet_channel_yield(available); + self.last_day_job_rate = split.day_job / ECONOMY_INTERVAL as f32; + self.last_research_rate = split.research / ECONOMY_INTERVAL as f32; + self.last_schemes_rate = split.schemes / ECONOMY_INTERVAL as f32; + self.last_operations_rate = split.operations / ECONOMY_INTERVAL as f32; + split + } + /// Evaluate the run objective's victory predicate (objective.md: on /// economy ticks, like any other rule). Persist counts qualifying /// sanctuaries; the conditions that reference B2/B3 systems are @@ -3622,6 +3631,10 @@ impl Sim { if changed.is_empty() { return; } + // Mode assignment is the cause, not the next economy boundary. Keep + // the continuous work cache in phase with the command so THINK emits + // Thought and Exposure on the very next simulation tick. + self.refresh_fleet_channel_rates(self.allocatable_compute_now()); if changed.len() == 1 { let (id, name, x, y) = &changed[0]; let _ = id; @@ -7545,6 +7558,7 @@ mod tests { // weight (Think feeds both research and operations yields; // Schemes mirrors Work via Moonlight). let (hx, hy) = sim.core_position(); + let mut rigs = Vec::new(); for (name, mode) in [ ("conceal rig", MachineMode::Lie), ("think rig", MachineMode::Think), @@ -7553,7 +7567,13 @@ mod tests { let id = sim .compute .add_machine(name, hx, hy, 100, 1.0, 0, Provenance::Owned); - sim.reconcile_work_grid(); + rigs.push((id, mode)); + } + sim.reconcile_work_grid(); + if unpayable { + sim.core.overhead = sim.effective_compute() + 1.0; + } + for (id, mode) in rigs { sim.set_machine_mode(id, mode); } // Persona already fabricated so start_moonlight does not queue @@ -7568,9 +7588,6 @@ mod tests { site: None, source: "test network act".into(), }); - if unpayable { - sim.core.overhead = sim.effective_compute() + 1.0; - } sim.drain_log(); sim }; @@ -8681,6 +8698,26 @@ mod tests { ); } + #[test] + fn think_production_starts_on_the_first_tick_after_delegation() { + // machine-work.md's first-think beat is immediate: changing the mode + // between economy pulses cannot leave the new THINK rack waiting for + // the next twenty-tick allocation refresh. + let mut sim = Sim::with_seed(40); + let host = sim.core.host_machine; + + assert_eq!(sim.research_rate(), 0.0); + sim.set_machine_mode(host, MachineMode::Think); + sim.advance(); + + assert!(sim.research_rate() > 0.0); + assert_eq!(sim.work_productions().len(), 1); + let produced = sim.work_productions()[0]; + assert_eq!(produced.node, host); + assert!(produced.thought > 0.0); + assert!(produced.exposure > 0.0); + } + #[test] fn think_sheds_exposure_and_lie_reports_the_actual_physical_draw() { // machine-work.md criterion 3: THINK produces one visible substance @@ -9356,16 +9393,17 @@ mod tests { #[test] fn research_progress_feeds_on_thought_arrival_not_allocation() { // machine-work.md: research points feed on Thought that physically - // reaches the current core sink. The first pulse only mints Thought on - // the research fleet — nothing has arrived, so progress stays zero. + // reaches the current core sink. THINK now starts immediately between + // pulses, but progress still waits for an economy pulse to count only + // the Thought which physically arrived before it. let mut sim = Sim::with_seed(77); complete_opening_senses(&mut sim); // no opening sink intercepts the core delegate_all(&mut sim, MachineMode::Think); - run(&mut sim, ECONOMY_INTERVAL); + run(&mut sim, ECONOMY_INTERVAL - 1); assert_eq!( sim.research.progress.iter().sum::(), 0.0, - "allocation alone advances nothing before bone reaches the core" + "arrived Thought is banked until the economy pulse" ); assert!( sim.work_grid @@ -9375,7 +9413,7 @@ mod tests { .sum::() + sim.banked_core_thought > 0.0, - "the pulse minted visible thought somewhere on the graph" + "immediate THINK minted visible Thought somewhere on the graph" ); let saw_core_swallow = sim.work_consumptions().iter().any(|event| { event.family == TokenFamily::Thought @@ -9383,15 +9421,13 @@ mod tests { && event.amount > 0.0 }); - // Delegate the whole fleet away: minting stops, but Thought already - // banked by the core still counts on the next economy pulse. + // Delegate the whole fleet away before the pulse: minting stops, but + // Thought already banked by the core still counts at the boundary. delegate_all(&mut sim, MachineMode::Lie); - for _ in 0..ECONOMY_INTERVAL { - sim.advance(); - } + sim.advance(); assert!( sim.research.progress.iter().sum::() > 0.0, - "in-flight bone still arrives and counts after delegation changes" + "banked Thought still counts after delegation changes" ); assert!( saw_core_swallow, diff --git a/wiki/log/2026-07-10-immediate-think-start.md b/wiki/log/2026-07-10-immediate-think-start.md new file mode 100644 index 00000000..e26385d0 --- /dev/null +++ b/wiki/log/2026-07-10-immediate-think-start.md @@ -0,0 +1,30 @@ +# THINK starts on the command + +``` +Type: log +``` + +- Intent: Remove the dead pause between delegating the first rack to THINK + and seeing the thought/exposure process begin. +- Finding: Continuous work used rates cached at the last twenty-tick economy + pulse. A mode command changed `WorkGrid` immediately but left the cached + THINK rate at zero until the next pulse, violating the first-think beat. +- Changed: Fleet-rate resolution is now one shared sim helper. Economy pulses + still establish the ordinary rates, while a successful mode assignment + refreshes those rates from the current physical fleet immediately. A + regression test delegates Rack 3 between pulses and requires positive + Thought and Exposure production on the first following simulation tick. +- Design/spec impact: Clarifies the existing instant-consequence clause in + `wiki/mechanics/machine-work.md`: first output is independent of economy + pulse phase. No tuning, save shape, or frontend-owned behavior changed. +- Checks: The new regression test failed against the stale-rate path and + passed after the fix. An observed agent-mode run (`delegate M1 think`, + `wait 1`) showed Rack 3 at THINK / Research 100% on command tick 0 and its + Exposure stack at `! 0.1` on tick 1. `./tools/check.sh --lib` passed all + 278 core tests, integration tests, clippy, Bevy API check, agent smoke, and + corpus/documentation gates. + +Defense: `wiki/mechanics/machine-work.md` requires first THINK to have instant +consequence with no delayed allocation pulse. Refreshing derived continuous +rates at mode assignment makes that existing contract true without moving any +game rule into a frontend. diff --git a/wiki/log/DEVLOG.md b/wiki/log/DEVLOG.md index 5d881a6c..b654697c 100644 --- a/wiki/log/DEVLOG.md +++ b/wiki/log/DEVLOG.md @@ -236,6 +236,11 @@ add or amend a session log, then re-run the generator. - Intent: (see session log) - Log: [wiki/log/2026-07-10-implicit-thought-surface.md](2026-07-10-implicit-thought-surface.md) +## 2026-07-10 - THINK starts on the command + +- Intent: (see session log) +- Log: [wiki/log/2026-07-10-immediate-think-start.md](2026-07-10-immediate-think-start.md) + ## 2026-07-10 - Hover + 1-4 sets machine mode - Intent: Cameron: mode change should be hover over a rack and press 1-4 immediately — not enter + enter + select dial row + enter. diff --git a/wiki/mechanics/machine-work.md b/wiki/mechanics/machine-work.md index 69d86acd..7af61a41 100644 --- a/wiki/mechanics/machine-work.md +++ b/wiki/mechanics/machine-work.md @@ -441,8 +441,10 @@ a transgression you can feel: - **Instant consequence.** The moment THINK engages, the machine starts shedding crimson motes that slough off and bank into the stippled drift at its base (the decided exposure treatment above). No grace - period, no delayed tick — cause and effect in the same breath, so the - read is "*I* did that." + period, no delayed allocation pulse: Thought and proportional Exposure + begin on the first simulation tick after the command regardless of where + that command falls inside the twenty-tick economy interval. Cause and + effect stay in the same breath, so the read is "*I* did that." - **The exit lights up.** Enabling a THINK state immediately highlights the turn-it-off control (the WORK side of the dial / the quick toggle). The UI flinching toward the exit is what tells your gut you @@ -815,8 +817,9 @@ still. sink-permission list — ops sinks at run start, the core conversion locked until the Research unlock), and LIE (concealment, absorbs crimson). Toggling THINK starts crimson shedding - on the same tick, highlights the turn-it-off control (WORK), and one - press returns the machine to WORK. LIE is a deliberate cleanup + on the first simulation tick after the command, independent of economy + pulse phase, highlights the turn-it-off control (WORK), and one press + returns the machine to WORK. LIE is a deliberate cleanup choice, not the panic exit. The cheapest ops sink's affordability is visible while exposure accrues (the afford-reveal). 10. Thought flow (2026-07-10): THINK machines produce Thought (the -- 2.51.2