# Audit report

> AUDIT the imdUSD v2 keeper, an off-chain Node process with NO on-chain rights: it calls permissionless functions, pays the IdentityMD plane for oracle answers from its own IMD, and is report-only by default, at the pinned commit.
>
> imdUSD v2 is a multi-collateral CDP stablecoin; this is its capped mainnet beta on Ethereum (Robinhood Chain 4663 follows). README.md gives scope, invariants, trust model and known issues: read it first. Earlier public waves: GET https://api.imd.fun/jobs/<id>/submissions (wave 1 bd0ce553 oracles, wave 2 5a8d427d prices, wave 3 ff9ebb9d prices).
>
> BUILD AND TEST (required; Node 20+): `npm ci && npm test` (unit tests with fakes, no network). Write each proof as a test under test/ (node:test), run it with `node --test <file>`, paste it with its output, then delete it. The fork suites (test/fork: an archive RPC, anvil and the contracts repo) are optional; say if you ran them.
>
> PROPERTIES:
> P1 Every duty happens within its bound (depth: samples every hour and a poke every UTC day; seat tick hourly while an auction runs; liquidation within one 60 s pass; a proof past 1.5% drift or within the margin before maxAge; a fall answer once minSecondsBetweenFalls has passed) while its RPCs and the plane answer; when they do not, an alert fires before the protocol is harmed.
> P2 The keeper never spends more IMD than its cap, never pays twice for one order, never buys a sIMD median answer except on a fall with a loan at risk (lib/feeds.mjs rules 1-2; NHI and seat legs are bought on schedule, bounded by the cap and reserveForFalls), and never pays any address but the pinned payTo.
> P3 A send whose outcome is unknown (timeout, replaced, dropped, reorged) is never re-sent in a way that acts twice or burns fees without bound; one writer per key.
> P4 No alert is cleared by a failed read: could-not-tell is never reported as fine.
> P5 Report-only mode sends no transaction and pays nothing.
>
> THREAT MODEL: a hostile or failing RPC controls what the keeper reads (stale, wrong, partial, disagreeing); a hostile plane or a man in the middle controls quotes, payment requests and answers; the ENS record identitymd.eth imd.servers chooses the plane server that receives PLANE_BEARER and payments; a compromised attester or a wrong panel majority controls the answers the keeper relays; a searcher or block builder controls the ordering and inclusion of keeper transactions within and across blocks (and can sandwich a public sample); an on-chain griefer controls the permissionless calls the keeper races (prove the same block, tick, poke); the operator controls config.js and may run a second keeper process on the same key with another STATE_DIR.
>
> PATH: keeper.mjs loops -> lib/* duties -> the contracts at github.com/fa11up/imdusd-v2-audit @ 67eadeac1e67db5f562e03eab8432a435debfb79 (ABIs in lib/deployment.mjs; src/Pricer.sol quote() decides what the feeds duty must buy) via Multicall3; payments lib/orders.mjs + lib/x402.mjs to api.imd.fun (Permit2, one capped approval); crash safety lib/journal.mjs. The plane is exercised only against a mock in the tests.
>
> CLAIMS TO CHECK: confirm or refute each with a test.
> C1 README 'Not proven' is complete: nothing else is assumed without a test;
> C2 payTo is pinned and a quote naming another payee is refused;
> C3 a paid order the plane then refuses holds all buying;
> C4 the send journal makes a crash between send and receipt safe;
> C5 after a real fall sIMD's liquidation price is max(median, proven spot) (src/Pricer.sol twin rule), so liquidation waits on the SLOWER leg: the keeper's median purchase (at-risk gate, 30 min gap, plane admission, the epoch walk) or a proof of a newer block (lib/feeds.mjs proves when the Pricer reports the legs apart). Measure the end-to-end delay of each order of events against a fall fast enough to leave bad debt;
> C6 the plane-health hold (lib/plane.mjs) always ends: a quiet or hostile plane cannot hold buying forever;
> C7 a failed read in the depth round does not cost that hour's samples (lib/depth.mjs runDepth);
> C8 a plane answering 'expired' or 'payment_failed' after payment cannot release a hold or lead to a second payment (lib/orders.mjs);
> C9 a corrupt or unreadable state file (orders.json, imd-spend.json, alerts.json) never reads as nothing held or nothing spent (lib/state.mjs).
>
> SOUNDNESS: one process, an hourly depth second chosen at random, report-only until switched on. For each duty, can this design meet P1 at all, or must the protocol not depend on it (say which, and what it would take)?
>
> PROOF STANDARD: tests with fakes over interleavings: timeouts, replaced and dropped transactions, reorgs, failing or disagreeing reads, plane errors, crashes between steps. Fuzz the stated rules over random orderings of events (spend caps, one payment per order, falls only, one writer), with node:test or fast-check.
>
> Answer each numbered question:
> 1. (P2) Spending: find a path that pays twice, pays past the cap, buys outside the rules, or pays another address (quote, Permit2 witness, retries, refused-then-paid orders, crash between pay and record).
> 2. (P3) Sends: replaced, dropped and stuck transactions, nonce gaps, fee bumps, two loops or two processes on one key sending at once, a crash mid-send. Find a double act or unbounded fee spend.
> 3. (P1, P4) Liveness and alerts: for each duty, what stops it silently? Find a failed read or an RPC disagreement that clears an alert or reports fine.
> 4. (P1) Duties against the contracts: seat ticks, depth rounds with proofs, liquidations (minSeized), the twin-rule prove, Allocator rules. Find a duty that reads the contracts wrongly (ABI, units, stale block) or acts on a stale decision. Does fallDecision's reference (lib/feeds.mjs) equal Pricer.quote's low in every case (unconverged leg, stale spot, cap)?
> 5. (P5) Report-only: find any path that sends or pays with --execute off.
>
> Not findings: README 'Only against a mock' and 'Not proven at all' as written (worse than written IS a finding); test/ and mocks.
>
> Every finding: severity by impact; file, line and the lines copied; the call sequence; the failing state, expected vs actual; reachable with the deploy config or not; the smallest fix; and a test that fails on this commit, RUN as BUILD says and pasted with its output (the judge reruns it). Answer every numbered question, including where nothing is wrong.

| | |
|---|---|
| Repository | https://github.com/fa11up/imdusd-v2-keeper-audit.git |
| Commit | `e53d362d9a66490112fa2528edb60737cd75a544` |
| Job | `ae48df88-ca87-40e8-b8c2-5ed739b81de5` |
| Judged | 2026-11-15 05:42 UTC |
| Findings | 2 high · 8 medium · 2 low · 1 info |

Four agents audited the code as it is at `e53d362`, each in one area (math, permissions, economics, control flow),
and a judge reproduced, merged and ranked what they found, then read the code once more itself. Nothing in the repository was changed or deployed.

## Findings

### 1. High: C9 refuted: a corrupt or unreadable orders.json / imd-spend.json reads as nothing held and nothing spent; the next write then destroys the hold

`lib/state.mjs:42`

```
    return existsSync(p) ? JSON.parse(readFileSync(p, "utf8")) : fallback;
  } catch {
    return fallback;
  }
```

readState() returns the caller's fallback on ANY error (JSON parse of a torn/garbled file, EACCES, EIO), not only when the file is absent. lib/orders.mjs loadOrders() falls back to {list: []} (line 30), so lostOrders() is [] and buyHold()'s 'lost'/'refused-paid' hold, which README says holds ALL buying until --clear-lost, disappears; spentToday() (56-59) falls back to 0, so the day's cap restarts. The next update()/saveOrders() then rewrites orders.json from the empty view, erasing the paid-order record for good. alerts.json takes the same path (P4). Violates P2 (cap, one payment per order) and C9. Reachable with the deploy config whenever a state file is present but unreadable (disk error, operator edit, partial restore, permission change). Merged from audit_economics, audit_flow, audit_permissions, audit_math. Smallest fix: return the fallback only when the file does not exist; on any read/parse error throw (or return a sentinel) and make buyHold() hold with a critical 'state-unreadable' alert; update() must refuse to overwrite a file it could not parse.

**Reproduction**

State: orders.json holds one refused-paid order and imd-spend.json 5 IMD today -> buyHold returns 'the plane refused a paid order...'. Overwrite orders.json with '{"list":[{"key":"k","stage":"refused-paid"' and imd-spend.json with '\u0000garbage'. Expected: buyHold holds. Actual: buyHold = null, spentToday = 0, lostOrders = [].

BUILD: npm ci && npm test (72 pass on e53d362). Proof: save the 'proof' source as test/zz-judge.test.mjs and run `node --test test/zz-judge.test.mjs` (node:test, fakes and the repo's mock plane only, no network); the test asserts the correct behaviour and FAILS on this commit. Output on this commit: not ok ... AssertionError [ERR_ASSERTION]: ACTUAL: buyHold=null, spentToday=0, lostOrders=0

TEST:
test("J1 C9: a corrupt orders.json / imd-spend.json must not read as nothing held / nothing spent", async () => {
  reset(); process.env.PLANE_BEARER = "x";
  writeState("orders.json", { list: [{ key: "k", purpose: "fall", stage: "refused-paid", planeOrder: "o1" }] });
  writeState("imd-spend.json", { day: new Date().toISOString().slice(0, 10), imd: utils.parseEther("5").toString() });
  assert.match(await buyHold(holdCtx(), E18.div(2)), /refused a paid order/);
  writeFileSync(statePath("orders.json"), '{"list":[{"key":"k","stage":"refused-paid"');
  writeFileSync(statePath("imd-spend.json"), "\u0000garbage");
  const hold = await buyHold(holdCtx(), E18.div(2));
  assert.notEqual(hold, null, `ACTUAL: buyHold=null, spentToday=${spentToday()}, lostOrders=${lostOrders().length}`);
});

**Proof**: a Foundry test that fails on this code and passes once it is fixed.

```solidity
// JUDGE SCRATCH (deleted after the run). Each test asserts the CORRECT behaviour: a failure here is a reproduced finding.
import { test } from "node:test";
import assert from "node:assert/strict";
import { mkdtempSync, rmSync, writeFileSync, readdirSync, readFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { BigNumber, providers, utils, Wallet } from "ethers";

process.env.STATE_DIR = mkdtempSync(join(tmpdir(), "keeper-judge-"));
const { readState, writeState, statePath, takeLock } = await import("../lib/state.mjs");
const { advanceOrders, buyHold, lostOrders, loadOrders, spentToday, startOrder } = await import("../lib/orders.mjs");
const { planeClient, signPayment, IMD } = await import("../lib/x402.mjs");
const { guardedSigner, send } = await import("../lib/tx.mjs");
const { recordIntent, pendingIntents } = await import("../lib/journal.mjs");
const { runDepth } = await import("../lib/depth.mjs");
const { fallDecision } = await import("../lib/feeds.mjs");
const { plane, nhiFromSwarm } = await import("../lib/plane.mjs");
const { makeAlerts } = await import("../lib/alerts.mjs");
const { runWatch } = await import("../lib/watch.mjs");
const { bodyOf, buildInput, questionHash } = await import("../lib/oracle.mjs");
const { makeMockPlane } = await import("./lib/mock-plane.mjs");

const E18 = BigNumber.from(10).pow(18);
const P = E18.div(250);
const KEY = "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d";
const ATTESTER = "0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a";
const PAY_TO = "0x4e0fa57bde726079356537e2f34d671e9f41adbc";
const holdCtx = () => ({ cfg: {}, planeState: { ok: true }, signer: { address: "0x00000000000000000000000000000000000000ee" },
  provider: { getBalance: async () => E18, getGasPrice: async () => BigNumber.from(10).pow(9) }, book: { token: () => ({ balanceOf: async () => E18.mul(50) }) } });
const reset = () => { for (const f of ["orders.json", "imd-spend.json", "STOP", "alerts.json", "depth.json", "feeds.json"]) rmSync(statePath(f), { force: true }); };

async function flow() {
  reset();
  const clock = { number: 1000, timestamp: 1_800_000_000, hash: `0x${"11".repeat(32)}` };
  const mock = makeMockPlane({ answer: () => ({ value: P }), chain: async () => clock, attesterKey: ATTESTER });
  const base = await mock.listen();
  const wallet = new Wallet(KEY);
  const provider = { getBlock: async () => ({ number: clock.number, timestamp: clock.timestamp }), getBalance: async () => E18, getGasPrice: async () => BigNumber.from(10).pow(9), unpin() {} };
  const leafAddr = "0x00000000000000000000000000000000000000a1";
  const leaf = { address: leafAddr, MIN_PANEL_SIZE: async () => 100, minAgreed: async () => 67, expectedQuestionHash: async (a, b) => questionHash(bodyOf("price"), Number(a), Number(b)) };
  const token = { balanceOf: async () => E18.mul(50), allowance: async () => E18.mul(50) };
  const signer = { address: wallet.address, provider, _signTypedData: (...a) => wallet._signTypedData(...a) };
  process.env.PLANE_BEARER = "local-test-bearer";
  const ctx = { execute: true, cfg: { PLANE: { payTo: PAY_TO } }, provider, signer, keeperAddress: wallet.address, log() {}, planeState: { ok: true, base },
    planeClient: planeClient(base, "local-test-bearer"), book: { token: () => token, leaf: () => leaf }, alerts: { raise: async () => {} } };
  const input = await buildInput(leaf, "price");
  return { ctx, mock, input, leafAddr, close: () => mock.close() };
}
const rounds = async (ctx, n) => { for (let i = 0; i < n; i++) await advanceOrders(ctx); };

test("J1 C9: a corrupt orders.json / imd-spend.json must not read as nothing held / nothing spent", async () => {
  reset(); process.env.PLANE_BEARER = "x";
  writeState("orders.json", { list: [{ key: "k", purpose: "fall", stage: "refused-paid", planeOrder: "o1" }] });
  writeState("imd-spend.json", { day: new Date().toISOString().slice(0, 10), imd: utils.parseEther("5").toString() });
  assert.match(await buyHold(holdCtx(), E18.div(2)), /refused a paid order/);
  writeFileSync(statePath("orders.json"), '{"list":[{"key":"k","stage":"refused-paid"');
  writeFileSync(statePath("imd-spend.json"), "\u0000garbage");
  const hold = await buyHold(holdCtx(), E18.div(2));
  assert.notEqual(hold, null, `ACTUAL: buyHold=null, spentToday=${spentToday()}, lostOrders=${lostOrders().length}`);
});
```

### 2. High: P3 refuted: an urgent send re-bumps a never-mined transaction 12.5% every pass with no fee ceiling and no bump count; one stuck nonce wedges the key

`lib/tx.mjs:162`

```
    for (const it of open) {
      if (!signer.bump) break;
      try {
        const h = await signer.bump(it);
```

send() on the urgent tier, once an open intent is older than URGENT_WAIT_SECONDS (60 s), calls signer.bump() on every open intent each pass. bump() (93-108) re-signs the same calldata with bumpFees() = max(old x 1.125, now) and never compares the result with MAX_GAS_GWEI (the ceiling at 172-177 checks only provider.getGasPrice()), nor counts bumps; recordIntent gives the bump a fresh `at`, so the cycle repeats every minute. A transaction a private relay never includes (SEND_RPC_URLS must be private; revert-protected relays drop a send that now reverts, e.g. a liquidation or prove someone else did first) is never retired or cancelled: its nonce never advances, every later action is signed behind it and cannot mine, and its fees grow geometrically until the node refuses for insufficient funds; if it ever mines, it pays that fee. Expected (P3): bounded bumps under MAX_GAS_GWEI, then a same-nonce cancel and a critical alert. Merged from audit_economics, audit_flow, audit_permissions. Smallest fix: in bump() cap maxFeePerGas/maxPriorityFeePerGas at MAX_GAS_GWEI and stop after N bumps; re-simulate the intent before bumping and, if it reverts or the cap is hit, replace it on the same nonce with a 0-value self-transfer; raise critical 'send-stuck'.

**Reproduction**

Real guardedSigner(Wallet) over a fake provider (base fee 10 gwei, no receipt ever, latest nonce 5, MAX_GAS_GWEI 100), relay stubbed to accept every signed tx; one open intent at nonce 5 (tip 0.3, maxFee 20.3 gwei), aged past 60 s before each of 60 urgent send() passes. Expected: maxFee <= 100 gwei. Actual: tip 351.8 gwei, maxFee 23803.9 gwei, 120 signed transactions relayed, nonce 5 still open.

BUILD: npm ci && npm test (72 pass on e53d362). Proof: save the 'proof' source as test/zz-judge.test.mjs and run `node --test test/zz-judge.test.mjs` (node:test, fakes and the repo's mock plane only, no network); the test asserts the correct behaviour and FAILS on this commit. Output on this commit: not ok ... AssertionError [ERR_ASSERTION]: ACTUAL after 60 urgent passes: nonce 5 tip 351.8 gwei, maxFee 23803.9 gwei (MAX_GAS_GWEI 100), 120 signed txs relayed, still open

TEST:
test("J4 P3: real guardedSigner.bump on a never-mined urgent send: fees stay under MAX_GAS_GWEI", async () => {
  reset();
  for (const f of pendingIntents()) rmSync(statePath(`tx/${f.nonce}.json`), { force: true });
  let relayed = 0;
  providers.StaticJsonRpcProvider.prototype.sendTransaction = async (signed) => { relayed += 1; return { hash: utils.keccak256(signed) }; };
  const gwei = (n) => utils.parseUnits(String(n), "gwei");
  const provider = { getBlock: async () => ({ baseFeePerGas: gwei(10) }), getTransactionReceipt: async () => null, getTransactionCount: async () => 5, getGasPrice: async () => gwei(10), getBalance: async () => E18 };
  const cfg = { SEND_RPC_URLS: ["https://relay.invalid"], MAX_GAS_GWEI: 100, URGENT_WAIT_SECONDS: 60 };
  const w = guardedSigner(new Wallet(KEY), provider, cfg, "tx");
  const signed = await w.signTransaction({ type: 2, chainId: 1, nonce: 5, to: PAY_TO, data: "0x", value: 0, gasLimit: 300000, maxPriorityFeePerGas: gwei(0.3), maxFeePerGas: gwei(20.3) });
  recordIntent({ nonce: 5, hash: utils.keccak256(signed), signed, label: "bite SIMD #1" });
  const proxy = { provider, address: w.address, rebroadcast: w.rebroadcast, bump: w.bump, journalScope: "tx" };
  const timeout = () => ({ hash: "0xnew", wait: async () => { const e = new Error("t"); e.code = "TIMEOUT"; throw e; } });
  for (let pass = 0; pass < 60; pass++) {
    const p = statePath("tx/5.json"); const it = JSON.parse(readFileSync(p, "utf8")); it.at = new Date(Date.now() - 61_000).toISOString(); writeFileSync(p, JSON.stringify(it));
    await send(proxy, "bite SIMD #1", async () => {}, async () => timeout(), cfg, { tier: "urgent" });
  }
  const t = utils.parseTransaction(JSON.parse(readFileSync(statePath("tx/5.json"), "utf8")).signed);
  const max = Number(utils.formatUnits(t.maxFeePerGas, "gwei")), tip = Number(utils.formatUnits(t.maxPriorityFeePerGas, "gwei"));
  assert.ok(max <= 100, `ACTUAL after 60 urgent passes: nonce 5 tip ${tip.toFixed(1)} gwei, maxFee ${max.toFixed(1)} gwei (MAX_GAS_GWEI 100), ${relayed} signed txs relayed, still open`);
});

**Proof**: a Foundry test that fails on this code and passes once it is fixed.

```solidity
// JUDGE SCRATCH (deleted after the run). Each test asserts the CORRECT behaviour: a failure here is a reproduced finding.
import { test } from "node:test";
import assert from "node:assert/strict";
import { mkdtempSync, rmSync, writeFileSync, readdirSync, readFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { BigNumber, providers, utils, Wallet } from "ethers";

process.env.STATE_DIR = mkdtempSync(join(tmpdir(), "keeper-judge-"));
const { readState, writeState, statePath, takeLock } = await import("../lib/state.mjs");
const { advanceOrders, buyHold, lostOrders, loadOrders, spentToday, startOrder } = await import("../lib/orders.mjs");
const { planeClient, signPayment, IMD } = await import("../lib/x402.mjs");
const { guardedSigner, send } = await import("../lib/tx.mjs");
const { recordIntent, pendingIntents } = await import("../lib/journal.mjs");
const { runDepth } = await import("../lib/depth.mjs");
const { fallDecision } = await import("../lib/feeds.mjs");
const { plane, nhiFromSwarm } = await import("../lib/plane.mjs");
const { makeAlerts } = await import("../lib/alerts.mjs");
const { runWatch } = await import("../lib/watch.mjs");
const { bodyOf, buildInput, questionHash } = await import("../lib/oracle.mjs");
const { makeMockPlane } = await import("./lib/mock-plane.mjs");

const E18 = BigNumber.from(10).pow(18);
const P = E18.div(250);
const KEY = "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d";
const ATTESTER = "0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a";
const PAY_TO = "0x4e0fa57bde726079356537e2f34d671e9f41adbc";
const holdCtx = () => ({ cfg: {}, planeState: { ok: true }, signer: { address: "0x00000000000000000000000000000000000000ee" },
  provider: { getBalance: async () => E18, getGasPrice: async () => BigNumber.from(10).pow(9) }, book: { token: () => ({ balanceOf: async () => E18.mul(50) }) } });
const reset = () => { for (const f of ["orders.json", "imd-spend.json", "STOP", "alerts.json", "depth.json", "feeds.json"]) rmSync(statePath(f), { force: true }); };

async function flow() {
  reset();
  const clock = { number: 1000, timestamp: 1_800_000_000, hash: `0x${"11".repeat(32)}` };
  const mock = makeMockPlane({ answer: () => ({ value: P }), chain: async () => clock, attesterKey: ATTESTER });
  const base = await mock.listen();
  const wallet = new Wallet(KEY);
  const provider = { getBlock: async () => ({ number: clock.number, timestamp: clock.timestamp }), getBalance: async () => E18, getGasPrice: async () => BigNumber.from(10).pow(9), unpin() {} };
  const leafAddr = "0x00000000000000000000000000000000000000a1";
  const leaf = { address: leafAddr, MIN_PANEL_SIZE: async () => 100, minAgreed: async () => 67, expectedQuestionHash: async (a, b) => questionHash(bodyOf("price"), Number(a), Number(b)) };
  const token = { balanceOf: async () => E18.mul(50), allowance: async () => E18.mul(50) };
  const signer = { address: wallet.address, provider, _signTypedData: (...a) => wallet._signTypedData(...a) };
  process.env.PLANE_BEARER = "local-test-bearer";
  const ctx = { execute: true, cfg: { PLANE: { payTo: PAY_TO } }, provider, signer, keeperAddress: wallet.address, log() {}, planeState: { ok: true, base },
    planeClient: planeClient(base, "local-test-bearer"), book: { token: () => token, leaf: () => leaf }, alerts: { raise: async () => {} } };
  const input = await buildInput(leaf, "price");
  return { ctx, mock, input, leafAddr, close: () => mock.close() };
}
const rounds = async (ctx, n) => { for (let i = 0; i < n; i++) await advanceOrders(ctx); };

test("J4 P3: real guardedSigner.bump on a never-mined urgent send: fees stay under MAX_GAS_GWEI", async () => {
  reset();
  for (const f of pendingIntents()) rmSync(statePath(`tx/${f.nonce}.json`), { force: true });
  let relayed = 0;
  providers.StaticJsonRpcProvider.prototype.sendTransaction = async (signed) => { relayed += 1; return { hash: utils.keccak256(signed) }; };
  const gwei = (n) => utils.parseUnits(String(n), "gwei");
  const provider = { getBlock: async () => ({ baseFeePerGas: gwei(10) }), getTransactionReceipt: async () => null, getTransactionCount: async () => 5, getGasPrice: async () => gwei(10), getBalance: async () => E18 };
  const cfg = { SEND_RPC_URLS: ["https://relay.invalid"], MAX_GAS_GWEI: 100, URGENT_WAIT_SECONDS: 60 };
  const w = guardedSigner(new Wallet(KEY), provider, cfg, "tx");
  const signed = await w.signTransaction({ type: 2, chainId: 1, nonce: 5, to: PAY_TO, data: "0x", value: 0, gasLimit: 300000, maxPriorityFeePerGas: gwei(0.3), maxFeePerGas: gwei(20.3) });
  recordIntent({ nonce: 5, hash: utils.keccak256(signed), signed, label: "bite SIMD #1" });
  const proxy = { provider, address: w.address, rebroadcast: w.rebroadcast, bump: w.bump, journalScope: "tx" };
  const timeout = () => ({ hash: "0xnew", wait: async () => { const e = new Error("t"); e.code = "TIMEOUT"; throw e; } });
  for (let pass = 0; pass < 60; pass++) {
    const p = statePath("tx/5.json"); const it = JSON.parse(readFileSync(p, "utf8")); it.at = new Date(Date.now() - 61_000).toISOString(); writeFileSync(p, JSON.stringify(it));
    await send(proxy, "bite SIMD #1", async () => {}, async () => timeout(), cfg, { tier: "urgent" });
  }
  const t = utils.parseTransaction(JSON.parse(readFileSync(statePath("tx/5.json"), "utf8")).signed);
  const max = Number(utils.formatUnits(t.maxFeePerGas, "gwei")), tip = Number(utils.formatUnits(t.maxPriorityFeePerGas, "gwei"));
  assert.ok(max <= 100, `ACTUAL after 60 urgent passes: nonce 5 tip ${tip.toFixed(1)} gwei, maxFee ${max.toFixed(1)} gwei (MAX_GAS_GWEI 100), ${relayed} signed txs relayed, still open`);
});
```

### 3. Medium: C8 refuted: a plane answering 'expired' or 'payment_failed' for a paid or LOST order releases the all-buying hold, and the next fall pays again

`lib/orders.mjs:207`

```
  if (["payment_failed", "expired"].includes(r.body?.status)) {
    await ctx.alerts.raise("warn", `feeds-order-unpaid-${o.purpose}`, `${o.purpose}: the plane reads order ${o.planeOrder} as ${r.body.status}: nothing was charged`);
    return finish(o, "unpaid", r.body.status);
```

awaitAdmission() runs for stage 'paying' AND 'lost' (step(), line 166). When GET /requests/<id> reports 'expired' or 'payment_failed' the order is finished 'unpaid' and removed on the plane's word alone; nothing checks the Permit2 nonce or an IMD transfer on chain. So a hostile plane / MITM that settles the signed payment and never admits can lift the 'lost' hold README reserves for a human (--clear-lost) with one word, then take another payment for the same fall. Spend stays counted, so the daily cap (5 IMD) bounds it; with the unbounded Permit2 deadline (separate finding) the signed permit also stays settleable. Merged from all four specialists. Smallest fix: never finish a 'lost' order on the plane's status; for 'paying', record the Permit2 nonce/deadline at the latch and finish 'unpaid' only once the deadline has passed and Permit2.nonceBitmap shows the nonce unused, else move to 'refused-paid'.

**Reproduction**

Mock plane with admit=false: order paid once; paidAt moved 2 h back -> stage lost, buyHold holds. Plane order status set to 'expired'; advanceOrders; a new fall order; 3 rounds. Expected: 1 lost order, 1 payment. Actual: 0 lost orders, 2 payments.

BUILD: npm ci && npm test (72 pass on e53d362). Proof: save the 'proof' source as test/zz-judge.test.mjs and run `node --test test/zz-judge.test.mjs` (node:test, fakes and the repo's mock plane only, no network); the test asserts the correct behaviour and FAILS on this commit. Output on this commit: not ok ... AssertionError [ERR_ASSERTION]: ACTUAL: lost=0, payments=2

TEST:
test("J2 C8: 'expired' on a LOST (paid, never admitted) order must not release the hold", async () => {
  const f = await flow();
  try {
    f.mock.state.admit = false;
    startOrder("fall", [{ role: "price", leaf: f.leafAddr, input: f.input }]);
    await rounds(f.ctx, 2);
    assert.equal(f.mock.state.payments.length, 1);
    const list = loadOrders(); list[0].paidAt = new Date(Date.now() - 2 * 3600_000).toISOString(); writeState("orders.json", { list });
    await advanceOrders(f.ctx);
    assert.equal(lostOrders().length, 1, "lost: held");
    f.mock.state.orders.get(list[0].planeOrder).status = "expired";
    await advanceOrders(f.ctx);
    startOrder("fall", [{ role: "price", leaf: f.leafAddr, input: f.input }]);
    await rounds(f.ctx, 3);
    assert.equal(f.mock.state.payments.length, 1, `ACTUAL: lost=${lostOrders().length}, payments=${f.mock.state.payments.length}`);
  } finally { await f.close(); }
});

**Proof**: a Foundry test that fails on this code and passes once it is fixed.

```solidity
// JUDGE SCRATCH (deleted after the run). Each test asserts the CORRECT behaviour: a failure here is a reproduced finding.
import { test } from "node:test";
import assert from "node:assert/strict";
import { mkdtempSync, rmSync, writeFileSync, readdirSync, readFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { BigNumber, providers, utils, Wallet } from "ethers";

process.env.STATE_DIR = mkdtempSync(join(tmpdir(), "keeper-judge-"));
const { readState, writeState, statePath, takeLock } = await import("../lib/state.mjs");
const { advanceOrders, buyHold, lostOrders, loadOrders, spentToday, startOrder } = await import("../lib/orders.mjs");
const { planeClient, signPayment, IMD } = await import("../lib/x402.mjs");
const { guardedSigner, send } = await import("../lib/tx.mjs");
const { recordIntent, pendingIntents } = await import("../lib/journal.mjs");
const { runDepth } = await import("../lib/depth.mjs");
const { fallDecision } = await import("../lib/feeds.mjs");
const { plane, nhiFromSwarm } = await import("../lib/plane.mjs");
const { makeAlerts } = await import("../lib/alerts.mjs");
const { runWatch } = await import("../lib/watch.mjs");
const { bodyOf, buildInput, questionHash } = await import("../lib/oracle.mjs");
const { makeMockPlane } = await import("./lib/mock-plane.mjs");

const E18 = BigNumber.from(10).pow(18);
const P = E18.div(250);
const KEY = "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d";
const ATTESTER = "0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a";
const PAY_TO = "0x4e0fa57bde726079356537e2f34d671e9f41adbc";
const holdCtx = () => ({ cfg: {}, planeState: { ok: true }, signer: { address: "0x00000000000000000000000000000000000000ee" },
  provider: { getBalance: async () => E18, getGasPrice: async () => BigNumber.from(10).pow(9) }, book: { token: () => ({ balanceOf: async () => E18.mul(50) }) } });
const reset = () => { for (const f of ["orders.json", "imd-spend.json", "STOP", "alerts.json", "depth.json", "feeds.json"]) rmSync(statePath(f), { force: true }); };

async function flow() {
  reset();
  const clock = { number: 1000, timestamp: 1_800_000_000, hash: `0x${"11".repeat(32)}` };
  const mock = makeMockPlane({ answer: () => ({ value: P }), chain: async () => clock, attesterKey: ATTESTER });
  const base = await mock.listen();
  const wallet = new Wallet(KEY);
  const provider = { getBlock: async () => ({ number: clock.number, timestamp: clock.timestamp }), getBalance: async () => E18, getGasPrice: async () => BigNumber.from(10).pow(9), unpin() {} };
  const leafAddr = "0x00000000000000000000000000000000000000a1";
  const leaf = { address: leafAddr, MIN_PANEL_SIZE: async () => 100, minAgreed: async () => 67, expectedQuestionHash: async (a, b) => questionHash(bodyOf("price"), Number(a), Number(b)) };
  const token = { balanceOf: async () => E18.mul(50), allowance: async () => E18.mul(50) };
  const signer = { address: wallet.address, provider, _signTypedData: (...a) => wallet._signTypedData(...a) };
  process.env.PLANE_BEARER = "local-test-bearer";
  const ctx = { execute: true, cfg: { PLANE: { payTo: PAY_TO } }, provider, signer, keeperAddress: wallet.address, log() {}, planeState: { ok: true, base },
    planeClient: planeClient(base, "local-test-bearer"), book: { token: () => token, leaf: () => leaf }, alerts: { raise: async () => {} } };
  const input = await buildInput(leaf, "price");
  return { ctx, mock, input, leafAddr, close: () => mock.close() };
}
const rounds = async (ctx, n) => { for (let i = 0; i < n; i++) await advanceOrders(ctx); };

test("J2 C8: 'expired' on a LOST (paid, never admitted) order must not release the hold", async () => {
  const f = await flow();
  try {
    f.mock.state.admit = false;
    startOrder("fall", [{ role: "price", leaf: f.leafAddr, input: f.input }]);
    await rounds(f.ctx, 2);
    assert.equal(f.mock.state.payments.length, 1);
    const list = loadOrders(); list[0].paidAt = new Date(Date.now() - 2 * 3600_000).toISOString(); writeState("orders.json", { list });
    await advanceOrders(f.ctx);
    assert.equal(lostOrders().length, 1, "lost: held");
    f.mock.state.orders.get(list[0].planeOrder).status = "expired";
    await advanceOrders(f.ctx);
    startOrder("fall", [{ role: "price", leaf: f.leafAddr, input: f.input }]);
    await rounds(f.ctx, 3);
    assert.equal(f.mock.state.payments.length, 1, `ACTUAL: lost=${lostOrders().length}, payments=${f.mock.state.payments.length}`);
  } finally { await f.close(); }
});
```

### 4. Medium: C7 refuted: one failed read in the depth round pokes, then marks the hour done with no samples

`lib/depth.mjs:67`

```
  if (!round.calls.length) return writeState(FILE, { ...state, done: true });
```

runDepth(): when simulateRound()/readLow() throws (RPC disagreement, timeout) the catch (54-56) leaves round = {calls: []}, poke = true; the poke goes alone (63-66) and line 67 writes done: true because round.calls is empty, the same branch as 'no sampler would land'. depthPlan() never reopens a done hour, so every DepthSampler misses that hour's bucket although the next 60 s pass would read fine. Repeated at the random second, a flaky/hostile read RPC empties buckets (under 20 filled the sampler reads 0 and sIMD borrowing closes). Merged from all four specialists. Smallest fix: flag a read failure in the catch and at line 67 write done: true only when the simulation succeeded and found nothing to send; record the poke separately so the retry sends only the samples.

**Reproduction**

depth.json {hour: H, at: now-1, done: false}; Multicall3 simulation throws 'read RPCs disagree'. Expected: poke sent, done false. Actual: sent ['poke'], done true.

BUILD: npm ci && npm test (72 pass on e53d362). Proof: save the 'proof' source as test/zz-judge.test.mjs and run `node --test test/zz-judge.test.mjs` (node:test, fakes and the repo's mock plane only, no network); the test asserts the correct behaviour and FAILS on this commit. Output on this commit: not ok ... AssertionError [ERR_ASSERTION]: ACTUAL: done=true after a failed read, no samples this hour

TEST:
test("J3 C7: a failed read in the depth round leaves the hour open", async () => {
  reset();
  const now = 20_000 * 86_400 + 3600;
  writeState("depth.json", { hour: Math.floor(now / 3600), at: now - 1, done: false });
  const sent = [];
  const c = (name, fn) => { const x = { address: `0x${name}`, callStatic: { [fn]: async () => {} }, [fn]: async () => { sent.push(name); return { hash: "0x1", wait: async () => ({ blockNumber: 1, gasUsed: 1 }) }; }, interface: { encodeFunctionData: (f) => f } }; x.connect = () => x; return x; };
  const multicall = c("batch", "aggregate3"); multicall.callStatic.aggregate3 = async () => { throw new Error("read RPCs disagree on call at 1"); };
  const provider = { getBlock: async () => ({ number: 1, timestamp: now, baseFeePerGas: 1n }), getStorageAt: async () => "0x0", getGasPrice: async () => BigNumber.from(1), getBalance: async () => E18, unpin() {} };
  const imdDepth = { ...c("poke", "poke"), lowOf: async () => ({ day: 20_000 }) }; imdDepth.connect = () => imdDepth;
  await runDepth({ execute: true, log() {}, provider, multicall, cfg: {}, signer: { provider, address: "0xk" }, book: { imdDepth, samplers: [c("sampler", "sample")] }, alerts: { raise: async () => {} } });
  assert.deepEqual(sent, ["poke"]);
  assert.equal(readState("depth.json").done, false, "ACTUAL: done=true after a failed read, no samples this hour");
});

**Proof**: a Foundry test that fails on this code and passes once it is fixed.

```solidity
// JUDGE SCRATCH (deleted after the run). Each test asserts the CORRECT behaviour: a failure here is a reproduced finding.
import { test } from "node:test";
import assert from "node:assert/strict";
import { mkdtempSync, rmSync, writeFileSync, readdirSync, readFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { BigNumber, providers, utils, Wallet } from "ethers";

process.env.STATE_DIR = mkdtempSync(join(tmpdir(), "keeper-judge-"));
const { readState, writeState, statePath, takeLock } = await import("../lib/state.mjs");
const { advanceOrders, buyHold, lostOrders, loadOrders, spentToday, startOrder } = await import("../lib/orders.mjs");
const { planeClient, signPayment, IMD } = await import("../lib/x402.mjs");
const { guardedSigner, send } = await import("../lib/tx.mjs");
const { recordIntent, pendingIntents } = await import("../lib/journal.mjs");
const { runDepth } = await import("../lib/depth.mjs");
const { fallDecision } = await import("../lib/feeds.mjs");
const { plane, nhiFromSwarm } = await import("../lib/plane.mjs");
const { makeAlerts } = await import("../lib/alerts.mjs");
const { runWatch } = await import("../lib/watch.mjs");
const { bodyOf, buildInput, questionHash } = await import("../lib/oracle.mjs");
const { makeMockPlane } = await import("./lib/mock-plane.mjs");

const E18 = BigNumber.from(10).pow(18);
const P = E18.div(250);
const KEY = "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d";
const ATTESTER = "0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a";
const PAY_TO = "0x4e0fa57bde726079356537e2f34d671e9f41adbc";
const holdCtx = () => ({ cfg: {}, planeState: { ok: true }, signer: { address: "0x00000000000000000000000000000000000000ee" },
  provider: { getBalance: async () => E18, getGasPrice: async () => BigNumber.from(10).pow(9) }, book: { token: () => ({ balanceOf: async () => E18.mul(50) }) } });
const reset = () => { for (const f of ["orders.json", "imd-spend.json", "STOP", "alerts.json", "depth.json", "feeds.json"]) rmSync(statePath(f), { force: true }); };

async function flow() {
  reset();
  const clock = { number: 1000, timestamp: 1_800_000_000, hash: `0x${"11".repeat(32)}` };
  const mock = makeMockPlane({ answer: () => ({ value: P }), chain: async () => clock, attesterKey: ATTESTER });
  const base = await mock.listen();
  const wallet = new Wallet(KEY);
  const provider = { getBlock: async () => ({ number: clock.number, timestamp: clock.timestamp }), getBalance: async () => E18, getGasPrice: async () => BigNumber.from(10).pow(9), unpin() {} };
  const leafAddr = "0x00000000000000000000000000000000000000a1";
  const leaf = { address: leafAddr, MIN_PANEL_SIZE: async () => 100, minAgreed: async () => 67, expectedQuestionHash: async (a, b) => questionHash(bodyOf("price"), Number(a), Number(b)) };
  const token = { balanceOf: async () => E18.mul(50), allowance: async () => E18.mul(50) };
  const signer = { address: wallet.address, provider, _signTypedData: (...a) => wallet._signTypedData(...a) };
  process.env.PLANE_BEARER = "local-test-bearer";
  const ctx = { execute: true, cfg: { PLANE: { payTo: PAY_TO } }, provider, signer, keeperAddress: wallet.address, log() {}, planeState: { ok: true, base },
    planeClient: planeClient(base, "local-test-bearer"), book: { token: () => token, leaf: () => leaf }, alerts: { raise: async () => {} } };
  const input = await buildInput(leaf, "price");
  return { ctx, mock, input, leafAddr, close: () => mock.close() };
}
const rounds = async (ctx, n) => { for (let i = 0; i < n; i++) await advanceOrders(ctx); };

test("J3 C7: a failed read in the depth round leaves the hour open", async () => {
  reset();
  const now = 20_000 * 86_400 + 3600;
  writeState("depth.json", { hour: Math.floor(now / 3600), at: now - 1, done: false });
  const sent = [];
  const c = (name, fn) => { const x = { address: `0x${name}`, callStatic: { [fn]: async () => {} }, [fn]: async () => { sent.push(name); return { hash: "0x1", wait: async () => ({ blockNumber: 1, gasUsed: 1 }) }; }, interface: { encodeFunctionData: (f) => f } }; x.connect = () => x; return x; };
  const multicall = c("batch", "aggregate3"); multicall.callStatic.aggregate3 = async () => { throw new Error("read RPCs disagree on call at 1"); };
  const provider = { getBlock: async () => ({ number: 1, timestamp: now, baseFeePerGas: 1n }), getStorageAt: async () => "0x0", getGasPrice: async () => BigNumber.from(1), getBalance: async () => E18, unpin() {} };
  const imdDepth = { ...c("poke", "poke"), lowOf: async () => ({ day: 20_000 }) }; imdDepth.connect = () => imdDepth;
  await runDepth({ execute: true, log() {}, provider, multicall, cfg: {}, signer: { provider, address: "0xk" }, book: { imdDepth, samplers: [c("sampler", "sample")] }, alerts: { raise: async () => {} } });
  assert.deepEqual(sent, ["poke"]);
  assert.equal(readState("depth.json").done, false, "ACTUAL: done=true after a failed read, no samples this hour");
});
```

### 5. Medium: Q4: fallDecision's reference is not Pricer.quote().low for an unconverged leg within skew (misses a fall) or a stale proven spot (buys on a rise)

`lib/feeds.mjs:52`

```
  const gap = m.gt(s) ? m.sub(s) : s.sub(m);
  const apart = gap.mul(10_000).gt(m.mul(skewBps));
  // Design C, the twin rule: sIMD's spot is proven, so legs apart are measured at the HIGHER leg, together at the lower.
  const reference = apart ? (m.gt(s) ? m : s) : m.lt(s) ? m : s;
```

src/Pricer.sol quote() @67eadeac: spotOk needs a fresh nonzero spot; agree needs the gap within skew AND both legs converged; agree -> low = min; else spotOk && proven -> low = max (twin rule); else low = m. fallDecision decides 'apart' from the gap alone and is given no staleness or convergence. (a) Median not converged (the normal state after a clamped fall answer) and spot within skew: Pricer low = max, keeper uses min, so a pool 6% under the Pricer's low reads 'no-fall' and the next walk is not bought while loans are at risk (C5's slower leg gets slower). (b) Proven spot stale and above the median: Pricer low = m, keeper uses the stale spot, so it buys a median answer (0.5 IMD) on a rise, against rule 2 / P2. Merged from all four specialists. Smallest fix: use q.low from book.pricer.quote() (already read in decideFall) as the reference, and treat !q.priced as no decision.

**Reproduction**

skewBps 500, triggerBps 500, atRisk. (a) median P (unconverged), spot 0.98P, pool 0.94P: Pricer low P, 6% fall -> expected buy, actual no-fall. (b) median P, stale spot 1.2P, pool 1.1P: Pricer low P, a rise -> expected no-fall, actual buy.

BUILD: npm ci && npm test (72 pass on e53d362). Proof: save the 'proof' source as test/zz-judge.test.mjs and run `node --test test/zz-judge.test.mjs` (node:test, fakes and the repo's mock plane only, no network); the test asserts the correct behaviour and FAILS on this commit. Output on this commit: not ok ... AssertionError [ERR_ASSERTION]: ACTUAL: (a) no-fall, (b) buy

TEST:
test("J8 Q4: fallDecision measures from Pricer.quote().low (unconverged median within skew; stale spot)", () => {
  const base = { skewBps: 500, triggerBps: 500, atRisk: true, lastBuyAt: 0, now: 1, minGapSeconds: 0, hadOpenOrder: false };
  // (a) median 100 not converged, spot 98 (within 5%): Pricer agree=false -> twin rule low = max = 100. pool 94 is 6% under it.
  const a = fallDecision({ ...base, median: P, spot: P.mul(98).div(100), pool: P.mul(94).div(100) });
  // (b) proven spot 120 STALE, median 100: Pricer low = m = 100. pool 110 is a rise.
  const b = fallDecision({ ...base, median: P, spot: P.mul(120).div(100), pool: P.mul(110).div(100) });
  assert.deepEqual([a.kind, b.kind], ["buy", "no-fall"], `ACTUAL: (a) ${a.kind}, (b) ${b.kind}`);
});

### 6. Medium: P4 refuted: a failed converged()/attested() read is swallowed and the feeds pass then reports feeds-not-converged-* as cleared

`lib/feeds.mjs:118`

```
    } catch {
      /* a leaf built before design C has no converged(): nothing to say */
```

convergenceAlerts() catches every error from leaf.converged()/attested()/latestValue() (the comment assumes only a pre-design-C leaf without converged(); the deployed leaves have it) and raises nothing; runFeeds() line 192 then calls alerts.settle('feeds-', keys raised this pass), which deletes the standing feeds-not-converged-median/-nhi and emits 'cleared (was warn)' while the leg is still clamped and sIMD lends nothing. Could-not-tell is reported as fine. Merged from all four specialists. Smallest fix: in the catch raise feeds-not-converged-unread-<role> (as provenSpotAlerts does) or add the existing key to the held set.

**Reproduction**

alerts.json holds feeds-not-converged-median; the next pass's converged() throws, so convergenceAlerts returns [] and settle('feeds-', {}) runs. Expected: the alert stands. Actual: 'ALERT INFO feeds-not-converged-median: cleared (was warn)'.

BUILD: npm ci && npm test (72 pass on e53d362). Proof: save the 'proof' source as test/zz-judge.test.mjs and run `node --test test/zz-judge.test.mjs` (node:test, fakes and the repo's mock plane only, no network); the test asserts the correct behaviour and FAILS on this commit. Output on this commit: not ok ... AssertionError [ERR_ASSERTION]: ACTUAL: ALERT WARN feeds-not-converged-median: held short | ALERT INFO feeds-not-converged-median: cleared (was warn)

TEST:
test("J5 P4: a failed converged() read keeps feeds-not-converged-median (alerts.settle path)", async () => {
  reset();
  const logs = []; const a = makeAlerts((s) => logs.push(s));
  await a.raise("warn", "feeds-not-converged-median", "held short");
  // runFeeds line 192: settle('feeds-', keys raised this pass); convergenceAlerts' catch raised nothing for a throwing converged().
  await a.settle("feeds-", new Set());
  assert.ok(!logs.some((l) => /feeds-not-converged-median: cleared/.test(l)), `ACTUAL: ${logs.join(" | ")}`);
});

### 7. Medium: P4 refuted: the watch pass clears depth-proof-failed-* and depth-prove-now-failed, which only the depth/feeds loops raise

`lib/watch.mjs:75`

```
  return Object.keys(readState("alerts.json", {})).filter((k) => /^(depth-sampler-|depth-poke-|depth-round-|index-|cannot-close-|feeds-|seats-|peg-|backstop-|allocator-|guardian-|loop-)/.test(k));
```

runWatch() ends with alerts.settle('', own keys + heldElsewhere()); heldElsewhere's prefix list has depth-sampler-|depth-poke-|depth-round- but not depth-proof- (depth.mjs:126) or depth-prove- (depth.mjs:141). Every 5-minute watch pass reports these as 'cleared (was warn)' while the proof still fails; they guard the proven sIMD spot (borrowing and the twin-rule liquidation). From audit_flow. Smallest fix: add depth-proof-|depth-prove- to the regex (or let each loop settle only its own prefix).

**Reproduction**

alerts.json holds depth-proof-failed-0xabc and depth-prove-now-failed; runWatch with a clean fake book. Expected: both survive. Actual: both cleared.

BUILD: npm ci && npm test (72 pass on e53d362). Proof: save the 'proof' source as test/zz-judge.test.mjs and run `node --test test/zz-judge.test.mjs` (node:test, fakes and the repo's mock plane only, no network); the test asserts the correct behaviour and FAILS on this commit. Output on this commit: not ok ... AssertionError [ERR_ASSERTION]: ACTUAL: ALERT INFO depth-proof-failed-0xabc: cleared (was warn) | ALERT INFO depth-prove-now-failed: cleared (was warn)

TEST:
test("J9 P4: the watch pass must not clear the depth loop's proof alerts", async () => {
  reset();
  const logs = []; const alerts = makeAlerts((s) => logs.push(s));
  await alerts.raise("warn", "depth-proof-failed-0xabc", "proof failed");
  await alerts.raise("warn", "depth-prove-now-failed", "prove failed");
  const ts = Math.floor(Date.now() / 1000);
  const provider = { getBlock: async () => ({ number: 1, timestamp: ts }), getBalance: async () => E18, unpin() {} };
  const Z = BigNumber.from(0);
  const book = { ilks: [], samplers: [], ledger: { unbacked: async () => Z }, liquidator: { edgeInAuction: async () => Z },
    params: { RETIRE_SLEEVE_TOKEN: async () => 1, pendingOf: async () => ({ payload: "0x", eta: Z }), depthIMD: async () => Z },
    imdDepth: { lowOf: async () => ({ day: 0, usd: 0 }) } };
  try { await runWatch({ book, cfg: {}, provider, log() {}, alerts }); } catch (e) { logs.push(`watch threw: ${e.message}`); }
  assert.ok(!logs.some((l) => /depth-prov.*cleared/.test(l)), `ACTUAL: ${logs.filter((l) => /cleared|threw/.test(l)).join(" | ")}`);
});

### 8. Medium: C6 refuted: the plane-health hold has no time bound; three old 'blocked' rows on a quiet or hostile plane hold all buying for ever

`lib/plane.mjs:53`

```
    if (settled.length === n && settled.every((r) => r.status === "blocked")) {
      return { base, ok: false, why: `the plane's last ${n} settled oracle requests were all blocked (newest ${settled[0].createdAt})`, servers };
```

plane() holds every purchase while the newest holdAfterBlocked (3) settled rows of the unauthenticated /oracle/requests are all 'blocked', whatever their age. The keeper buys nothing while held, so on a quiet plane nothing new settles and the hold never ends; a hostile plane or the ENS-chosen server can keep it forever. While held no fall median, NHI or seat leg is bought (sIMD stops lending; liquidation waits on the stale higher median, C5) and the only alert is warn 'feeds-plane-hold'. Merged from audit_economics and audit_flow. Smallest fix: ignore settled rows older than a bound (e.g. 6 h) or lift the hold after N hours, and escalate to critical while it holds.

**Reproduction**

/health ok; /oracle/requests returns three 'blocked' rows dated 30 days ago. Expected: ok true. Actual: ok false, 'the plane's last 3 settled oracle requests were all blocked'.

BUILD: npm ci && npm test (72 pass on e53d362). Proof: save the 'proof' source as test/zz-judge.test.mjs and run `node --test test/zz-judge.test.mjs` (node:test, fakes and the repo's mock plane only, no network); the test asserts the correct behaviour and FAILS on this commit. Output on this commit: not ok ... AssertionError [ERR_ASSERTION]: ACTUAL: the plane's last 3 settled oracle requests were all blocked (newest 2026-10-16T05:40:38.729Z)

TEST:
test("J6 C6: a quiet plane whose newest 3 settled requests are a month-old 'blocked' must not hold buying for ever", async () => {
  const recent = { requests: [1, 2, 3].map(() => ({ status: "blocked", createdAt: new Date(Date.now() - 30 * 86400_000).toISOString() })) };
  const fetchImpl = async (url) => ({ ok: true, json: async () => (String(url).endsWith("/health") ? { status: "ok" } : recent) });
  const r = await plane(null, { API_BASE: "https://plane.test", PLANE: { useEns: false, holdAfterBlocked: 3 } }, fetchImpl);
  assert.equal(r.ok, true, `ACTUAL: ${r.why}`);
});

### 9. Medium: P3: one writer per key is not enforced: the lock, spend ledger and nonce journal are per STATE_DIR

`lib/state.mjs:24`

```
  const p = statePath("keeper.lock");
```

takeLock() writes <STATE_DIR>/keeper.lock; imd-spend.json, orders.json and tx/<nonce>.json are per directory too. The threat model's operator running a second --execute process on the same key with another STATE_DIR gets a second lock, a second 5 IMD daily cap, an independent fall decision (both can pay for the same fall) and a nonce journal blind to the other's private sends, so both sign the same nonce and one action (liquidation, relay) is silently replaced. Merged from audit_economics, audit_flow, audit_permissions. Smallest fix: lock on the key (lock file named by the keeper address in a fixed location, or refuse --execute when the chain's pending nonce differs from this journal's view), and take the spend as max(file, today's on-chain IMD transfers from the key to payTo).

**Reproduction**

Two STATE_DIRs, one process each, same key. Expected: the second takeLock() throws. Actual: both succeed.

BUILD: npm ci && npm test (72 pass on e53d362). Proof: save the 'proof' source as test/zz-judge.test.mjs and run `node --test test/zz-judge.test.mjs` (node:test, fakes and the repo's mock plane only, no network); the test asserts the correct behaviour and FAILS on this commit. Output on this commit: not ok ... AssertionError [ERR_ASSERTION]: ACTUAL: both locks taken; each dir has its own spend ledger and nonce journal

TEST:
test("J7 P3: a second process on the same key with another STATE_DIR is refused", () => {
  const saved = process.env.STATE_DIR;
  process.env.STATE_DIR = mkdtempSync(join(tmpdir(), "ka-")); const ra = takeLock();
  process.env.STATE_DIR = mkdtempSync(join(tmpdir(), "kb-"));
  let refused = false; let rb = null;
  try { rb = takeLock(); } catch { refused = true; }
  ra(); rb?.(); process.env.STATE_DIR = saved;
  assert.ok(refused, "ACTUAL: both locks taken; each dir has its own spend ledger and nonce journal");
});

### 12. Medium: P1: a /swarm document with a negative count makes nhiFromSwarm negative; S.nhi.accepts() then throws and the whole feeds pass dies before the fall decision and the twin-rule prove

`lib/plane.mjs:68`

```
  let p = n(h.seatsEnrolled) === 0n ? 0n : (n(h.agentsOnline) * WAD) / n(h.seatsEnrolled);
  if (p > WAD) p = WAD;
```

nhiFromSwarm caps p above (p > WAD) but not below 0, and n() accepts negative numbers. runFeeds awaits decideNhi (176) before decideFall (177) and proveNow (188); once NHI is due (age >= 22 h with sIMD debt) decideNhi calls S.nhi.accepts(live.toString()) with a negative value, ethers throws 'value out-of-bounds', and the pass rejects: no fall is bought, the legs-apart proof is not sent, the proven-spot and convergence alerts are not evaluated, for as long as the plane serves that document (loop-failing alert after 5 passes). Threat model: a hostile plane / MITM controls answers. From audit_math. Smallest fix: clamp every term to [0, WAD] and return null (no-read) outside it; isolate decideNhi in its own try/catch so the fall path never depends on it.

**Reproduction**

nhiFromSwarm({health:{agentsOnline:-100,seatsEnrolled:10,verifierUp:true,publisherUp:true,deployerUp:true},counts:{jobStates:{completed:1}}}). Expected: >= 0. Actual: -3400000000000000000.

BUILD: npm ci && npm test (72 pass on e53d362). Proof: save the 'proof' source as test/zz-judge.test.mjs and run `node --test test/zz-judge.test.mjs` (node:test, fakes and the repo's mock plane only, no network); the test asserts the correct behaviour and FAILS on this commit. Output on this commit: not ok ... AssertionError [ERR_ASSERTION]: ACTUAL: -3400000000000000000 (S.nhi.accepts(-3400000000000000000) then throws 'value out-of-bounds' inside decideNhi, before decideFall and proveNow)

TEST:
test("J12 P1: a /swarm document with a negative count yields an index in [0, 1e18]", () => {
  const v = nhiFromSwarm({ health: { agentsOnline: -100, seatsEnrolled: 10, verifierUp: true, publisherUp: true, deployerUp: true }, counts: { jobStates: { completed: 1 } } });
  assert.ok(v >= 0n, `ACTUAL: ${v} (S.nhi.accepts(${v}) then throws 'value out-of-bounds' inside decideNhi, before decideFall and proveNow)`);
});

### 10. Low: P2: pay()'s last-moment re-check drops reserveForFalls, so a quoted seat/NHI order pays into the reserve kept for falls

`lib/orders.mjs:182`

```
  const hold = await buyHold(ctx, amount);
  if (hold) return ctx.log(`order   ${o.purpose}: held at the last moment: ${hold}`);
```

decideNhi (feeds.mjs:277) and runSeats hold unless spent + cost + reserveForFalls <= dailyCap, but pay() re-checks with buyHold(ctx, amount) and no reserve, and the order does not carry one. Spend can move between the decision and payment (up to unpaidOrderMaxSeconds 600 s), and the seat/NHI leg then eats the last IMD reserved for a fall later that day. From audit_math. Smallest fix: store the reserve on the order in startOrder and pass it to buyHold in pay().

**Reproduction**

imd-spend.json 4 IMD today; startOrder('seats', 2 questions = 1 IMD); 2 rounds against the mock plane. Expected: held (4+1+1 > 5). Actual: paid, 5.0 of 5 IMD spent.

BUILD: npm ci && npm test (72 pass on e53d362). Proof: save the 'proof' source as test/zz-judge.test.mjs and run `node --test test/zz-judge.test.mjs` (node:test, fakes and the repo's mock plane only, no network); the test asserts the correct behaviour and FAILS on this commit. Output on this commit: not ok ... AssertionError [ERR_ASSERTION]: ACTUAL: paid 1, spent today 5.0 of 5 IMD (reserve 1 gone)

TEST:
test("J10 P2: pay()'s last-moment re-check keeps reserveForFalls for a seat order", async () => {
  const f = await flow();
  try {
    writeState("imd-spend.json", { day: new Date().toISOString().slice(0, 10), imd: utils.parseEther("4").toString() });
    startOrder("seats", [{ role: "seatMedian", leaf: f.leafAddr, input: f.input }, { role: "seatSpot", leaf: f.leafAddr, input: f.input }]);
    await rounds(f.ctx, 2);
    assert.equal(f.mock.state.payments.length, 0, `ACTUAL: paid ${f.mock.state.payments.length}, spent today ${utils.formatEther(spentToday())} of 5 IMD (reserve 1 gone)`);
  } finally { await f.close(); }
});

### 11. Low: P2: the Permit2 deadline is the plane's maxTimeoutSeconds, unbounded: a signed payment can stay settleable for years

`lib/x402.mjs:84`

```
  const dl = deadline ?? Math.floor(Date.now() / 1000) + (accept.maxTimeoutSeconds || 300);
```

challengeProblem() pins asset, chain, payee and amount but not accepts[0].maxTimeoutSeconds, which signPayment() turns into the PermitWitnessTransferFrom deadline. A hostile plane asking 315360000 s holds a valid permit for 0.5 IMD for ten years, settleable after it has answered 'expired' (order finished unpaid) or after --clear-lost, outside the day the cap counted, while the 30 IMD Permit2 allowance stands. Payee pinned, bounded per signature. Merged from audit_flow and audit_math. Smallest fix: refuse a challenge whose maxTimeoutSeconds exceeds a config bound (e.g. 900 s) and record the nonce/deadline on the order.

**Reproduction**

signPayment(wallet, challenge with maxTimeoutSeconds = 10 years). Expected: deadline within an hour. Actual: 3650 days.

BUILD: npm ci && npm test (72 pass on e53d362). Proof: save the 'proof' source as test/zz-judge.test.mjs and run `node --test test/zz-judge.test.mjs` (node:test, fakes and the repo's mock plane only, no network); the test asserts the correct behaviour and FAILS on this commit. Output on this commit: not ok ... AssertionError [ERR_ASSERTION]: ACTUAL: permit valid for 3650 days

TEST:
test("J11 P2: the Permit2 deadline is bounded by the keeper, not the plane's maxTimeoutSeconds", async () => {
  const ch = { resource: {}, resourceUrl: "", requesterScopeHash: "ab".repeat(32), accepts: [{ scheme: "exact", network: "eip155:1", amount: "500000000000000000", asset: IMD, payTo: PAY_TO, maxTimeoutSeconds: 10 * 365 * 86400 }],
    quote: { id: "q", quoteHash: "cd".repeat(32), action: "oracle.request", expiresAt: 1, payment: { network: "eip155:1", asset: IMD, amount: "500000000000000000", payTo: PAY_TO } } };
  const { paymentHeader } = await signPayment(new Wallet(KEY), ch);
  const dl = Number(JSON.parse(Buffer.from(paymentHeader, "base64").toString()).payload.permit2Authorization.deadline);
  assert.ok(dl - Date.now() / 1000 <= 3600, `ACTUAL: permit valid for ${Math.round((dl - Date.now() / 1000) / 86400)} days`);
});

### 13. Info: C5 / soundness: after a real fall sIMD liquidation waits on the median's epoch walk; one process cannot meet P1's 60 s bound for sIMD

`lib/feeds.mjs:59`

```
  if (lastBuyAt && now - lastBuyAt < minGapSeconds) return { buy: false, kind: "wait", why: `bought ${now - lastBuyAt}s ago, next from ${minGapSeconds}s` };
```

Order of events for a 40% one-block fall (mat 1.5): the spot leg is proved within ~1-2 blocks (runFeeds: q.agree false -> proveNow), but the Pricer then prices at max(median, spot) = the old median, so nothing is liquidatable. The median leg needs atRisk, no open order, 30 min since the last fall buy (this line), a healthy plane (C6), cap room, quote -> pay -> admission -> panel answer -> relay; and the leaf walks at most its 20% epoch allowance, so a 40% fall needs ceil(ln 0.6 / ln 0.8) = 3 epochs, each a new paid answer. Until converged, liquidation is measured at the clamped median above the pool: bad debt for hours to days whatever the keeper does. The slower leg is the median walk, which the keeper can only start. Depth (hourly) is attainable with the C7 fix; seat ticks only if run on the urgent tier above ROUTINE_MAX_GAS_GWEI; non-sIMD liquidation within one pass only while no nonce is stuck (P3 finding). What it would take: a Pricer rule letting a newer proven spot below the median price liquidation, a second keeper on another key (with a per-key lock), report-only off with paged alerts. Q5 (report-only): no path found that sends or pays: advanceOrders returns on !execute, decideFall/decideNhi return before buyHold/ensureAllowance/startOrder, and test/duties covers report-only. C2 (payTo pinned, x402.mjs:60-61), C3 (refused-paid holds, orders.mjs:198-200) and C4 (journal re-broadcasts the same signed bytes) confirmed by the existing 72 passing tests; C4 holds for crash safety but see the P3 finding for never-mined sends. Fork suites not run.

**Reproduction**

Arithmetic on config.example.js (minSecondsBetweenFalls 1800, allowance 20%/epoch) and src/Pricer.sol quote(): `q.low = q.high = Math.max(m, s)` while the legs disagree. Not a defect in this file alone; no failing test.

---

Judge's submission `8ddb470ce164ed1e0d98af0c18b02e835ec5d3678c45e7d28e864c03b207b862`, accepted on the IdentityMD network. Acceptance means the report met the job's checks;
it is not a guarantee that the code has no other defects.
