open-standards/proof/SPEC.md
Transaction Science 0503e69b32
All checks were successful
standards workspaces build + test / jouleclaw-rs (pull_request) Successful in 1m57s
standards workspaces build + test / aac-rs (pull_request) Successful in 39s
standards workspaces build + test / joule-code-rs (pull_request) Successful in 38s
standards workspaces build + test / joulecontract-rs (pull_request) Successful in 37s
standards workspaces build + test / map-rs (pull_request) Successful in 31s
standards workspaces build + test / mesh-rs (pull_request) Successful in 28s
no-vanity-metrics / Scan committed Markdown for vanity metrics (pull_request) Successful in 3s
standards workspaces build + test / openpay (pull_request) Successful in 1m41s
standards workspaces build + test / eoc-rs (pull_request) Successful in 1m49s
standards workspaces build + test / jcp-rs (pull_request) Successful in 1m17s
standards workspaces build + test / proof-rs (pull_request) Successful in 1m11s
standards workspaces build + test / sandbox-rs (pull_request) Successful in 2m7s
standards workspaces build + test / smart-byte-rs (pull_request) Successful in 2m33s
standards workspaces build + test / arl-rs (pull_request) Successful in 5m52s
standards workspaces build + test / joule-ui-rs (pull_request) Successful in 5m24s
standards workspaces build + test / jouledesk-rs (pull_request) Successful in 4m25s
standards workspaces build + test / joulehook-rs (pull_request) Successful in 10m23s
standards workspaces build + test / sense-rs (pull_request) Successful in 7m31s
standards workspaces build + test / macOS energy backend (jcp-energy) (pull_request) Successful in 4m38s
proof-cascade: IOReport is a hardware reading only while every CPU rail it sums keeps advancing
The IOReport watch, `CPU_RAILS`, was "any one": Live as soon as ONE CPU rail
met the sustained-advance rule. The power reading sums every CPU rail
(`counts_once`), and on a multi-die part that is two rails, `DIE_0_CPU
Energy` and `DIE_1_CPU Energy` on an M3 Ultra. So one die live beside a
frozen one would have admitted the meter and bracketed one die's energy as
the whole CPU's, as `HwShunt`. RAPL was already held to every package
counter it sums (9334ec0c); IOReport now is too.

The rule: Live only when every CPU rail present in the samples advanced in
at least five intervals, in at least 80% of those observed, over at least
300 ms; Frozen otherwise; Absent with no CPU rail. A rail is present if it is
in any sample, the first included (the watch already registered every
sample's rails; a test now pins it), and is judged over every interval
observed. With every source on the same rule there is nothing for
`Watched.every` to choose, so the field is gone: a `Watched` is which
channels to watch, and the watch needs every one of them.

Two test-side checks encoded the old rule and now encode this one: the
oracle behind the real-IOReport tests says "moved" only when every CPU rail
it reads changed at five reads or more and at 80% of them, and
`assert_verdict_matches_record`, the tests' own reading of a probe record,
calls a run Live only when every CPU rail seen, the first sample's included,
rose in at least 5 intervals at 80% over 300 ms. The oracle reads exactly the
probe's rails on both hosts: one on this M5 Max, the two dies on the M3
Ultra.

Docs: the meter module doc, `IoReportStatus`, `CpuRail`, `Watched`,
`CPU_RAILS`, `RailWatch` and `PACKAGES`; SPEC §5's liveness clause (for
IOReport, every CPU rail the figure sums, one per die, must advance on its
own); the proof README.

Tests, all scripted (macOS here). The rule:
`watch_two_dies_that_both_keep_advancing_is_live` (live at the tenth
interval, or at the fifteenth when one die misses its first three, either
die), `watch_one_die_that_keeps_advancing_beside_a_frozen_one_is_frozen` (the
other die held, stepped once, stepped five times, or advanced three
intervals in five, either die; and twin `CPU Energy` channels),
`watch_one_die_alone_that_keeps_advancing_is_live`, and
`watch_a_die_in_only_some_samples_is_a_rail_the_watch_needs`. The whole probe
loop: `the_probe_needs_every_die_to_keep_advancing`. The real `host_status()`
on scripted reads: `host_status_calls_two_dies_that_keep_advancing_live` and
`host_status_calls_one_live_die_beside_a_frozen_one_frozen`. The single-rail
tests are unchanged. Two existing tests encoded "any one":
`two_advances_of_the_same_rail_are_not_live_and_sustained_ones_are` held
DIE_0 constant and expected Live from DIE_1 alone, so DIE_0 now advances at
every interval and the verdict is still DIE_1's; and
`rapl_every_package_counter_must_keep_advancing` drops its contrast with the
any-one-rail watch, which no longer exists.

proof-cascade: 86 lib tests and the three integration files pass on this M5
Max. The same lib test binary passes 86/86 on the M3 Ultra (macOS 27.0.1),
where the real probe gave `CpuRailFrozen` over 25 and 26 intervals and the
oracle saw 0 changes on both dies in 19 reads. `cargo test --workspace`
passes (126 tests) and the proof-conformance binary passes all 10 checks.
Clippy adds nothing for aarch64-apple-darwin (two warnings in the IOReport
FFI predate this) and is clean for x86_64-unknown-linux-gnu.

Falsified, each restored from git and the suite re-run green: the rule back
to any one rail fails the one-live-die-beside-a-frozen-one tests (rule, loop
and `host_status`), the two-live-dies test, the some-samples test, the
updated two-advances test and the RAPL every-package test; registering a
rail only once an interval has closed fails
`watch_a_die_in_only_some_samples_is_a_rail_the_watch_needs`.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 21:33:56 -04:00

14 KiB
Raw Permalink Blame History

Proof v0.1 — A Carrier for Verified Work

Status: v0.1.0 draft. Licence: specification text CC-BY-4.0; reference code Apache-2.0.

A vendor-neutral standard for energy-accounted formal verification. A proof obligation is content-addressed; a solver is anything that can attempt one at a declared tier; a discharged obligation seals as a signed receipt that records which tier closed it and the joules it cost. The standard fixes the one axis the field leaves implicit — joules per verified proof — and makes it portable: any prover can emit a Proof receipt, and any auditor can verify it with the family's shared JCR-1 tooling.


1. Scope and doctrine

  1. Correctness is the verifier's, not the optimizer's. A solver returns proved only when a verifier would accept the artifact. Energy is never traded for correctness; the standard changes which tier pays for a proof, not whether it is checked.
  2. Cheapest sufficient solver wins. Every obligation goes to the cheapest tier that can discharge it and escalates only on failure (§4).
  3. Energy is reported, honestly. Every closed obligation carries the joules it cost with a provenance tag (§5). An estimate is never dressed as a measurement.
  4. A proof is not a specification. A receipt attests that an obligation was discharged. Whether the statement encodes what was meant is a separate, declared property (spec_grounding, §2) that a prover MUST NOT self-certify.
  5. Non-goals. The standard does not mandate a proof language, a solver implementation, or a model. It standardizes the goal, the cascade accounting, and the receipt — not the prover.

2. The proof obligation (REQUIRED)

A goal is { statement, context, kind, intent, grounding }:

  • statement — the formal proposition, in some context (e.g. lean4@mathlib, smt2). The pair (statement, context) is the unit of identity.
  • kind — a coarse difficulty hint (Trivial, Arithmetic, Decidable, General, Hard). It informs but does not bind routing.
  • intent — free text: what the statement is meant to encode.
  • grounding — the spec-grounding of the statement against intent: Ungrounded (default), HumanReviewed, or CrossChecked. A conforming prover MUST default to Ungrounded and MUST NOT upgrade grounding on its own.

A goal's id is blake3(statement ‖ 0x00 ‖ context) (§3). Identity depends on the statement and context only; intent and grounding do not change it.

3. Canonical serialization and content addressing (REQUIRED)

Content addresses and the signed receipt core use canonical det-CBOR (RFC 8949 §4.2.1): length-first then bytewise key ordering, shortest integer form, and no floats, bools, or nulls in the signed core. A content id is blake3(canonical_cbor(value)), rendered lowercase-hex on the wire. This is the same canonical encoding the JCR-1 receipt signs over (§6), so an id and a receipt agree on bytes.

4. The cascade (REQUIRED)

Solvers occupy a strictly ordered ladder, cheapest first:

Tier Class Energy (reference)
T0 deterministic automation (proof-language tactics) ~free CPU
T1 SMT / decision procedures cheap CPU
T2 a small distilled in-domain prover a small model
T3 a frontier reasoning model — last resort the most expensive rung

A conforming router MUST:

  1. Attempt solvers in non-decreasing tier order.
  2. Stop at the first solver returning proved (the closing tier).
  3. Account every joule spent, including on cheaper solvers that ran and failed first. Those joules are wasted_uj — the disclosed cost of escalation, which MUST NOT be hidden.
  4. Treat a solver's refusal (it cannot decide the goal) as escalation, never as a counter-result. A solver MUST NOT report proved without verifier acceptance.

The reference energy per tier (50 µJ / 5 mJ / 0.5 J / 50 J) is non-normative. The normative requirement is that the figure is reported and honestly tagged (§5), not its value.

5. Energy and provenance (REQUIRED)

Every reading is integer microjoules with a provenance tag:

  • HwShunt — read from a hardware energy interface: a shunt or power meter, the SMC, or a vendor energy counter (RAPL, NVML, Apple IOReport, ...). REQUIRES a present measured figure. A vendor counter's reading is a hardware reading, subject to the liveness requirement below.
  • ModelBased — computed from a calibrated per-operation model (e.g. measured wall time × a power model). A measured figure MAY be absent.
  • Estimator — a coarse constant. A measured figure MUST be absent.

A conforming implementation MUST enforce this coupling: HwShunt without a measured figure, or Estimator with one, is invalid.

Liveness. A reading from a vendor energy counter (RAPL, NVML, Apple IOReport, ...) MAY be graded as a hardware reading only while the counter is demonstrably live for the domain the figure claims: the implementation MUST have observed the counter covering that domain advance under load (for Apple IOReport: every CPU energy rail the figure sums - the rail itself, one per die on a multi-die part - advancing on its own in at least 5 sample intervals, in at least 4 of every 5, over at least 300 ms while the probing thread burns CPU; one rail that held leaves the whole figure not live). A counter that is present but not live - frozen, or republished in lumps - MUST NOT produce a hardware-graded figure; the implementation falls back to its estimator or reports the work unmetered, and SHOULD record why. In this standard a hardware-graded figure is HwShunt; the fallback is ModelBased or Estimator.

Batch accounting reports, against a frontier-only baseline (every goal costed at T3), the total joules, the savings, and the energy attributed to each closing tier. The per-tier breakdown is REQUIRED so that Amdahl behaviour — the few hardest goals dominating the joules — is visible rather than averaged away.

5.1 The joule ceiling (OPTIONAL to declare, REQUIRED to honour)

Accounting says what a proof cost. A ceiling says what it is allowed to cost. A standard whose premise is joules-per-verified-proof needs both: the ladder spans a millionfold from T0 to T3, so an unbounded cascade will escalate a goal nobody wanted solved that badly into the most expensive rung available and report the bill afterwards.

A caller MAY declare a ceiling in microjoules for a run. Where one is declared:

  • An implementation MUST NOT begin a rung whose nominal cost would take the run past the ceiling. The check is on the next rung's cost, before it runs. Refusing after the fact spends precisely the joules the ceiling exists to withhold.
  • A ceiling below the cheapest rung's nominal cost attempts nothing and reports that it attempted nothing. This is a coherent request, not an error.
  • A run halted for want of budget MUST be distinguishable from a run no rung could decide. proved = false alone conflates them, and the two are different claims about the world: a budget halt says nothing whatever about whether the goal is provable. An implementation MUST record the tier it refused.
  • Actual spend MAY exceed the ceiling when a solver overshoots its nominal cost. Those joules were really spent, so the receipt MUST report them as spent rather than clamping to the budget. A ceiling bounds what is attempted, not what physics did.
  • Batch accounting MUST count budget halts separately from failures, for the same reason: folding them together makes a batch look mathematically harder than it was.

Where a ceiling was declared, the receipt (§6) carries it, so a proof is not merely costed but demonstrably obtained within a declared budget. A receipt from a run with no ceiling omits the field and is byte-identical to one sealed before this section existed.

5.2 Stand-in solvers (REQUIRED to declare)

A reference stand-in is a solver that returns a verdict to exercise the ladder and discharges nothing — it decides by goal kind, never by inspecting the claim. The reference implementation ships a full ladder of them so the cascade, the accounting and the receipt can be demonstrated without a solver estate.

They are a hazard precisely because the rest of the pipeline is sound. A stand-in at the top rung returns Proved for any goal, with the content address of the goal's own statement as its "artifact" — the claim offered as its own evidence — and the sealing path will sign that into a JCR-1 receipt that verifies under the steward key. Such a receipt is not forged. It is honestly signed and substantively empty, which is worse, because it verifies.

Neither existing qualifier catches this. Provenance qualifies an attempt's energy; SpecGrounding qualifies its intent. Neither says whether the verdict was earned.

  • A solver that is a stand-in MUST declare itself as one.
  • An implementation MUST record, on the result and through any proof cache, whether the closing solver was a stand-in. A cache hit that dropped the marker would launder an unearned proof into an unmarked one.
  • A relying party that will act on a proof MUST check it. Verifying the envelope establishes the signature, not that anything was proved. The marker therefore rides in the signed receipt as closed_by_stub (§6.1), so a party holding only the artifact can make that check without trusting the prover.
  • A stand-in MUST NOT be presented as a decision procedure in any published figure or claim derived from a run.

Note the trait-level default is "not a stand-in", which is the unsafe direction and is deliberate: it keeps real solvers from having to opt out, but it means the declaration is a contract on the implementor, not a property the framework can infer. A new stand-in that forgets to declare itself is indistinguishable from a real one.

6. The receipt (REQUIRED)

A discharged obligation seals as a JCR-1 receipt — the family's shared signed-receipt envelope: canonical det-CBOR inside a COSE_Sign1 envelope (RFC 9052, EdDSA/Ed25519, alg = -8). The proof/1 profile signs a flat uint/text core:

proof_receipt     = "proof/1"
goal_id           = hex(blake3(statement ‖ 0x00 ‖ context))
verdict           = "proved" | "unproved"
closed_by         = "T0".."T3"           ; omitted if unproved
artifact          = hex(content-id)      ; omitted if unproved
total_uj          = uint                 ; all joules, incl. escalation
wasted_uj         = uint                 ; joules on failed cheaper attempts
baseline_uj       = uint                 ; frontier-only cost of this goal
energy_provenance = "HwShunt" | "ModelBased" | "Estimator"
spec_grounding    = "Ungrounded" | "HumanReviewed" | "CrossChecked"
from_cache        = 0 | 1
closed_by_stub    = 1                    ; omitted when the closer was real (§5.2)
ceiling_uj        = uint                 ; omitted when the run was unbounded
steward           = the signer's id (the COSE kid)

A conforming implementation MUST refuse to seal a receipt for an unproved goal, and MUST carry spec_grounding so a receipt for an Ungrounded statement says, in the signed artifact itself, that the statement was not validated against intent. Proof receipts are byte-compatible with the family's JCR-1 verifiers.

6.1 closed_by_stub — the verdict's own qualifier

An implementation MUST emit closed_by_stub = 1 when the rung that closed the goal was a reference stand-in (§5.2), and MUST omit the field otherwise.

§5.2 already requires a relying party to check this before acting on a proof. Until it entered the signed core the marker lived only on the in-process result, so a party holding only the receipt — which is the entire purpose of sealing one — could not perform the check the specification asks of them. A verifier that must consult the prover to learn whether a proof is real is not a verifier.

closed_by does not answer this. It names a tier, and the same tag covers a real decision procedure on one ladder and a stand-in on another; T0 is a propositional decider in a real ladder and a GoalKind gate in the reference one. Nor do the other qualifiers: energy_provenance qualifies the energy and spec_grounding the intent. This is the only field that says whether the verdict was earned.

Omit-when-false is normative, not an optimization. It makes the field free for honest proofs: a receipt closed by a real solver canonicalizes to exactly the bytes it did before this field existed, so every such receipt sealed under an earlier revision still verifies. The byte cost falls only on receipts that were making an unearned claim.

A verified proof SHOULD be memoized by goal id. An identical goal then closes for zero new joules (from_cache = 1, total_uj = 0), and the accumulating corpus of verified proofs — not the routing — is the durable asset. The cache is the energy flywheel: the more proved, the cheaper future proofs become.

8. Conformance

A conforming implementation passes, with real checks:

  • C1 — cheapest-sufficient routing: each goal closes at the cheapest tier that can discharge it.
  • C2 — disclosed escalation: total_uj equals the sum of all attempts, and wasted_uj is exactly the joules of cheaper attempts that failed first.
  • C3 — energy honesty: the ladder is strictly monotone; the batch is accounted against a frontier-only baseline with non-negative savings.
  • C4 — the flywheel: re-proving an identical goal costs zero new joules.
  • C5 — the receipt: a proved result seals to JCR-1 and verifies; a wrong steward fails.
  • C-neg: provenance coupling is enforced; an unproved goal cannot be sealed; a tampered envelope fails; canonical CBOR rejects floats/bools.

Published vectors live in conformance/v1/ (cargo run -p proof-conformance --bin gen-vectors).

9. Non-goals

The standard does not certify a prover's capability, does not validate that a statement encodes a real-world requirement (that is the spec_grounding axis, owned by humans), and does not prescribe a model or a proof language. Efficiency diffuses; the standard's contribution is to make the joules portable and the spec-grounding honest, so that "verify every line" can be reasoned about as a cost, not a slogan.