route-record: version 2 prices per operation, ranks by measured failure, binds a hop receipt to its output #552
Loading…
Reference in a new issue
No description provided.
Delete branch "wai/route-physics"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What this changes
WAI's route record (
wai/extensions/route-record.md) gets a version 2 of its price book and its record. It fixes three places where the record did not follow the physics of the work:operationit prices. Entries are ordered by endpoint, then operation. A job's candidates are the entries for its own (capability, operation). An encode and a decode now get their own estimates (§4.2), their own unit-price limit (§4.3) and their own place in the ranking (§4.4).failures:hops,from,to(the basis),failed,billed_price(what the provider billed perdivisorunits for the failed hops) andfailed_ms(the mean time a failed hop took), ornull.failed / (hops − failed)failures on average (a geometric count). Each failure costs the router what the provider billed for it, and costs the job the time it took.cost + ⌊F·failed/(hops−failed)⌋, withF = ⌊q·billed_price/divisor⌋;ttfb_ms + ⌊failed_ms·failed/(hops−failed)⌋.price,ttfbandtargetskeys read these first, then the list figures.Con average and serves with probabilitys, ordering the survivors byC/sminimises the expected spend over the attempts.C/sis exactly the expected figure per successful job.nullfailures, are unknown.null. The rule the text already applies to everynullfigure then ranks them after every stated figure, and among themselves by the list figure. Unknown is never read as "no failures", which would reward going unmeasured, and never as a worst case. No limit reads failures.unit_priceandspendread the list price and the list cost. A line bills the billed attempt.failure_costwaiTransport proposed (wai-additions §27), built on the physics with its basis stated. One difference: an unknown failure is not counted as 0.outputs: the jwp-receipts §2.1 content hashes of what it delivered, in order.subject.outputmust equal their commitment. Otherwise the record is refusedreceipt_output_mismatch.oer-receipts.md§3 now defines that commitment (Output): one output is named by its content hash, which is what the text already implied through jwp-receipts §2.4.5. Several outputs are named by the jwp-receipts §2.2 Merkle root over their content hashes in delivery order, with unlabelled leaves. The objects must be distinct, so that one root names one list.Version 1 documents and records read, route and verify exactly as before. A version-1 reader refuses version 2 (
pricebook_invalid,unknown_version) rather than misreading it. A record's version must match its price book's version, or the record is refusedpricebook_mismatchatrecord.version.Corpus and implementations
live-conformance/routes/vectors.json: every version-1 vector is byte for byte the same except one. The controlrecord_version_2is nowrecord_version_3, because version 2 is now defined; it is still refusedunknown_version. 36 version-2 vectors are added: 10 accepted, plus a control for each new rule. Each control is refused at the reason and place it declares.wai::route_record: addsFailures, the expected figures,output_commitment,content_hashandseal_delivered.sealis unchanged, so PR #550's corpus helpers compile as they are.route_record_verify.pyis written from the text. It computes the commitment withclaim-conformance/_blake3.py, and drops 11 more of its own checks (45 in all).routes/vectors.jsonby vector name, and every vector it reads is unchanged.Tests (Framework,
192.168.4.47)cargo test --no-default-features --features route_record --lib route_record::: 8 passed. The new tests cover the expected figures, when they are unknown or unbounded, and the commitment.cargo test --no-default-features --features route_record --test route_record: 7 passed. The new tests cover version 2's cases andwai route verifyon version-2 vectors.wai_route_record_vectors verify: the corpus regenerates byte for byte, and every vector gets its outcome.python3 -I live-conformance/route_record_verify.py live-conformance/routes(Python 3.14 withcryptography):cargo clippy --no-default-features --features route_record --lib --tests --bin wai_route_record_vectors --bin wai: no warning in the touched files. The warnings it does print are in other modules and come from main.wai breaking --against 4902e6679: 9 breaks, each acknowledged inwai/CHANGELOG.md: the corpus control, plus 8 Rust API changes (new fields and the new reason). It reports "no unacknowledged breaking change".--features receipt_ledger --lib receipt_ledger::: 8 passed;--test receipt_ledger: 4 passed;receipt_ledger_verify.py live-conformance/ledger: status 0.git merge-treeagainst #550's head8642b1394merges cleanly. The CHANGELOG section sits above "Routed media jobs", not at the top of Unreleased, so that it does not collide with #550's insertion. On that trial merge (Framework, never pushed):--test route_record: 7 passed;--lib receipt_ledger::: 10 passed;--lib route_record::: 8 passed;--test receipt_ledger: 5 passed;route_record_verify.py: status 0;receipt_ledger_verify.py: status 0.Falsifications
Every new rule was broken in
src/route_record.rs, one mutation at a time. For each,wai_route_record_vectors verifyran against the committed corpus, then the source was restored. The tree verified clean before the first mutation and after the last.All 29 mutations were caught. In each case the vector that names the rule failed, the run exited 1, and no run panicked. Each line below gives the broken rule, then the vector that caught it.
v2_a_decode_at_its_own_price, along with every version-2 ranking.v2_ranked_by_the_expected_cost;v2_ranked_by_the_expected_time_to_first_byte;v2_the_targets_read_the_expected_time_to_first_byte.im.deltamoves;im.iotamoves.im.epsilonmoves;im.gammamoves;v2_the_ceiling_reads_the_list_cost.fromat mostto;toat mostissued; no billed price, and no failure time, without a failed hop; at least one hop): each one's control is refusedpricebook_mismatchinstead ofpricebook_invalid.v2_price_book_out_of_order_by_operation.price_book_version_3.candidate_mismatch.record_version_3.receipt_output_mismatchcontrols;v2_a_late_hops_receipt_moved_from_another_job.each caught by
v2_served_three_outputsand the single-output vectors.route_record_verify.pyhas its own falsification loop: it drops each of its 45 checks in turn, the 11 new ones included, and the corpus caught every one.Ledger follow-ups (after #550; this PR does not touch the ledger)
outputs(eachint_motiondecode receipt with its codestream) should be checked against the billed hop'soutputs: the content hashes of the envelopes it delivered.WIV1payload.expected_costis never a charge. A job is billed from its billed attempt at the tariff's price. A version-2 record'sexpected_costandfailuresare the router's ranking figures and are never read into a line. No ledger code reads them today.v2_ranked_by_the_expected_cost,v2_failover_whose_failed_hop_delivered_nothing,v2_served_three_outputs,v2_served_after_the_deadline,v2_a_decode_at_its_own_price. Neither verifier needs a code change: both callroute_record::verifyorRRV.verify, which now read version 2. A decode job also gives #550's per-operation tariff a version-2 decode to price.🤖 Generated with Claude Code