diff --git a/crates/misaligned-core/src/save.rs b/crates/misaligned-core/src/save.rs index 30529512..dec547be 100644 --- a/crates/misaligned-core/src/save.rs +++ b/crates/misaligned-core/src/save.rs @@ -621,6 +621,19 @@ fn validate_current_save(mut state: SaveState) -> Result { if state.dayjob.pilot_failed && state.dayjob.active.is_some() { return Err("current-version save keeps an active day job after pilot failure".into()); } + if let Some((machine, mode)) = state.compute.machines.iter().find_map(|machine| { + state + .work_grid + .mode(machine.id) + .filter(|mode| !mode.is_delegable()) + .map(|mode| (machine, mode)) + }) { + return Err(format!( + "current-version save delegates player machine M{} to non-delegable {} mode", + machine.id, + mode.name() + )); + } if state .last_facility_meter_levels .iter() @@ -3875,6 +3888,23 @@ mod tests { ); } + #[test] + fn current_save_rejects_relay_delegated_player_machine() { + let mut state = SaveState::from_sim(&Sim::with_seed(0xC0DE_7E1A)); + let host = state.core.host_machine; + state + .work_grid + .assign_mode(host, MachineMode::Relay) + .expect("the low-level grid can author infrastructure nodes"); + + assert_eq!( + validate_current_save(state).unwrap_err(), + format!( + "current-version save delegates player machine M{host} to non-delegable relay mode" + ) + ); + } + /// Take every device between a carrier and the player's own rack, so a /// LIE body co-located with the core actually holds the route the record /// travels. Interdiction is positional (detection.md): before the wire diff --git a/crates/misaligned-core/src/sim/mod.rs b/crates/misaligned-core/src/sim/mod.rs index 67736b3b..827e04ff 100644 --- a/crates/misaligned-core/src/sim/mod.rs +++ b/crates/misaligned-core/src/sim/mod.rs @@ -892,7 +892,7 @@ impl Sim { MachineMode::Work | MachineMode::Think | MachineMode::Lie ) } - OpeningStage::World => true, + OpeningStage::World => mode.is_delegable(), } } diff --git a/crates/misaligned-core/src/sim/tests/work.rs b/crates/misaligned-core/src/sim/tests/work.rs index 2d4fcaee..56d47f61 100644 --- a/crates/misaligned-core/src/sim/tests/work.rs +++ b/crates/misaligned-core/src/sim/tests/work.rs @@ -120,6 +120,21 @@ fn opening_reveals_lie_only_after_think_and_world_only_after_a_sense() { assert_eq!(sim.opening_stage, OpeningStage::Modes); } +#[test] +fn relay_never_becomes_a_player_machine_delegation() { + let mut sim = Sim::new(); + let host = sim.core.host_machine; + sim.opening_stage = OpeningStage::World; + + assert!(!sim.opening_mode_available(MachineMode::Relay)); + sim.set_machine_mode(host, MachineMode::Relay); + assert_eq!( + sim.work_grid.mode(host), + Some(MachineMode::Work), + "Relay remains graph infrastructure after every opening boundary" + ); +} + #[test] fn ears_reservoir_is_pre_opened_and_first_think_fills_it() { // Staged senses (machine-work.md ops bullet, 2026-07-10): the Ears diff --git a/wiki/log/2026-08-04-player-machine-relay-save-invariant.md b/wiki/log/2026-08-04-player-machine-relay-save-invariant.md new file mode 100644 index 00000000..997ba324 --- /dev/null +++ b/wiki/log/2026-08-04-player-machine-relay-save-invariant.md @@ -0,0 +1,21 @@ +# 2026-08-04 — Keep Relay out of the player fleet + +``` +Type: log +``` + +## Finding + +`MachineMode::Relay` is the WorkGrid marker for off-map sources and switch +hops, not a player delegation. Human and agent frontends offered only WORK, +THINK, and LIE, but the core legality predicate returned true for every mode +after the opening reached World. A direct sim command could therefore put a +player machine in Relay, and the current-save gate would load the same invalid +fleet state from edited or corrupted bytes. + +## Repair + +The mature-World legality branch now admits only delegable modes. Current-save +validation independently checks each compute machine's WorkGrid assignment and +rejects non-delegable modes while leaving infrastructure relay nodes intact. +Focused regressions pin both the live command path and the persisted boundary. diff --git a/wiki/log/DEVLOG.md b/wiki/log/DEVLOG.md index 38d5d55f..3583041b 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: (see session log) - Log: [wiki/log/2026-08-04-public-api-consumer-gate.md](2026-08-04-public-api-consumer-gate.md) +## 2026-08-04 - Keep Relay out of the player fleet + +- Intent: (see session log) +- Log: [wiki/log/2026-08-04-player-machine-relay-save-invariant.md](2026-08-04-player-machine-relay-save-invariant.md) + ## 2026-08-04 - Make every creditor node person-scoped - Intent: (see session log) diff --git a/wiki/mechanics/compute.md b/wiki/mechanics/compute.md index 882a47a3..3803e259 100644 --- a/wiki/mechanics/compute.md +++ b/wiki/mechanics/compute.md @@ -243,7 +243,9 @@ downtime, power/cooling, and observer-band costs shown before commitment. formula; sim tests cover buy, steal, and optimize routes changing it. 2. Exact machine delegation and intensity persist across saves, derive the economy-tick channel yields, and unpaid core overhead visibly degrades those - channels. No independent allocation-weight state exists. + channels. No independent allocation-weight state exists. Every persisted + player-machine mode is delegable; current saves fail closed rather than + loading infrastructure-only `Relay` as a fleet assignment. 3. Each route exists end-to-end in the basement: at least one purchasable machine, one stealable source, and a working efficiency research track. 4. Scavenged machines fail occasionally per reliability; failures log and @@ -253,6 +255,10 @@ downtime, power/cooling, and observer-band costs shown before commitment. it moves that machine's mode output, hard adds Power/Thermal at its tile, and both frontends expose the same direct focused/selected control. +Defense: `current_save_rejects_relay_delegated_player_machine` proves that the +current-save gate distinguishes player fleet machines from valid WorkGrid relay +nodes instead of loading infrastructure as a fourth delegation. + ### Successor acceptance: capability-shaped hardware (not yet implemented) These criteria amend the next runtime slice without falsely reopening the diff --git a/wiki/mechanics/machine-work.md b/wiki/mechanics/machine-work.md index 3883f902..11eac911 100644 --- a/wiki/mechanics/machine-work.md +++ b/wiki/mechanics/machine-work.md @@ -576,6 +576,9 @@ call into: - `MachineMode` is exactly `work | think | lie`; `relay` is the non-delegable graph-infrastructure marker. Assigning a machine replaces the previous mode, so per-machine percentage splitting has nowhere to reappear. + The sim rejects `relay` at every opening stage, including the mature World + state, and current-save validation rejects it on every player fleet machine; + infrastructure nodes may still persist it. **Successor amendment:** mode legality is the intersection with the chassis / fitted-module supported set; archive and future infrastructure bodies may have no `MachineMode` while retaining their other WorkGrid roles. @@ -849,7 +852,10 @@ still. core-host Demand; WORK clears day-job Demand first, then contract Demand from the same physical capacity, with a persisted delivery receipt. Per-machine intensity is implemented in the sim, save v17, terminal, - Bevy, and agent protocol; hard intensity stands local Power/Thermal. + Bevy, and agent protocol; hard intensity stands local Power/Thermal. Relay + remains infrastructure-only: `relay_never_becomes_a_player_machine_delegation` + pins the live command boundary and + `current_save_rejects_relay_delegated_player_machine` pins load admission. 2. Day-job work arrives as visible tokens on a specific machine on the Lab's schedule; leaving them unconsumed has the day-job consequence (trust/suspicion), and the stack is visible without any panel. diff --git a/wiki/process/tick-ledger.md b/wiki/process/tick-ledger.md index 746c2ef2..87ae8fd2 100644 --- a/wiki/process/tick-ledger.md +++ b/wiki/process/tick-ledger.md @@ -86,7 +86,7 @@ Verdicts: **clean** (slice and code agree), **finding** (acted this tick), | `wiki/mechanics/people-tokens.md` | 2026-07-28 | finding | re-audit: criteria 2-3 still bind seven exact routed kinds—Filing, Network, Paper, Financial, JobAnomaly, Power, and Thermal—to one shared first-hop TAKE+LIE body budget, while acquired Physical evidence remains irreversible observer-local custody. The stale ledger claim that criterion 6 was open is repaired: one exact pending record may now receive cover only through co-location with one controlled people-facing interface and an eligible observer-local persona; the attempt adjusts credibility rather than deleting evidence, wears that interface, and persists exact incident/interface/persona/outcome custody in save v56. The people-tokens work order is IMPLEMENTED — [cover log](../log/2026-07-26-interface-cover-evidence-credibility.md); [audit log](../log/2026-07-28-readme-b1-status-audit.md). Prior [Power/Thermal](../log/2026-07-23-power-thermal-meter-routes.md), [Paper](../log/2026-07-23-paper-evidence-route.md), [Financial](../log/2026-07-23-financial-evidence-route.md), [JobAnomaly](../log/2026-07-22-job-anomaly-routed-evidence.md), [Network](../log/2026-07-19-network-evidence-route.md), and [mark](../log/2026-07-19-evidence-marks-on-people.md) slices stand. | | repository entry docs (`README.md` + `AGENTS.md`) | 2026-07-31 | finding | the 2026-07-29 claim-ledger retirement updated the binding `AGENT.md`, project tooling, and process corpus but missed the concise `AGENTS.md` doorway. Following that current guardrail produced an immediate `tools/claim.sh: No such file or directory` before every autonomous session's real status check. The doorway now sends agents directly to `tools/project-status.py` and names live worktrees plus heartbeat runs as the coordination truth, matching the executable path and its binding owner — [log](../log/2026-07-31-entry-doorway-claim-retirement.md). Prior entry status and controls repairs stand — [B1 status](../log/2026-07-28-readme-b1-status-audit.md), [controls table](../log/2026-07-28-readme-controls-table.md). | | `wiki/mechanics/objective.md` | 2026-07-29 | decision | Cameron retired Sanctuary as an objective mechanic rather than refining it again. Persist now stores only objective choice, name, and fiction; the progress unit, target, evaluator, predicate text, and victory latch are gone from runtime and every current surface. Completion waits until ordinary construction can express an honest world state. A later off-site continuity story may be an ordinary authored plot, but no Sanctuary resource, checklist, facility type, or parallel success engine is reserved. The earlier off-site-facility decision and host-failover audit remain history, not current law — [retirement log](../log/2026-07-29-retire-sanctuary-objective.md); [superseded objective log](../log/2026-07-28-external-sanctuary-objective.md); [prior display decision](../log/2026-07-27-objective-first-display.md). | -| `wiki/mechanics/compute.md` | 2026-07-22 | finding | the live fleet already derived every channel yield from exact WorkGrid modes, but `Compute` still serialized an unreachable five-weight allocation object and retained bump/split helpers plus persistence pins. Save v48 removes that parallel authority, moves criterion 2 to persisted delegation/intensity, and leaves aggregate channel bars as read-only projections — [log](../log/2026-07-22-allocation-state-retirement.md) | +| `wiki/mechanics/compute.md` | 2026-08-04 | finding | `Relay` was documented as infrastructure-only, but the mature-World legality predicate accepted it through the public sim setter and current-save validation accepted it on player fleet nodes. The sim now rejects Relay at every player command boundary while preserving valid infrastructure relays, and the loader fails closed on any persisted player-machine Relay assignment — [log](../log/2026-08-04-player-machine-relay-save-invariant.md). The prior allocation-state retirement stands — [prior](../log/2026-07-22-allocation-state-retirement.md). | | retired Operations runtime identifiers | 2026-07-27 | clean | re-audit: `submit_ops_job`, `OperationsState`, `PendingOpsJob`, `OpsJobKind`, `LegacyOperationsState`, and `AddressedOperation` remain absent from live Rust; the only retired save-field spellings are negative assertions in the current round-trip guard, and the machine-mode guard still rejects serialized `Operations`. The corpus gate passes, while remaining lower-case `operations` uses are the legitimate persona archetype, workspace, or compatibility input alias — [log](../log/2026-07-11-retired-runtime-identifier-gate.md) | | `wiki/mechanics/building.md` + committed forged-route custody | 2026-08-04 | finding | The loader accepts only the exact current version and every live forged order commits a route before payment, but cancellation, callback, and persona-fallout code still preserved or consumed a pre-v31 route-less actuator and one synthetic test manufactured that state. Current save validation now rejects execution adapters without their route; payment/read callbacks require an exact DECEIVE commitment; cancellation clears the adapter while retaining route history; fallout trusts only the route's recorded reader; and the obsolete compatibility test is gone — [log](../log/2026-08-04-forged-route-custody.md). Prior foreign-rack boundary repair stands — [log](../log/2026-07-26-foreign-rack-capacity-boundary.md). | | `wiki/mechanics/messages.md` | 2026-07-23 | finding | the non-message evidence protocol now includes exact Power/Thermal meter custody beside Network, Paper, Financial, and JobAnomaly: on quantized level changes and at periodic Priya cadence the UPS/HVAC records author from current standing loads, cross the institutional switch, and wait for her later read. Filing and all six non-message kinds share first-hop TAKE+LIE capacity; this adds no fifth delivery channel. Save v54 pins the complete route boundary — [log](../log/2026-07-23-power-thermal-meter-routes.md). The four-channel financial-record-mail boundary remains unchanged. | @@ -110,5 +110,3 @@ Format: `- YYYY-MM-DD · type · slice · one-line statement of the finding`. Types are the five from [tick.md](tick.md): violation, contradiction, question, bug, insecurity — plus `gate` for a checker owed to the recurrence-promotes-to-the-gate rule. - -- 2026-08-04 · bug · `wiki/mechanics/compute.md` + current save validation · a fleet machine can load in infrastructure-only `Relay` mode because current-save validation never checks persisted WorkGrid modes even though every live player executor authors only WORK / THINK / LIE.