igneum/docs/analysis/proving-outcome-ledger.md
igneum-labs 2e6914f8c9 The proving outcome ledger (review B F08): every claimed job ends in one outcome; the report reads paid completions, missed deadlines and wasted work by cause
tools/fleet/box-prover.py point 6: each claimed segment is an eligible job and closes once as paid, expired (unpaid, held_expired) or cancelled (disk, export, cut, chain, timeout, shards, statement, sign, refused), active until then; the row carries outcome, cause, deadline, margin at the claim, seconds spent (wasted unless paid) and the deadline miss; the state carries the counters and the wasted seconds by cause; every close is a RESULT outcome line, the run ends with a RESULT ledger line.

tools/fleet/prover-outcomes.py: the report over state files and logs (a log from before the ledger is reconstructed from its RESULT lines; a state row wins over a log row): outcomes, paid completions (segments, shards, IGN, median end to end, median time to pay, median margin), missed deadlines by cause with the median miss (never negative, none on a stale tip), wasted work by cause, throughput per hour and the paid share of the seconds spent, the open jobs. --self-test to known numbers (known-failed first), run by the gate. tools/fleet/night.py sums the fleet's counters into its hourly row. docs/analysis/proving-outcome-ledger.md: the definitions and how to read the three ratios.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-08 19:19:39 +00:00

4.5 KiB

The proving outcome ledger (review B F08)

8 October 2026, the enforced proving lane. Review B, finding F08: the proving pipeline reported waste and deadline censoring, never sustained capacity; "add an outcome ledger for every eligible job: completed, active, expired or explicitly cancelled, then report paid completions, missed deadlines and wasted work by cause".

What an eligible job is

A segment a prover claimed (RESULT claim in tools/fleet/box-prover.py): every block of the segment present in its worklist, every shard open, unpaid and absent from its pool, the deadline (last block's DAA plus the unproven window) at least the margin past the tip. A segment nobody claimed is not a job; the chain's own view of those (pending, unproven, paid per segment) is igneum_getProvingStatus and stays as it was.

The one outcome per job

Outcome When Cause field
active claimed and not yet closed: in export, cut or chain; submitted and waiting for a carrier; held for a retry none
paid a carrying block paid the segment record (RESULT paid) none
expired the deadline passed with no paid record unpaid (submitted, never carried in time), held_expired (a held record past its deadline), never_submitted (log reconstruction only: the log ran 50 DAA past the deadline with no submit)
cancelled this prover gave the job up disk, export, cut, chain, timeout, shards, statement, sign, refused

A job closes once; a second close is ignored. Each segment row in prover-state.json carries outcome, cause, deadline, margin_daa (at the claim), spent_s (export, cut and chain seconds), wasted_s (the same unless paid), miss_daa (an expiry's distance past the deadline at the close), closed_at. The state carries outcomes (paid, active, expired, cancelled), wasted_s by cause and deadline_misses. Every close is a RESULT outcome line and the run ends with a RESULT ledger line naming the still-active segments.

The report

tools/fleet/prover-outcomes.py --state <prover-state.json> ... --log <prover.log> ... [--json] merges the fleet's state files (a state row wins over the same segment in a log) and reconstructs the ledger for a prover from before this change from its RESULT lines (claim, chain, shards, statement, sign, segment_refused, held, submitted, paid, unpaid, held_expired). It prints:

  • Outcome ledger: jobs by outcome.
  • Paid completions: segments, shards, IGN, median end to end, median time from submit to pay, median margin at the claim.
  • Missed deadlines: count by cause, median miss in DAA (never negative; none when the log's last tip is stale), median margin at the claim, the seconds spent on them.
  • Wasted work by cause: jobs and seconds per cause, against the seconds spent in all.
  • Throughput over the covered span: segments paid per hour, shards paid per hour, the paid share of the seconds spent.
  • Active: the open jobs, with whether each is submitted.

--self-test runs a synthetic log (six jobs: paid, timeout, unpaid, shards, held_expired, active) and four old-shape state rows to known numbers; the gate runs it (tools/ci/pre-push.sh). tools/fleet/night.py sums the fleet's counters into its hourly row (outcomes, deadline_misses, wasted_s), which page.py publishes.

Reading it

The question the finding asks is answered by three ratios the report prints: paid jobs over claimed jobs, the paid share of the seconds spent, and the median margin at the claim for paid against expired jobs. A pipeline whose expired jobs claimed with a margin close to the floor (240 DAA) and whose paid jobs claimed wider is sizing its jobs too close to the deadline; the fix is the margin, never a longer exclusive window (the finding's warning: a longer window is tested against slow or malicious claimants before it moves). A pipeline whose waste sits under chain or timeout has a prover problem, under unpaid a carrier problem (records accepted and never carried in time), under refused a chaining problem (fresh records refused while the previous segment waits).

Not in this change

  • The chain-side ledger (every segment the chain planned, claimed or not, with its pending, unproven and paid state per claimant) stays on the node's RPC as it is; a per-segment outcome feed from the observer is the next step if the fleet's view and the chain's view disagree.
  • Job sizing, resumable verified work, early cancellation and bounded assignment protection (the finding's second paragraph) are design items for the pool and prover lanes; this ledger is the instrument they read.