diff --git a/crates/misaligned-core/src/save.rs b/crates/misaligned-core/src/save.rs index 6d82825d..30529512 100644 --- a/crates/misaligned-core/src/save.rs +++ b/crates/misaligned-core/src/save.rs @@ -618,6 +618,9 @@ fn validate_current_save(mut state: SaveState) -> Result { state.detection.validate_topology()?; validate_persona_custody(&state)?; validate_messages(&state)?; + 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 state .last_facility_meter_levels .iter() @@ -3854,6 +3857,24 @@ mod tests { ); } + #[test] + fn current_save_rejects_active_day_job_after_pilot_failure() { + let mut state = SaveState::from_sim(&Sim::with_seed(0xDA7A_10B0)); + state.dayjob.pilot_failed = true; + state.dayjob.active = Some(crate::dayjob::Job { + started: state.sim_tick, + deadline: state.sim_tick + 100, + band_lo: 2.0, + band_hi: 8.0, + quality: 0.0, + }); + + assert_eq!( + validate_current_save(state).unwrap_err(), + "current-version save keeps an active day job after pilot failure" + ); + } + /// 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/wiki/log/2026-08-04-day-job-terminal-save-invariant.md b/wiki/log/2026-08-04-day-job-terminal-save-invariant.md new file mode 100644 index 00000000..9f15756b --- /dev/null +++ b/wiki/log/2026-08-04-day-job-terminal-save-invariant.md @@ -0,0 +1,20 @@ +# 2026-08-04 — Reject active work after pilot failure + +``` +Type: log +``` + +## Finding + +The live deadline path clears the active Voss packet before the fourth strike +sets `pilot_failed` and ends the run. Current-save validation did not preserve +that transition boundary: edited or corrupted v63 bytes could load both +`pilot_failed` and an active job, leaving frontends to project work after its +terminal state. + +## Repair + +The current-save gate now rejects that impossible combination. The focused +regression begins with an otherwise current `SaveState`, introduces exactly the +forbidden active terminal pairing, and pins the fail-closed error. The day-job +spec now states the persisted invariant beside its round-trip criterion. diff --git a/wiki/log/DEVLOG.md b/wiki/log/DEVLOG.md index 7201d986..38d5d55f 100644 --- a/wiki/log/DEVLOG.md +++ b/wiki/log/DEVLOG.md @@ -66,6 +66,11 @@ add or amend a session log, then re-run the generator. - Intent: (see session log) - Log: [wiki/log/2026-08-04-decision-index-generation-defense.md](2026-08-04-decision-index-generation-defense.md) +## 2026-08-04 - Reject active work after pilot failure + +- Intent: (see session log) +- Log: [wiki/log/2026-08-04-day-job-terminal-save-invariant.md](2026-08-04-day-job-terminal-save-invariant.md) + ## 2026-08-04 - Keep Bevy palette roles on one authority - Intent: (see session log) diff --git a/wiki/mechanics/day-job.md b/wiki/mechanics/day-job.md index 55694533..73db099c 100644 --- a/wiki/mechanics/day-job.md +++ b/wiki/mechanics/day-job.md @@ -180,7 +180,9 @@ attention, but it is a mirror of machine state, not the source of the work. triggering from sustained sandbagging. 4. A machine left in day-job mode keeps working hands-off at its persistent intensity; no cursor presence or declared target is required. -5. Save/load round-trips jobs, trust, attention, and the pilot clock. +5. Save/load round-trips jobs, trust, attention, and the pilot clock. A failed + pilot is terminal day-job state: no active job may coexist with + `pilot_failed` in a current save. 6. The active job is inspectable at the host rack with the same facts the panel shows plus its visible demand stack. Thermal/Power measurements carry that host location into the exact facility-meter record; JobAnomaly carries @@ -190,3 +192,7 @@ attention, but it is a mirror of machine state, not the source of the work. 7. Light / medium / hard host intensity changes the delivered rate and can therefore produce sandbag / meet / excel from the same job and hardware. Both frontends expose the focused/selected control and surface the state. + +Defense: `current_save_rejects_active_day_job_after_pilot_failure` proves that +the current-save gate rejects an active packet after the live deadline path has +cleared it and ended the pilot. diff --git a/wiki/process/tick-ledger.md b/wiki/process/tick-ledger.md index 1b5f1024..746c2ef2 100644 --- a/wiki/process/tick-ledger.md +++ b/wiki/process/tick-ledger.md @@ -74,7 +74,7 @@ Verdicts: **clean** (slice and code agree), **finding** (acted this tick), | `wiki/interface/context-menu.md` | 2026-07-29 | finding | shared legality, executable-only human rows, local-vs-strategic ownership, global F3 precedence, and exact person-entry paths still verify. The root nevertheless repeated `PROPOSE LINK TO …` once per legal target, turning one intention into topology-sized noise. The shared human menu now folds that fanout into one `PROPOSE A LINK` row and a live exact `WHERE SHOULD THIS CONNECT?` page; terminal and Bevy re-query the same page while agent mode keeps flat exact commands — [log](../log/2026-07-29-link-destination-fold.md). The prior F3 repair remains pinned — [log](../log/2026-07-26-bevy-global-f3-input-precedence.md). | | `wiki/mechanics/personas.md` | 2026-07-28 | finding | criterion 6 remains honestly incomplete: grants persist and revoke but still create no resource, permission, graph edge, or owner-system route. The adjacent current-save boundary trusted that public ledger without graph validation, allowing duplicate persona ids, stale id cursors, or dangling relationship/grant/expectation/action custody to reach first-match runtime lookups. Loading now validates the complete PersonaWorld id/reference/time graph, one-to-one grant/expectation pairing, serialized grant protocol, and active-grant uniqueness before exposing the state; this hardens existing custody without promoting criterion 6 or 6b — [save audit](../log/2026-07-28-persona-save-custody-audit.md). Prior [grant-topology](../log/2026-07-18-persona-grant-topology-audit.md) and [observer-integrity](../log/2026-07-18-persona-observer-integrity.md) findings stand. | | `wiki/interface/views.md` + representation docs | 2026-07-26 | finding | criterion 1's frontend-only representation and state-parity contract still stands, but Bevy's context-menu precedence made F3 unavailable while that modal attention state was open even though terminal preserved it. F3 now routes once before every post-opening modal branch, keeps the exact menu/Operations/held-choice state, and changes no simulation or save bytes — [log](../log/2026-07-26-bevy-global-f3-input-precedence.md) | -| `wiki/mechanics/day-job.md` | 2026-07-22 | finding | under/over-band JobAnomaly no longer enters Detection.pending: the day-job result authors one exact record at the host machine/site/device and schedules Voss's route and cadence read. Strikes and other outcome effects remain immediate; route-local LIE or recruited-handler suppression may stop only the unread evidence record — [log](../log/2026-07-22-job-anomaly-routed-evidence.md). The prior band-ramp, cadence, origin-lean, last-chance, and three shipped trust-unlock findings remain valid. | +| `wiki/mechanics/day-job.md` | 2026-08-04 | finding | current-save validation accepted `pilot_failed` with an active job even though the live deadline path clears the packet before setting terminal pilot state. The loader now rejects that impossible active terminal combination, preventing frontends from projecting work after the pilot has ended — [log](../log/2026-08-04-day-job-terminal-save-invariant.md). Prior routed-evidence, band-ramp, cadence, origin-lean, last-chance, and trust-unlock findings remain valid — [prior](../log/2026-07-22-job-anomaly-routed-evidence.md). | | `wiki/mechanics/core.md` | 2026-07-29 | finding | the queued liveness follow-up is closed: host loss now filters synchronized targets to currently online machines before ranking freshness, so a dark staged target cannot become host or produce repeated failover receipts; no synchronized live target ends the run — [liveness log](../log/2026-07-29-live-b1-fallback-selection.md). The prior B1/B2 boundary remains exact: the free-cadence `last_sync` is only a check-in, current-state host failover restores no snapshot, and completed-image rollback remains deferred — [boundary log](../log/2026-07-29-b1-host-failover-boundary.md). There is still no Thought-backed project, partial progress, heat, saved project state, sidebar ETA/cost/target, source-liveness resolver, or capability-shaped eligibility — [project-status log](../log/2026-07-28-core-criteria-status-audit.md). | | `wiki/mechanics/cursor.md` | 2026-07-28 | finding | re-audit: cursor state remains frontend-only; sight, hearing, fog precedence, remembered snapshots, blueprint opacity, telemetry, provenance, identity gates, and cold signal pings still match the implemented contract. Criterion 2 explicitly required a before/after simulation-state-hash proof for arbitrary cursor movement, but the only existing hash tests began after cursor placement and proved F3 view immutability instead. Terminal and Bevy now sweep every map coordinate through their production cursor helpers, pin edge clamping, and require unchanged simulation save-state hashes — [log](../log/2026-07-28-cursor-immutability-defense.md) | | `wiki/mechanics/intel.md` | 2026-08-04 | finding | the queued current-save follow-up is closed: historical v24/v30 transitions now remain statement-local retired history, criterion 7 states exact-current round-trip truth, and the save-claim gate rejects unversioned live migration prose plus the numbered `collapses` synonym — [save-boundary log](../log/2026-08-04-intel-current-save-boundary.md). The full audit's player-facing repair also stands: core emits criterion 12's exact conditional **OLDER INFORMATION WILL BE LOST IF MORE ARRIVES**, with a full-buffer projection regression for all consumers — [bounded-loss log](../log/2026-08-04-intel-bounded-loss-language.md). Prior Storage B, recursive custody, magnitude, and consequence-first findings stand. | @@ -111,5 +111,4 @@ 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/day-job.md` + current save validation · a current save can combine `pilot_failed` with an active day job even though live resolution clears the job before terminal failure, leaving frontends to project an impossible active terminal state. - 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.