wai-vector: WOS1 and WVG1 with the refusal order, the interval check and the cost (D7-2; stacked on #215) #216
Loading…
Reference in a new issue
No description provided.
Delete branch "wai/d7-2-vector-formats"
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?
Adds
wai/wai-vector, the reference crate ofwai.graphics.vector, with the corpuswai/vector-conformance/and the workflowwai-vector.yml. This is phase D7-2 of D7: the formats, the order in which a sink refuses them, and the arithmetic a sink loads with (the clock, track evaluation, the load-time interval check and the per-frame cost). The raster (RS1–RS9) and the frames follow in D7-3, #217, which is stacked on this branch.Stacked on #215 (
wai/d7-1-graphics-text, headf66c09ed). The branch starts at #215's head, so until #215 merges this diff also shows the extension text. Merge #215 first. Nothing is registered.What lands
wai/wai-vector. It isno_stdwithallocand has no dependencies. SHA-256 iswai-ops's, included by#[path](src/sha256.rs), so the tree keeps one. The crate's files:refuse.rs: the codes of §2.1 and §2.7, and the request errors;bounds.rs: every cap of §2.6 and every bound this PR relies on, with compile-time assertions;arith.rs:R_rat,R_half_up,div255, both quantisers, and the straight-alpha channel;wos1.rs: §2.1's eleven load checks in order, and the writer;wvg1.rs:split), with nothing allocated until the exact length holds;length + 1bytes and hashing before parsing (step 6);clock.rs: the frame range by division;track.rs: §3.2;place.rs: placement (§3.3), the interval check (§3.4(a), step 8) and the cost (§3.4(b), step 9);form.rs: the JSON authoring form (§2.12). It has one member per field, zero fields included, and packs back to the same bytes;scene.rs: a builder that writes canonical payloads (authoring);vectors.rsandvectors/: the generator and the recomputation.The CLI (
--features std):check,cost,pack,unpack,vectors genandvectors verify.wasm/: awasm32-unknown-unknownmodule with a C ABI and no bindings generator. Itsverify.mjsrecomputes every expected file, and every authoring form, under Node.fuzz/seeds/: seeds for the deterministic fuzz: a payload that uses every table, and a set with every point tag. The fuzz itself istests/fuzz_smoke.rs. There is nocargo-fuzztarget, because it would bring a third-party dependency.wai/vector-conformance/: 268 vectors, written bywai-vector vectors genand verified byvectors verify.**Provenance.**line. Every payload, set and string is written here from formulas, with no media and no font.sha256.jsonstarts with thediv255table entry that §22.6 asks for, soevery_decoded_output_states_a_sha256checks it. No exclusion row is needed.arithdiv255over every input, both quantisers over[−2^18, 2^18], the straight-alpha channel over everyc ≤ A,R_ratandR_half_upat ties and at the driver's widths, and track evaluation at the extremescanonicalrefusalsgeometry2^22 − 1and2^22on each axis; at a second size; for an inactive item, a clip, enclosing groups and a glyph with no points; and the true-range casecost2^32 − 1(accepted) and2^32; stated costs off by one; a reused path; touching windows; a group; review A's shapes.github/workflows/wai-vector.yml:native(linux, linux-arm64, macos; debug and release tests, thenvectors verify);wasip2(the tests andvectors verifyunder wasmtime 38.0.3);node(verify.mjsunder Node 22);fuzz(2,000 mutations per seed, the peak-allocation test, the comparison count, andcheck-workflow-concurrency.sh).concurrencyblock. Its filters are §11.5's base set:wai/wai-vector/**,wai/wai-ops/src/sha256.rs,wai/vector-conformance/**,wai/tools/wai_vector_*.py,wai/extensions/graphics-vector.mdand the workflow. Addingwai/vector-conformance/also fireswai-conformance.ymlthrough itswai/*-conformance/**filter.Tests, and the falsification of each
Each test was committed first. Then the code it guards was broken, the named test was shown to fail, the code was restored through git, and the test was shown to pass. Each row is one run, on the dev Mac, debug unless marked. The whole table was run again on this PR's final head (
5b0f2abd), with the same results.arith::div255_over_every_input(andR_rat,R_half_up, the straight-alpha channel, against exact rationals)div255(128) is the half-up rounding of x/255refusal_order::wos1_load_check_order(each code; five fault pairs)check 4 before check 5refusal_order::record_pass_reports_field_range_before_any_cross_table_check(kind = 4, ref = 0andinterp = 2 + j,j ≥ n_tables, each beside a 5(a) fault)payload_malformedwherefield_rangewas expectedrefusal_order::the_four_new_sum_rules(one vector each for path contours, path points, strings, glyphs and clusters)n_clusterssum droppedrefusals/s5a-clusters-sum was acceptedrefusal_order::exact_length_and_trailing_bytesunwrap_err()on anOkcanonical::canonical_round_trip_on_every_vector(parse then write, and form then pack)every-table: form round tripcanonical::duplicates_by_sorting_comparison_count(review A's F10 shape at n = 65,535: 1,152,100 comparisons, against the bound 2·n·⌈log2 n⌉ = 2,097,120 and 2,147,385,345 pairs)more than 2097120 comparisonsclock_track::frame_range_by_division(gup tou64::MAXatP = 2^40)g·Pformed ini64attempt to multiply with overflow; release:frame 16777216 is out of range(the product wraps to 0)clock_track::integer_speed_roll_lands_on_the_grid(M2's 750-frame roll at four rates) andvalues_stay_between_their_keys(M5)25/1, k = 1, 750 frames, frame 1geometry_cost::interval_check_boundaryandinterval_not_true_rangeunwrap_err()on anOk: the document was acceptedgeometry_cost::cost_counts_a_reused_path_per_use,cost_ceiling_boundarybw·bhterm dropped; (2) a reused path counted onceassertion left == rightat lines 70 and 71held_sets::a_counting_resolver_sees_at_most_length_plus_one_bytesthe sink read 1048950 bytes of a set of length 374fuzz_smoke::fuzz_smoke(no panic; every accepted input writes back to its bytes)index out of bounds(debug and release)peak_alloc::peak_allocation_is_linear_in_the_input(≤ 6·input + 64 KiB over the corpus; worst 5.33×; nothing reserved for a header that claims the caps)a header claiming the caps made the parser reserve 53477376 bytesAlso:
refusal_order::steps_in_ordercovers 14 pairs across steps 1–7.refusal_order::every_vector_has_the_code_its_note_names,corpus::*andgeometry_cost::review_a_attack_payloads_are_refused(below).Review A's attack payloads
too_large(step 9)too_large(step 9); one item alone is acceptedtoo_large(step 9)WOS1too_large(step 2,held_len); 2 sets of 2^25 bytes stay within2^26and areoutline_not_heldpayload_malformed(5(g)), found by sortingAt full scale, built in memory (
geometry_cost::review_a_full_scale_costs), the cost reproduces RC2's figures exactly:2^24 + 65,536 · c(16,381).Cross-checks
Review A's
core.pyand the design's prototypes. A scratch script reads the committed bytes withstruct, sharing no code with the crate. Usingcore.r_rat,core.rhu,merged_checks.hull_mulandreview_checks.cost_fill, it recomputes:arithtable and case (div255, both quantisers, the straight-alpha channel,R_rat,R_half_up, and track evaluation throughmerged_checks.eval_track);core.contour_curves;Agreement on all of it, with one difference, from the text (below). With a cost, an interpolated value and a geometry verdict planted wrong, the script reports all three.
The tree-wide tests:
corpus_provenance,int_kernels_vectors::every_decoded_output_states_a_sha256andsigner_ids_are_labels: green.**Provenance.**label changed,corpus_provenancefails and namesvector-conformance/README.md. Withsha256.jsonremoved, the sidecar test fails on it. So both read this corpus.Where the text, the design and the prototypes differ (the text is followed)
P_0MUST be on-curve, in every contour.core.pyandreview_checks.pyreturn early for a one-point contour and accept a control there.refusals/wos1-grammar-one-point-controlisoutline_malformed, the one contour on which the cross-check and the crate disagree.glyph_count = 0only withbyte_len ≥ 1, and the converse, read only their own record.ruby_len ≥ 1into the record pass for that reason. By the same reasoning these would befield_rangeat step 4. The text could choose either way.payload_malformed;outline_malformedfor a set), because the magic cannot be read. A sentence in §2.7 step 1 and §2.1 check 1 would settle it.wai-vector vectors gen(§22.6'ssrc/bin/wai-vector.rs), not §9.1'swai_vector_vectors gen.vectors/refusals/withexpected/refusals/(§22.6), not inrefusals/withrefusals.json(§9.1).authorfeature, because the verifier's round trip (§2.5) needs the writer.authorcan arrive with D7-5'sWSF1.Legs and gate
Locally, on the dev Mac, every vector is identical on three engines:
vectors verify(268 vectors, 775 generated files);wasm32-wasip2under wasmtime 49.0.2: every test, andvectors verify;wasm32-unknown-unknownunder Node 26.11.1:verify.mjs(268 vectors, 228 authoring forms).CI pins wasmtime 38.0.3 and Node 22.
A second instruction-set architecture (SPEC §7 asks it of frozen digests): a static
x86_64-unknown-linux-muslbuild of the tool ranvectors verifyon an x86_64 Linux host. All 268 vectors and 775 generated files are identical.Fuzz at depth: 520,000 inputs from 260 seeds; no panic; 1,214 accepted inputs, each written back to its bytes.
Clippy: clean on every target with
--features std, except one lint inwai-ops'ssha256.rs, which this crate includes by path and does not edit.The wai-rs gate (
gate.sh d7-2): build, lib tests, carriage, the corpus loop (21 of 21), fast kernels, byte-exact, wai-web wasm32 and the neural gates are all GREEN.Public text:
check-public-names.shwith the private list: clean on each of the 544 changed files, one per invocation, and at CI scope (1,818 files);check-comparative-prose.shandcheck-no-vanity-metrics.sh: clean;check-wai-section-refs.py: clean;check-workflow-concurrency.sh: clean.Merge notes
wai/wai-vector/,wai/vector-conformance/and.github/workflows/.wai-vector.yml(native×3,wasip2,node,fuzz);wai-conformance.yml(corpus_provenance,every_decoded_output_states_a_sha256);no-vanity-metrics.yml.🤖 Generated with Claude Code