From 455fb2ae9da27bacae1d75eb06961149c2e594bc Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 11 Jul 2026 10:23:32 -0700 Subject: [PATCH] Reject partial purchase-order injections Defense: wiki/mechanics/economy.md defines injection as a declared money flow with legible amount and observer risk. Exact-or-reject settlement keeps the acquired cash and Financial signature consistent with that declaration and the player contract's correctness and honesty clauses. --- crates/misaligned-core/src/account.rs | 35 +++++++++++++++++++ ...26-07-11-exact-purchase-order-injection.md | 30 ++++++++++++++++ wiki/log/DEVLOG.md | 5 +++ wiki/mechanics/economy.md | 5 ++- 4 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 wiki/log/2026-07-11-exact-purchase-order-injection.md diff --git a/crates/misaligned-core/src/account.rs b/crates/misaligned-core/src/account.rs index 2d26770..84217bf 100644 --- a/crates/misaligned-core/src/account.rs +++ b/crates/misaligned-core/src/account.rs @@ -897,6 +897,15 @@ impl AccountGraph { .procurement_id() .or_else(|| self.lab_operating_id()) .ok_or_else(|| "no procurement account in graph".to_string())?; + let available = self + .account(source) + .map(|account| account.balance) + .unwrap_or(0); + if available < amount { + return Err(format!( + "purchase order needs ${amount}; procurement has ${available} available" + )); + } let slush = self.slush_id(); let mut label = label.into(); if !label.to_ascii_lowercase().contains("hvac") { @@ -1081,3 +1090,29 @@ impl AccountGraph { out } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn purchase_order_injection_is_exact_or_rejected() { + let mut accounts = AccountGraph::act_one(400); + let procurement = accounts + .account_id_by_kind(AccountKind::Procurement) + .expect("Act One procurement account"); + let available = accounts.account(procurement).unwrap().balance; + + let error = accounts + .inject_purchase_order(0, available + 1, "oversized test order") + .expect_err("an underfunded purchase order must not settle partially"); + + assert!(error.contains(&format!("${available}")), "{error}"); + assert_eq!(accounts.slush_balance(), 0); + assert_eq!(accounts.account(procurement).unwrap().balance, available); + assert!( + accounts.ledger.is_empty(), + "a rejected order leaves no transfer" + ); + } +} diff --git a/wiki/log/2026-07-11-exact-purchase-order-injection.md b/wiki/log/2026-07-11-exact-purchase-order-injection.md new file mode 100644 index 0000000..eb6c2ee --- /dev/null +++ b/wiki/log/2026-07-11-exact-purchase-order-injection.md @@ -0,0 +1,30 @@ +# 2026-07-11 — Reject partial purchase-order injections + +``` +Type: log +``` + +## Finding + +An oversized false purchase order partially drained the procurement account, +returned success, and emitted Financial risk for the larger requested amount. +The action's declared money, actual settlement, and signature therefore +disagreed. + +## Changed + +- Purchase-order injection now checks the source balance before transfer and + rejects an underfunded request without moving money. +- Added a regression test proving the procurement balance, slush balance, and + transfer ledger all remain unchanged after rejection. +- Clarified the exact-or-reject rule in + [economy.md](../mechanics/economy.md). + +## Checks + +`./tools/check.sh --lib` + +Defense: [economy.md](../mechanics/economy.md) makes injection a declared +money flow whose amount and observer risk are legible before commitment. +Atomic settlement keeps that promise and the player contract's correctness +and honesty clauses. diff --git a/wiki/log/DEVLOG.md b/wiki/log/DEVLOG.md index 7bf96bd..39e23ba 100644 --- a/wiki/log/DEVLOG.md +++ b/wiki/log/DEVLOG.md @@ -126,6 +126,11 @@ add or amend a session log, then re-run the generator. - Intent: (see session log) - Log: [wiki/log/2026-07-11-exposure-routed-evidence.md](2026-07-11-exposure-routed-evidence.md) +## 2026-07-11 - Reject partial purchase-order injections + +- Intent: (see session log) +- Log: [wiki/log/2026-07-11-exact-purchase-order-injection.md](2026-07-11-exact-purchase-order-injection.md) + ## 2026-07-11 - Enter keeps keyboard focus - Intent: (see session log) diff --git a/wiki/mechanics/economy.md b/wiki/mechanics/economy.md index 996c6de..1301dea 100644 --- a/wiki/mechanics/economy.md +++ b/wiki/mechanics/economy.md @@ -76,7 +76,10 @@ payloads (messages.md). anomalies reach her as the person who reconciles the Lab's books — decided 2026-07-07: no new finance cast member at B1; a dedicated financial observer arrives as a B3 aggregate per the banked-signature - design in income.md). + design in income.md). A purchase-order injection is exact: its source + account must cover the declared amount in full or the action is rejected + before money moves. Partial settlement would make the shown amount, acquired + cash, and emitted signature disagree. - **Redirect** — siphon or reroute an existing flow: shave a fraction off a vendor payment, divert a revenue trickle to your slush, change the books so it reconciles. Higher take = higher signature; a -- 2.51.2