Skip to main content

Module verify

Module verify 

Source
Expand description

pounce verify <problem.nl> <claim.sol> — independent solution checker.

§Why this exists

When pounce is a tool an agent calls, the agent should never be the thing you trust for “the solution satisfies the constraints.” Trust belongs to a small, deterministic checker that re-derives the answer from the canonical problem — not from the agent’s narration and not even from the solver’s own exit string. Optimization is the rare setting where this is cheap: a claimed x* is just numbers, and feasibility is one constraint evaluation (g_l ≤ g(x*) ≤ g_u, x_l ≤ x* ≤ x_u), O(nnz) work with no resolve.

pounce verify loads the canonical .nl, reads a claimed .sol, and reports the worst constraint/bound violation (and, when the .sol carries constraint duals, a first-order/KKT stationarity residual). It defends the three agent-workflow failure modes:

  • fabrication (“here’s a solution that looks like pounce ran”) — invented numbers fail the residual check against the real model;
  • ignoring the solver — a downstream consumer gates on the receipt’s verified: true plus the problem hash, not on prose;
  • solving the wrong problem (dropping/relaxing a constraint to dodge infeasibility) — the check runs against the canonical constraints and bounds, so a point that is only feasible for a relaxed model is caught here.

The JSON receipt content-addresses both inputs by SHA-256 so a consumer can confirm which problem was verified. When the POUNCE_VERIFY_KEY environment variable holds a secret the agent does not have, the receipt is additionally signed with HMAC-SHA256 over a float-free preimage (see signing_preimage) — so an agent cannot mint a receipt that a consumer holding the key will accept. The consumer recomputes the HMAC over the same preimage and compares.

Verdict / exit code: 0 when every violation is within tolerance (FEASIBLE); 20 when a violation exceeds tolerance (INFEASIBLE); 2 on a usage or I/O error. Optimality is reported but, by default, does not gate — feasibility is the rigorous, sign-convention-independent guarantee; pass --require-optimal to also gate on the stationarity residual.

§Two different complementarity quantities (gh #516)

“Complementarity” names two distinct residuals, and printing either one under the bare label invites a comparison against the other:

  • constraint complementarity — max_i |λ_i| · dist(g_i, nearest finite side) over rows, from the .sol’s constraint duals. This is the one verify has always computed.
  • bound complementarity — max_j max(|z_L·(x−x_L)|, |z_U·(x_U−x)|) over variables, from the bound multipliers. This is what Ipopt prints as Complementarity, and it needs the ipopt_zL_out / ipopt_zU_out .sol suffixes.

They can differ by many orders of magnitude at the same point and neither is wrong for what it measures. verify therefore names both explicitly, reads the bound multipliers when the .sol carries them, and says “not checked” — rather than nothing — when it does not.

Those same suffixes also sharpen stationarity: without them the residual is bound-projected and cannot see a bound multiplier that is missing or wrong (gh #495); with them the exact residual is available, and --require-optimal gates on it.

Modules§

sha256

Structs§

RowReport
VerifyArgs
Parsed verify subcommand arguments.
VerifyOutcome
The fully-evaluated verification result. Serialized to the JSON receipt and rendered to the console.

Constants§

KEY_ENV
Environment variable holding the HMAC key. When set (non-empty) and a --json-output receipt is requested, the receipt is signed.

Functions§

run
run_from_argv
Entry point dispatched from main when argv[1] == “verify”.
signing_preimage
The exact byte string that gets HMAC-signed. Deliberately float-free — only hex hashes, integer counts, and the verdict — so any language reproduces it byte-for-byte (no float-formatting parity problems between Rust and a Python/JS consumer). One key=value per line, fixed order, trailing newline. The consumer re-derives this from the receipt fields, recomputes HMAC-SHA256(key, preimage), and compares to signature. Documented in docs/src/verify.md.