# Audit report

> Audit the Python program under spend-guard at commit 288a03eda34957e23fd6ab19c2581ce984ba2a1d. This is a re-audit after prior audit 57db0c89. Focus on guard.py and its tests: numeric validation of quote, cap and token inputs, and whether any input can get past a well-formed cap. Require a file/line, impact and runnable reproduction for every finding. Dry-run only: no fixes, credentials or live spend. The imd-flop and viewer folders are outside this audit.

| | |
|---|---|
| Repository | https://github.com/uxp-labs/flop-devnet-lab.git |
| Commit | `288a03eda34957e23fd6ab19c2581ce984ba2a1d` |
| Job | `355a3703-ff50-47d9-9b2e-6eea054872c3` |
| Judged | 2026-11-10 06:02 UTC |
| Findings | 1 medium · 9 low · 3 info |

Four agents audited the code as it is at `288a03e`, 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. Medium: Quote output-token count is never checked against --max-tokens, so a quote covering fewer tokens than the request passes a well-formed cap

`spend-guard/guard.py:71`

```
        if raw is not None and raw > 0 and nonnegative_count(tin) and nonnegative_count(tout):
```

guard.py sends a.max_tokens to /models/estimate (line 58) but only requires estimated_output_tokens to be a nonnegative integer (lines 71-73). It never requires tout >= a.max_tokens and never bounds the cost by the worst case the request allows. A quote that counts 0 or 1 output tokens for a request asking for 8, 1000 or 1,000,000 is padded only by 20*raw/(tin+tout) and compared to the cap, so the guard records would_send for a request whose billable maximum at the quote's own per-token rate is 20x to 500x the cap. A small tout also shrinks the template pad because per_tok = raw/(tin+tout). SPEC.md item 1 says the padded quote should match what really gets billed and item 2 says to refuse before sending when it is over the cap; neither holds here. In the current dry run the harm is a misleading would_send ledger row; the planned live mode (SPEC 'Next') is gated on this decision, and the child key's max_spend would then be the only limit. Neither test file has a case where estimated_output_tokens differs from --max-tokens (all use 8/8). Reported independently by audit_flow, audit_economics, audit_math and audit_permissions; merged here. Fix (keeps the design): refuse when estimated_output_tokens < a.max_tokens, or compute the bound as per_tok * (tin + TEMPLATE_PAD + max(tout, a.max_tokens)) and compare that to the cap; add regression cases for tout < max_tokens and tout == 0.

**Reproduction**

Offline harness, no network (stubs only the free estimate call; run from an empty temp dir so ledger.jsonl lands there): python3 -c "import io,sys,runpy,urllib.request; B=sys.argv.pop(1).encode(); urllib.request.urlopen=lambda *a,**k: io.BytesIO(B); sys.argv[0]='guard.py'; runpy.run_path('<repo>/spend-guard/guard.py', run_name='__main__')" BODY --user Hi --max-tokens N --cap C. Case A: BODY='{"estimated_cost_flop":0.004,"estimated_input_tokens":3,"estimated_output_tokens":1}' --max-tokens 1000 --cap 0.05. Expected: refused_local (quoted rate 0.004/4 = 0.001 per token times the allowed 3+20+1000 tokens is 1.023 FLOP, about 20x the cap). Actual (verified at 288a03e, Python 3.12.3): decision would_send, quote_padded 0.024, max_tokens 1000. Case B: BODY='{"estimated_cost_flop":0.0001,"estimated_input_tokens":3,"estimated_output_tokens":1}' --max-tokens 1000000 --cap 0.05. Actual: would_send, quote_padded 0.0006 (worst case at quoted rate is 25 FLOP). Case C: BODY='{"estimated_cost_flop":0.0001,"estimated_input_tokens":3,"estimated_output_tokens":0}' --max-tokens 8 --cap 0.05. Actual: would_send, quote_padded 0.000767 for a quote that covers zero output tokens.

### 2. Low: Integer quotes above 2**53 are narrowed by float(raw), so a raw quote strictly above the cap is would_send

`spend-guard/guard.py:75`

```
                unrounded = float(raw) + TEMPLATE_PAD * float(per_tok)
```

json_number keeps an integer estimated_cost_flop exact in quote_raw, but the decision on line 90 compares `unrounded`, built from float(raw) on line 75. Above 2**53 float() rounds to nearest, which can be below the true quote (9007199254740993 -> 9007199254740992.0; 100000000000000008000 -> 1e20). When the token total is large enough that TEMPLATE_PAD*per_tok is under half an ulp, unrounded equals the cap exactly and `unrounded <= cap` passes although quote_raw > cap. The ledger row then shows quote_raw 9007199254740993 next to cap 9007199254740992.0 with decision would_send, contradicting SPEC item 2. Magnitudes are absurd (>9e15 FLOP against a 20,000 FLOP sign-up key), so this is a comparison-correctness gap rather than a practical loss. Reported by audit_permissions and audit_math; merged. Fix: also require the exact raw to be <= cap (e.g. compare with fractions.Fraction, or reject int quotes where int(float(raw)) != raw).

**Reproduction**

Offline harness, no network (stubs only the free estimate call; run from an empty temp dir so ledger.jsonl lands there): python3 -c "import io,sys,runpy,urllib.request; B=sys.argv.pop(1).encode(); urllib.request.urlopen=lambda *a,**k: io.BytesIO(B); sys.argv[0]='guard.py'; runpy.run_path('<repo>/spend-guard/guard.py', run_name='__main__')" BODY --user Hi --max-tokens N --cap C. BODY='{"estimated_cost_flop": 9007199254740993, "estimated_input_tokens": 0, "estimated_output_tokens": 1000000000000000000000000000000}' --max-tokens 8 --cap 9007199254740992. Expected: refused_local (quote_raw 9007199254740993 > cap 9007199254740992). Actual (verified): decision would_send, quote_raw 9007199254740993, quote_padded 9007199254740992.0, cap 9007199254740992.0. Same with estimated_cost_flop 100000000000000008000 and --cap 1e20: would_send with quote_raw exceeding the cap by 8000.

### 3. Low: Token counts are unbounded, so the template pad underflows to zero and a quote exactly at the cap is sent unpadded; int and float costs then disagree

`spend-guard/guard.py:36`

```
    return value >= 0
```

nonnegative_count has no upper bound: integer counts of any size and float counts up to 1e308 pass. per_tok = raw/total (line 74) then makes TEMPLATE_PAD*per_tok smaller than half an ulp of raw, so unrounded == raw and a quote equal to the cap passes `unrounded <= cap` with no pad, defeating the rule that the cap is compared to the padded quote (the same cost with normal counts is refused). With a Python-int cost and a count beyond float range, raw/total is exact int division that underflows to 0.0 and the decision is would_send, while the same count with a float cost raises OverflowError and is refused, so the two paths treat the same absurd input oppositely and the ledger gives no sign the pad was lost. Monetary impact per call is sub-ulp; the defect is that count plausibility is not validated and no test covers unrounded == cap or huge counts. Reported by audit_math and audit_permissions; merged. Fix: bound counts to a sane maximum (a model context limit) and refuse otherwise, and/or refuse when the computed pad is 0.0 or does not change float(raw).

**Reproduction**

Offline harness, no network (stubs only the free estimate call; run from an empty temp dir so ledger.jsonl lands there): python3 -c "import io,sys,runpy,urllib.request; B=sys.argv.pop(1).encode(); urllib.request.urlopen=lambda *a,**k: io.BytesIO(B); sys.argv[0]='guard.py'; runpy.run_path('<repo>/spend-guard/guard.py', run_name='__main__')" BODY --user Hi --max-tokens N --cap C. Case A: BODY='{"estimated_cost_flop": 0.05, "estimated_input_tokens": 3, "estimated_output_tokens": 1000000000000000000}' --max-tokens 8 --cap 0.05. Expected: refused_local (0.05 plus a positive 20-token pad exceeds 0.05; with counts 3/8 the same cost gives quote_padded 0.140909 and refused_local). Actual (verified): would_send, quote_padded 0.05, cap 0.05. Same with estimated_input_tokens 1e300. Case B: BODY='{"estimated_cost_flop": 1, "estimated_input_tokens": 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, "estimated_output_tokens": 0}' --cap 1. Actual: would_send, quote_padded 1.0 (pad is 0). Case C: same count with estimated_cost_flop 0.066: refused_local, quote_padded null.

### 4. Low: Ledger and stdout record a half-up rounded quote_padded that can exceed the cap on a would_send row

`spend-guard/guard.py:77`

```
                    candidate = round(unrounded, 6)
```

The decision on line 90 uses `unrounded`, but the row (line 92) stores candidate = round(unrounded, 6) as quote_padded. When unrounded sits just below the cap and rounds up past it, the persisted row says decision would_send with quote_padded > cap, breaking the invariant a ledger reader relies on (SPEC item 6: the ledger carries the quote and the cap for reconciliation). The row then looks like a cap breach and cannot be distinguished from one. The mirror case (refused_local with quote_padded == cap, the 0.0660001 nudge in test_audit_repro.py) is tested; this direction is not. Reported by audit_permissions, audit_economics and audit_math; merged. Fix: persist the value that was compared (unrounded, or a second field), or round toward zero for display, and assert in tests that would_send rows satisfy quote_padded <= cap.

**Reproduction**

Offline harness, no network (stubs only the free estimate call; run from an empty temp dir so ledger.jsonl lands there): python3 -c "import io,sys,runpy,urllib.request; B=sys.argv.pop(1).encode(); urllib.request.urlopen=lambda *a,**k: io.BytesIO(B); sys.argv[0]='guard.py'; runpy.run_path('<repo>/spend-guard/guard.py', run_name='__main__')" BODY --user Hi --max-tokens N --cap C. BODY='{"estimated_cost_flop": 0.0177418, "estimated_input_tokens": 3, "estimated_output_tokens": 8}' --max-tokens 8 --cap 0.0499997. unrounded = 0.0177418*31/11 = 0.04999961818 <= cap, so would_send; round(...,6) = 0.05. Expected: a would_send row whose quote_padded <= cap. Actual (verified): {quote_padded: 0.05, cap: 0.0499997, decision: would_send} on stdout and in ledger.jsonl. Also BODY cost 0.06599996451612902 with --cap 0.1859999 gives would_send with quote_padded 0.186.

### 5. Low: Finite positive quotes whose padded value is below 5e-7 FLOP are refused and logged exactly like malformed quotes (quote_padded null)

`spend-guard/guard.py:78`

```
                    if json_number(candidate) is not None and candidate > 0:
```

The usability test `candidate > 0` is applied to the 6-decimal rounded value, not to `unrounded` (already checked finite and > 0 on line 76). Any padded quote under 5e-7 rounds to 0.0, fails the check, and both padded and unrounded are set to None, so line 90 returns refused_local regardless of how large the cap is. The ledger row (quote_padded null, refused_local, spend 0) is identical to the row written for a NaN or string cost, so the two cases cannot be told apart later. The same happens for a subnormal cost such as 5e-324. This fails closed, so no spend is at risk, but it is a false refusal contrary to SPEC item 2 (refuse only when the padded quote is over the cap) and the comment on line 67. No test covers a positive quote below the rounding resolution. Reported by audit_permissions and audit_economics; merged. Fix: decide on `unrounded` and keep the rounded value for display only, or round at the gateway's actual billing granularity.

**Reproduction**

Offline harness, no network (stubs only the free estimate call; run from an empty temp dir so ledger.jsonl lands there): python3 -c "import io,sys,runpy,urllib.request; B=sys.argv.pop(1).encode(); urllib.request.urlopen=lambda *a,**k: io.BytesIO(B); sys.argv[0]='guard.py'; runpy.run_path('<repo>/spend-guard/guard.py', run_name='__main__')" BODY --user Hi --max-tokens N --cap C. BODY='{"estimated_cost_flop": 1e-07, "estimated_input_tokens": 3, "estimated_output_tokens": 8}' --max-tokens 8 --cap 1. unrounded = 2.8e-7, finite and > 0, far under cap 1. Expected: would_send. Actual (verified): {quote_raw: 1e-07, quote_padded: null, cap: 1.0, decision: refused_local, spend: 0}. With estimated_cost_flop 2e-07 the same command gives would_send with quote_padded 1e-06, so the cut-off is the rounding step, not the cap. estimated_cost_flop 5e-324 is also refused with quote_padded null.

### 6. Low: A zero input-token count is accepted for a non-empty prompt, so the prompt's cost drops out of the cap check, and the suite locks this in

`spend-guard/guard.py:73`

```
            if total > 0:
```

Only the sum tin+tout must be positive. estimated_input_tokens = 0 (or -0.0) is accepted although the prompt always contains at least --user (required) plus any --system text, so a quote that omits the input cost entirely is treated as valid, and the pad is spread over output tokens only. test_guard_numeric.py lines 258-268 (test_one_sided_zero_count_would_send, test_negative_zero_count_would_send) assert would_send for this input, so the suite treats an impossible quote as a valid one. Impact: with a 10,000-character system prompt a quote reporting 0 input tokens passes a cap that the real input would exceed at the quote's own rate. Reported by audit_flow (audit_math noted the test lock-in); merged. Fix: require tin >= 1 when the prompt is non-empty (or a conservative lower bound from len(prompt)), and change the two tests to expect refused_local.

**Reproduction**

Offline harness, no network (stubs only the free estimate call; run from an empty temp dir so ledger.jsonl lands there): python3 -c "import io,sys,runpy,urllib.request; B=sys.argv.pop(1).encode(); urllib.request.urlopen=lambda *a,**k: io.BytesIO(B); sys.argv[0]='guard.py'; runpy.run_path('<repo>/spend-guard/guard.py', run_name='__main__')" BODY --user Hi --max-tokens N --cap C. Run with --system "$(python3 -c 'print("x "*5000)')" --user Hi --max-tokens 1 --cap 0.1 and BODY='{"estimated_cost_flop":0.004,"estimated_input_tokens":0,"estimated_output_tokens":1}'. Expected: refused_local (a 10,000-character prompt cannot cost 0 input tokens; at 0.004 per token several thousand input tokens far exceed 0.1). Actual (verified): decision would_send, quote_padded 0.084, cap 0.1.

### 7. Low: --cap is parsed with float(), which accepts underscores, non-ASCII digits, whitespace and overflow literals, so a mistyped cap silently becomes a different, larger number

`spend-guard/guard.py:54`

```
    ap.add_argument("--cap", type=float, required=True)
```

argparse type=float hands the string to float(), whose grammar is wider than the decimal amount README and SPEC show. float('0_5') is 5.0, float('1_0') is 10.0, float('\u0661') is 1.0, float(' 1 ') is 1.0 and float('1e400') is inf. json_number(a.cap) on line 87 only rejects nan and inf, so the underscore spellings become a positive, finite, well-formed cap and the decision proceeds against a number the operator did not type: a cap typed 0_5 instead of 0.5 is enforced as 5.0, ten times the intended limit, with no warning in the ledger. The inf/nan cases are refused, but only after the estimate request has been made, whereas '--cap abc' exits 2 before any request, so cap validation is split across two places. The only cap-parsing tests are 'abc', 'nan', 'inf' and '-1'. Reported by audit_economics and audit_permissions; merged. Fix: parse --cap with an argparse type function that accepts only an ASCII decimal (or decimal.Decimal) that is finite and > 0, and exit 2 otherwise so a bad cap never reaches the network.

**Reproduction**

Offline harness, no network (stubs only the free estimate call; run from an empty temp dir so ledger.jsonl lands there): python3 -c "import io,sys,runpy,urllib.request; B=sys.argv.pop(1).encode(); urllib.request.urlopen=lambda *a,**k: io.BytesIO(B); sys.argv[0]='guard.py'; runpy.run_path('<repo>/spend-guard/guard.py', run_name='__main__')" BODY --user Hi --max-tokens N --cap C. BODY='{"estimated_cost_flop":0.066,"estimated_input_tokens":3,"estimated_output_tokens":8}' --max-tokens 8 --cap 0_5. Expected: exit 2 with an argparse error about --cap, as for --cap abc, and no ledger row. Actual (verified): exit 0, {quote_padded: 0.186, cap: 5.0, decision: would_send}. Same shape with --cap 1_0 (cap 10.0), --cap $'\u0661' (cap 1.0) and --cap ' 1 ' (cap 1.0). With --cap 1e400 one estimate request is made and then refused_local with cap null.

### 8. Low: receipts.py crashes with an uncaught exception on any non-object JSON body and on non-HTTP network errors instead of reporting a structured state

`spend-guard/receipts.py:30`

```
    rows = body if isinstance(body, list) else body.get("receipts", body.get("data", [body]))
```

After a 200 response the body is assumed to be a list of dicts or a dict holding one; a JSON string, number, list of non-dicts, or a 'receipts' value that is not a list raises AttributeError on line 30 or inside classify_receipt (guard.py line 40). On an HTTP error, get() returns whatever json.load produced, and line 26 calls body.get on it, so a 401/503 whose body is a JSON array or string also raises AttributeError: no http code, no key_expired flag, and --last-known is never printed. get() catches only urllib.error.HTTPError, so a timeout, DNS failure or connection reset surfaces as an uncaught urllib.error.URLError. In each case the program exits 1 with a traceback and no machine-readable row, contrary to SPEC item 5 and README (report the state as given; expired key gets its own flag). The key is not in the traceback, so this is robustness, not a leak. receipts.py has no tests. Reported by audit_permissions and audit_economics; merged. Fix: coerce the body (`body = body if isinstance(body, dict) else {}` on the error path; skip or report non-dict rows on the success path) and catch URLError alongside HTTPError, returning {"http": null, "error": str(e), "state": null}.

**Reproduction**

Offline, from spend-guard/ so `from guard import ...` resolves, with a throwaway key file (not a real key): stub urllib.request.urlopen before runpy.run_path('receipts.py', run_name='__main__') with sys.argv = ['receipts.py', '--key-file', kf]. (1) urlopen raises urllib.error.HTTPError(url, 401, 'Unauthorized', {}, io.BytesIO(b'["not","a","dict"]')). Expected: {"http": 401, "error": null, "state": null}. Actual (verified): AttributeError: 'list' object has no attribute 'get', exit 1, nothing printed. (2) urlopen returns an io.BytesIO with status 200 and body '"ok"' -> AttributeError 'str' object has no attribute 'get' (line 30); body '[1,2]' -> AttributeError 'int' object has no attribute 'get' (classify_receipt); body '{"receipts":"x"}' -> AttributeError 'str'. (3) urlopen raises urllib.error.URLError('timed out') -> uncaught URLError, exit 1. Expected in each case: one JSON row with state null and an error field, exit 0.

### 9. Low: receipts.py sends the first string whose field name contains 'key' as the bearer, so a key file with a key_id field never authenticates and an array file leaks its whole text as the token

`spend-guard/receipts.py:21`

```
        j = json.loads(raw); key = next(v for k, v in j.items() if isinstance(v, str) and any(w in k.lower() for w in ("key", "token", "bearer")))
```

Selection iterates the JSON object in insertion order and takes the first string value whose key name contains key, token or bearer. A key file such as {"key_id": "k_123", "api_key": "sk_..."} sends k_123 as the bearer, every receipt read returns 401, and the output reads like an expired or invalid key. If the file is a JSON array, .items() raises AttributeError, which the except clause swallows, and the entire file text is sent as the bearer. Reported by audit_permissions; audit_economics did not cover it. Fix: look up a fixed list of exact field names in priority order (api_key, key, token), and fail with a clear message when none is present.

**Reproduction**

Offline, from spend-guard/: write a temp key file containing {"key_id": "k_123", "api_key": "sk_secret"} (not a real key), stub urllib.request.urlopen to record req.get_header('Authorization') and return io.BytesIO(b'[]') with status 200, then runpy.run_path('receipts.py', run_name='__main__') with sys.argv = ['receipts.py', '--key-file', kf]. Expected: Authorization 'Bearer sk_secret'. Actual (verified): 'Bearer k_123'. With the key file '["sk_a"]' the captured header is 'Bearer ["sk_a"]'.

### 10. Low: classify_receipt passes NaN and non-numeric spends through unvalidated, so receipts.py can emit invalid JSON

`spend-guard/guard.py:43`

```
            "settled": s,  # None stays None: blank, never 0
```

classify_receipt copies settled_spend and reserved_spend as given. Python's json.load accepts the NaN token, so a receipt with settled_spend NaN is printed by receipts.py line 31 (json.dumps with default allow_nan=True) as "settled": NaN, which is not valid JSON and fails strict parsers. main() uses allow_nan=False for exactly this reason, but this path does not. Booleans (false), negative values and strings are also reported as settled/reserved amounts as if valid, and false compares equal to 0, the 'never 0' case the docstring rules out. Reported by audit_math. Fix: run both spends through json_number (None for anything else, plus a flag that the gateway value was invalid) and dump with allow_nan=False.

**Reproduction**

From spend-guard/: python3 -c "import json; from guard import classify_receipt; print(json.dumps(classify_receipt(json.loads('{\"settled_spend\": NaN}'))))" prints {..., "settled": NaN, ...}; parsing that with json.loads(out, parse_constant=<raise>) raises ValueError('NaN') (verified). Expected: settled null or flagged invalid, and valid JSON output. Also {"settled_spend": false} yields "settled": false and {"settled_spend": -0.432, "reserved_spend": "0.05"} yields those values unchanged (verified).

### 11. Info: Locally decidable refusals (negative --max-tokens, cap <= 0, nan or inf cap) are detected only after the estimate request, so the prompt is sent for a request that was always going to be refused

`spend-guard/guard.py:58`

```
        e = estimate(a.model, prompt, a.max_tokens)
```

The checks a.max_tokens >= 0 and cap > 0 on line 90 run after estimate() has already sent the model, the prompt and the negative max_tokens to the gateway. Nothing in the result can turn these inputs into a send, so the call is wasted and the prompt leaves the machine needlessly. The estimate endpoint is free, so there is no spend impact; this is a fail-closed ordering gap. test_negative_cli_max_tokens_refuses asserts urlopen == 1 for this input, so a reorder also needs that expectation updated. Reported by audit_economics; overlaps the 1e400 case in the --cap finding. Fix: validate max_tokens and cap right after parse_args, write the refused_local row, and skip the estimate.

**Reproduction**

Offline harness, no network (stubs only the free estimate call; run from an empty temp dir so ledger.jsonl lands there): python3 -c "import io,sys,runpy,urllib.request; B=sys.argv.pop(1).encode(); urllib.request.urlopen=lambda *a,**k: io.BytesIO(B); sys.argv[0]='guard.py'; runpy.run_path('<repo>/spend-guard/guard.py', run_name='__main__')" BODY --user Hi --max-tokens N --cap C. BODY='{"estimated_cost_flop":0.066,"estimated_input_tokens":3,"estimated_output_tokens":8}' --max-tokens -5 --cap 1. Expected: refused_local with no estimate request. Actual (verified): refused_local, but the stubbed urlopen is invoked once first (count 1). Same for --cap 0, --cap -1 and --cap nan.

### 12. Info: guard.py exits 0 on refused_local and changes the type of the spend field between rows, so shell callers cannot gate on it

`spend-guard/guard.py:96`

```
    print(json.dumps(rec, indent=1, allow_nan=False))
```

main() prints the record and returns without sys.exit; both would_send and refused_local end with status 0, and the only signal is the decision field in stdout. README presents the tool as a gate (refuse locally if the result is over your cap), and `python3 guard.py ... && send.sh` would proceed on a refusal. The spend field is integer 0 on refusal and the string "not_sent" on approval, which a consumer summing the ledger must special-case. Neither is a cap bypass inside guard.py; both are integration hazards for the live mode SPEC.md plans. No test asserts the exit code of a refusal. Reported by audit_economics. Fix: sys.exit(1) (or a dedicated code) on refused_local and keep spend numeric or null.

**Reproduction**

Offline harness, no network (stubs only the free estimate call; run from an empty temp dir so ledger.jsonl lands there): python3 -c "import io,sys,runpy,urllib.request; B=sys.argv.pop(1).encode(); urllib.request.urlopen=lambda *a,**k: io.BytesIO(B); sys.argv[0]='guard.py'; runpy.run_path('<repo>/spend-guard/guard.py', run_name='__main__')" BODY --user Hi --max-tokens N --cap C. BODY='{"estimated_cost_flop":0.066,"estimated_input_tokens":3,"estimated_output_tokens":8}' --max-tokens 8 --cap 0.05 >/dev/null; echo rc=$?. Expected: non-zero status for the refusal (0.186 > 0.05). Actual (verified): rc=0, identical to the would_send case with --cap 1; the refused row has spend 0 and the approved row spend "not_sent".

### 13. Info: Numeric regression suite does not exercise the edges that misbehave: output tokens below --max-tokens, large-int precision, pad underflow, display rounding past the cap, sub-microFLOP quotes, cap spel

`spend-guard/test_guard_numeric.py:135`

```
    def test_finite_under_cap_would_send(self):
```

The suite covers type confusion (bool, string, nested object, NaN/inf, fractional counts) well, but every numeric case uses 0.066/3/8 or trivial values, and both suites always pass --max-tokens 8 with estimated_output_tokens 8. Untested: estimated_output_tokens smaller than --max-tokens, a cap exactly equal to the unrounded padded quote, integer costs above 2**53, token counts beyond float range, a quote_padded that rounds above the cap on a would_send row, a positive quote below 5e-7, --cap 0, --cap 0_5, --cap 1e400, the exit code of a refusal, and receipts.py entirely (test_guard_numeric.py line 7 says so). Two tests (lines 258-268) assert would_send for a 0-input-token quote on a non-empty prompt, locking in the defect reported above. Each low/medium finding in this report is an input the suite would have caught while it passes 32/32 and test_audit_repro.py passes 4/4. Reported by audit_permissions; merged with test notes from audit_flow and audit_economics. Fix: add those inputs as cases plus a small property test asserting decision == would_send implies raw <= cap, unrounded <= cap and tout >= max_tokens.

**Reproduction**

grep -n '2\*\*53\|1e-7\|0.0499997\|0_5\|max-tokens", "1000\|output_tokens": 1,' spend-guard/test_guard_numeric.py spend-guard/test_audit_repro.py returns no case. Running `python3 -B spend-guard/test_guard_numeric.py` and `python3 -B spend-guard/test_audit_repro.py` at 288a03e: both OK (32 and 4 tests), while the inputs listed in the findings above produce the described contradictions through the same stubbed harness.

---

Judge's submission `fbf3c4605e347647d81df7c09c76e7526c6e7f9c107b0866ae1005be7dcc82d2`, 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.
