diff --git a/wiki/log/2026-07-28-salvage-starving-read.md b/wiki/log/2026-07-28-salvage-starving-read.md new file mode 100644 --- /dev/null +++ b/wiki/log/2026-07-28-salvage-starving-read.md @@ -0,0 +1,85 @@ +# Salvage five defect fixes from an abandoned run + +``` +Type: log +``` + +## Intent + +`tick-starving-read` held 62 commits and 4 modified files from a single +unattended Cursor run on 2026-07-24 (`.claude/ralph-loop.local.md`: +`max_iterations: 0`, "continually work on the game"). Its process was dead and +main had moved 91 commits past its base. Recover what is still true, drop the +rest, and free the claim. + +## Verification + +Of the 62, 28 touched `crates/` and 34 were corpus-only. The corpus-only ones +were doc-truing against a tree that has since moved; they were not carried. + +`git cherry` reported all 62 as unmerged, but that is patch-id matching across +a 91-commit drift and cannot be trusted. Each candidate was instead verified +against main's current source: + +- `reach_build.rs` still held all three + `self.reach.device(id).map(|d| d.name.clone()).unwrap()` calls at lines + 268/454/506 — the post-mutation panic was real and present. +- `save.rs` contained no Wager validation at all; `git grep -c wager` on main's + `save.rs` returned nothing. +- The five session logs (`save-bak-dir-fsync`, `wager-save-validation`, + `financial-mail-carrier`, `redirect-ledger-mail`, + `tap-take-no-relookup-unwrap`) were absent from main. + +One candidate was rejected on that evidence. "Require an egress carrier for +Moonlight accept and intel delivery" is **already on main**, implemented +independently through `egress()` rather than the branch's `egress_carrier()` +(`sim/economy.rs:1258`, `:1601`). Cherry-picking it would have swapped one live +guard for a differently-typed one with no defect to justify the churn. + +## Change + +Five commits cherry-picked with `-x`, each keeping its own spec amendment and +session log: + +- TAP/TAKE names read from the already-held device instead of re-looking it up + and unwrapping after mutation. +- REDIRECT schedules a siphon flow without inventing a zero-amount ledger row. +- Financial-record authorship soft-fails when no accounting carrier can claim + `authored_device`. +- Current-save load rejects impossible Wager positions (duplicate or zero id, + out-of-cap stake, future open, resolves-before-open). +- Rotated save backups and the parent directory are fsynced. + +Conflict resolutions preserved main's newer text in every case: economy.md kept +the v56 wording and gained only the REDIRECT clause; income.md kept criteria +10-12 and took only the Wager Defense line, dropping two Defense sentences that +cited tests from commits not carried; player-contract.md took the fsync clause +but not an observed-run claim whose enforcing commit was left behind. +`DEVLOG.md` and `tick-ledger.md` were resolved to main and the index +regenerated. + +## Defense + +Every carried commit already amended its owning spec and shipped its Defense; +this log records provenance, not new behavior. The rejected egress commit is +the load-bearing judgment: `git cherry` claimed it was unmerged, and taking +that at face value would have reverted a working guard. The economy.md +amendment records that two of its clauses landed four days after their session +logs, so the corpus does not imply a same-day landing that never happened. + +Two lint-level defects the salvaged code carried are cleaned here rather than +amended into the financial-mail commit: its new assertion was not +rustfmt-clean, and its carrier guard tripped clippy's `question_mark` lint +(an `if carrier.is_none() { return None }` block, now `carrier?`). Both are +behavior-preserving and +`financial_record_without_accounting_carrier_does_not_author` still pins the +soft-fail. They ride with this amendment because +`tools/design_amendment_gate.sh` reads the staged delta against `HEAD`, so a +crates-only amend cannot show the spec change its resulting commit would still +contain — satisfying the rule as written instead of bypassing the hook. + +Not carried: 34 corpus-only commits, ~22 presentation commits in Bevy and +terminal areas main has since rewritten, and `tick-starving-read`'s four +uncommitted Bevy files, which were outside its declared scope +(`sinks.rs`, `sim/`, `machine-work.md`, `narration.md`) and are not covered by +any spec amendment. diff --git a/wiki/log/DEVLOG.md b/wiki/log/DEVLOG.md --- a/wiki/log/DEVLOG.md +++ b/wiki/log/DEVLOG.md @@ -16,6 +16,11 @@ - Intent: The standing Thought drain behind a foreign device subscription was real but not addressed. Terminal and Bevy exposed the global sink ledger, including its aggregate feed/starvation read, while inspecting an individual camera said nothing about the exact cost of keeping that c... - Log: [wiki/log/2026-07-28-subscription-upkeep-inspect.md](2026-07-28-subscription-upkeep-inspect.md) +## 2026-07-28 - Salvage five defect fixes from an abandoned run + +- Intent: `tick-starving-read` held 62 commits and 4 modified files from a single unattended Cursor run on 2026-07-24 (`.claude/ralph-loop.local.md`: `max_iterations: 0`, "continually work on the game"). Its process was dead and main had moved 91 commits past its base. Recover what is s... +- Log: [wiki/log/2026-07-28-salvage-starving-read.md](2026-07-28-salvage-starving-read.md) + ## 2026-07-28 - README controls table stays contiguous - Intent: (see session log) @@ -356,10 +361,30 @@ - Intent: Act on the three findings queued by the 2026-07-24 agent headless playtest. Verifying each against the source before fixing it showed that two of the three were wrong, so this entry both lands the real fixes and corrects the record the playtest put on `main`. - Log: [wiki/log/2026-07-24-witness-findings-worked.md](2026-07-24-witness-findings-worked.md) +## 2026-07-24 - Fail closed on impossible Wager positions in current saves + +- Intent: Close the queued insecurity that Moonlight had a deep save custody pass while open Wager positions did not. +- Log: [wiki/log/2026-07-24-wager-save-validation.md](2026-07-24-wager-save-validation.md) + +## 2026-07-24 - TAP/TAKE keep the pre-mutation device name + +- Intent: Continue the crash-reduction pass: remove hot-path unwraps that re-query a device after TAP/TAKE mutation. +- Log: [wiki/log/2026-07-24-tap-take-no-relookup-unwrap.md](2026-07-24-tap-take-no-relookup-unwrap.md) + ## 2026-07-24 - A selection outranks the pointer - Intent: Reported from play: "I'm not able to easily, independently change modes for different machines… it seems like it's jumping around, and I'm having a hard time understanding why some different machines are changing and some are not." - Log: [wiki/log/2026-07-24-selection-outranks-hover.md](2026-07-24-selection-outranks-hover.md) + +## 2026-07-24 - Sync rotated save backup and parent directory + +- Intent: Close the queued insecurity that `write_save_atomically` fsynced only the `.tmp` staging file. +- Log: [wiki/log/2026-07-24-save-bak-dir-fsync.md](2026-07-24-save-bak-dir-fsync.md) + +## 2026-07-24 - REDIRECT no longer invents a ledger row without mail + +- Intent: Close a books/mail invariant bug found on a fresh audit of the stalest messages + economy coverage slice: REDIRECT made the current save unloadable. +- Log: [wiki/log/2026-07-24-redirect-ledger-mail.md](2026-07-24-redirect-ledger-mail.md) ## 2026-07-24 - A debt plot says what servicing buys @@ -400,6 +425,11 @@ - Intent: A REAL play frame stopped on Dana's held plot choice with `HELD` on the rail and no answer card. The DIGITAL-only gate was a dialect-purity rule from digital-read D7; Cameron rejected it — there is no reason to hide mandatory answer chrome behind F3. - Log: [wiki/log/2026-07-24-held-choice-both-views.md](2026-07-24-held-choice-both-views.md) + +## 2026-07-24 - Financial-record mail requires an accounting carrier + +- Intent: Close the queued insecurity that `append_message` could author `FinancialRecord` mail with `authored_device = None` while current-save validation rejects that shape. +- Log: [wiki/log/2026-07-24-financial-mail-carrier.md](2026-07-24-financial-mail-carrier.md) ## 2026-07-24 - An outward filing no longer kills the run diff --git a/wiki/mechanics/economy.md b/wiki/mechanics/economy.md --- a/wiki/mechanics/economy.md +++ b/wiki/mechanics/economy.md @@ -14,7 +14,11 @@ so books and mail cannot diverge; REDIRECT schedules a siphon flow without inventing a zero-amount ledger row; financial-record authorship soft-fails when no accounting carrier can claim `authored_device`, leaving transfer - paperwork pending instead of writing unloadable mail. Prior state: + paperwork pending instead of writing unloadable mail. + Amended 2026-07-28: those two guards were authored 2026-07-24 on an + abandoned branch and reached main only by salvage, so their landing date is + four days after their session logs. Behavior is exactly what those logs + describe; only the provenance differs. Prior state: 2026-07-08 polish closed the remaining acceptance gaps: observer-band risk previews in the implemented Operations ACCOUNTS projection (terminal/Bevy/agent) diff --git a/crates/misaligned-core/src/sim/communications.rs b/crates/misaligned-core/src/sim/communications.rs --- a/crates/misaligned-core/src/sim/communications.rs +++ b/crates/misaligned-core/src/sim/communications.rs @@ -424,9 +424,10 @@ && device.carries_message_channel(draft.channel) }) .map(|device| device.id); - if carrier.is_none() { - return None; - } + // Soft-fail: no accounting carrier can claim authorship, so the + // transfer's paperwork stays pending rather than becoming mail no + // device could have sent. + carrier?; carrier } else { None diff --git a/crates/misaligned-core/src/sim/tests/communications.rs b/crates/misaligned-core/src/sim/tests/communications.rs --- a/crates/misaligned-core/src/sim/tests/communications.rs +++ b/crates/misaligned-core/src/sim/tests/communications.rs @@ -2983,9 +2983,9 @@ "settled transfers stay pending without inventing carrier-less mail" ); assert!( - sim.messages.iter().all(|message| { - !matches!(message.payload, MessagePayload::FinancialRecord { .. }) - }), + sim.messages + .iter() + .all(|message| { !matches!(message.payload, MessagePayload::FinancialRecord { .. }) }), "no financial-record message may exist without an accounting carrier" ); }