wai-vector: the default stroke set and the test repertoire, as WSF1 sources expanded by Annex A, and the text vectors (D7-5) #224

Merged
dcharlot merged 6 commits from wai/d7-5-stroke-set into main 2026-10-12 15:32:12 -04:00
Owner

Adds the in-house outline sets of wai.graphics.vector: the default stroke set and the test repertoire, each drawn here as integer centre lines in the stroke source format WSF1 and expanded by an integer stroker into an ordinary WOS1. The corpus gains a text group, and stroke-glyph members of runs and motion. The extension gains §4 and Annexes A and B. This is phase D7-5 of D7.

It starts from main at 4ab66d24 (with #219 merged), and touches no file that #218 or #220 edits. Nothing is registered.

What lands

  • src/wsf1.rs (feature author):

    • the WSF1 reader, its ten checks in order, and its writer;
    • the pinned table of 64 unit vectors in Q15;
    • the expansion of Annex A. Segment offsets are (R_rat(−dy·ρ, len), R_rat(dx·ρ, len)) with len = isqrt(dx² + dy²). Caps and the outer side of joins take table directions chosen by exact cross-product signs, placed at R_half_up(ρ·u, 15). The inner side of a join takes its two offset points. One closed contour per stroke path, every point on-curve.
  • The default stroke set (src/vectors/stroke.rs): printable ASCII, U+0020–U+007E, each glyph ID its code point. It is drawn from straight segments and ellipse arcs whose points come from the same Q15 table, so the drawing is integers throughout and no outline is traced from any font. Apache-2.0. Its notes carry parent_sha256 (the source's digest), converter=wsf1-expansion/1, licence, licence_sha256, copyright, rfn=none, source, glyphs and metrics, then a blank line, then the Apache License 2.0 text.

  • src/builtin/: the stroke set's bytes (include_bytes! of the corpus file), its SHA-256 and its length, and hold(), which puts them in a sink's set store.

  • The test repertoire (src/vectors/repertoire.rs), Apache-2.0 and corpus-only:

    • Arabic: the isolated, initial, medial and final forms of beh, teh, yeh, seen, lam and meem; alef's two forms; lam-alef, isolated and final; fatha and sukun;
    • Hebrew: shin, lamed, vav and final mem;
    • Devanagari: ha and da with their headlines, the conjunct na-da as one glyph, the i-matra, the ii-matra and the anusvara;
    • a monochrome face for U+1F600;
    • the small shaper the generator uses: the joining table, the lam-alef rule, the reordered i-matra, the conjunct, and authored mark anchors.

    Each joining form's connector, and each headline, runs to the edge of its advance, so the pen's round cap overlaps the neighbour by 2ρ, as joins in production fonts overlap.

  • graphics-vector.md:

    • §4: the sink reads WOS1 only; pinning and carriage; the notes' keys and the licence they carry; the default stroke set; the test repertoire;
    • Annex A: the WSF1 layout and checks, why its caps fit WOS1's, the Q15 table as 512 bytes with their SHA-256, and the expansion;
    • Annex B: the stroke set's and its source's digests and lengths;
    • the status note: §5 and §6 remain headings.
  • vector-conformance/: sources/ (the two WSF1 sources); sets/stroke-ascii.wos1 and sets/test-repertoire.wos1, both held; the new vectors below; and sha256.json's new frame entries. The README's provenance line names the two in-house sets, their drawing sources and the generator. The README now lists the groups that render.

  • wasm/: the Node leg's module is built with default-features = false, a sink's build with no stroker. verify.mjs checks the stroke set's SHA-256 against Annex B.

  • wai-vector.yml:

    • native gains the step cargo build --no-default-features --lib, the sink's build;
    • the filters gain the root LICENSE, whose text the in-house sets carry.
set SHA-256 bytes glyphs points contours
sets/stroke-ascii.wos1 (Annex B) 05476c4658ef7366c7700736487eac4c3c1b7d21264978c7d05bd4c7427736c9 105,523 95 18,368 146
sources/stroke-ascii.wsf1 4eec759d394d021aec8ce99b41e4b25fecb36372c77459ef62df182b9eb8c783 19,181 95 1,591 centre points 146 strokes
sets/test-repertoire.wos1 2310ab64ddcf4d851d4d7c08bbce141fba178f03fdcbb07b05db3863e179846f 59,879 42 9,389 92
sources/test-repertoire.wsf1 f20c628442ecc778e6d1f1ef3994d767730e5dd827f85cf9542a1b7f18661136 15,168 42 669 centre points 92 strokes

The Q15 table's 512 bytes have SHA-256 11c05a1323c8b6dde89a5627d29acecb9163b87dfd169054236fb1d1def66437. Pen radius 80 outline units; upem2 2000. Most of the stroke set's points are round caps, at up to 32 table directions each.

The vectors: 12 new, 331 in all, and 70 new frame digests (sha256.json grows from 66 to 87 entries).

group vector sizes what it pins
text latin-ascii 320×96, 640×192 printable ASCII from the held stroke set: one block over three runs, one cluster per glyph
text arabic 160×64, 320×128 بَيْت سلام right to left: initial, medial, final and isolated forms by joining type, lam-alef, fatha and sukun on their bases, glyphs in visual order, byte_first decreasing
text hebrew 128×48, 256×96 שלום right to left, with the final mem
text devanagari 128×48, 256×96 हिंदी and हिन्दी: the i-matra before its consonant, the anusvara above, the conjunct na-da, one cluster per syllable
text mixed-direction 224×48, 448×96 an Arabic block holding "WAI 2026": an LTR run from the stroke set and an RTL run from the test repertoire, drawing one block
text split-lines 208×96, 416×192 one block over three runs and lines: a cluster of zero glyphs (the space at a break) and a cluster of no bytes (an inserted hyphen)
text ruby 80×56, 160×112 a Latin ruby block from the stroke set over a Devanagari base from the test repertoire
text vertical-rl 48×96, 96×192 mode = 1, upright Latin stacked
text emoji 80×40, 160×80 U+1F600 drawn by one monochrome glyph: one cluster, four bytes, one glyph
runs arabic-joins 96×48 an Arabic joining sequence in one run, large: the join knots of §3.13
runs arabic-joins-split 96×48 the same glyphs split over two runs, which an encoder never writes: source-over across the join
motion stroke-slice 320×180, 50 frames a stroke-set lower third (a bar and two lines that slide in on an easing table, hold and fade out as a group) and a credit roll moving up three design pixels a frame

Each payload names its sets as held, by SHA-256 and length. The text layer's projection lands with D7-7, so these vectors pin runs, clusters, blocks, text_hash, block identities, costs and frames.

Where this differs from the brief, and why

  • The sources live in vector-conformance/sources/, not sets/src/. The merged checker opens every entry of sets/ as a set, and so did tests/corpus.rs. A directory there would crash the checker.
  • author is a default feature. It gates wsf1, the two drawings, the text vectors and the generator's entry points. The library's other generator modules stay compiled, so the recomputation path is unchanged. A sink turns author off, and CI now builds it that way twice: natively, and as the Node leg's module. The CLI requires std and author.
  • The checker gains one line, "text" in RENDER_GROUPS. The corpus README, which the checker's corpus driver follows, now names text among the groups that render. No computation changes. The change was made after the unmodified checker had been run (below), and is a separate commit.
  • The existing runs and motion vectors are kept, and the stroke-glyph vectors are added beside them. The set-R vectors pin exact geometry (the seam pixel at 255, the 256 phases) that a stroke glyph would blur.
  • The full 1280×720 slice document is S-1's. motion/stroke-slice is the same shape at 320×180, so the Python checker renders it in seconds.

Tests, and the falsification of each

Each test was committed first. Then the code it guards was broken, the named test was run and shown red, the code was restored through git, and the test was shown green. All runs are debug, on the dev Mac. The script that did this is not committed.

test the break red after restore
stroke_sets::the_stroke_sets_digest_equals_annex_b: the bundled bytes, STROKE_ASCII_SHA256, Annex B, the committed source's digest, and the expansion of that source all agree; the set's glyph IDs are U+0020–U+007E one Q15 entry, U[1].sin 3212 → 3700 the expansion's digest is dc928373…, Annex B states 05476c46… green
stroke_sets::annex_a_states_the_table: Annex A's hex block is unit_table_bytes(), and its stated SHA-256 matches U[1].sin 3212 → 3213 Annex A's bytes are the table the expansion reads green
stroke_sets::the_expansion_is_deterministic: offsets worked by hand (n((100, 200), 90) = (−81, 40), since isqrt(50000) = 223; n((−7, 3), 80) = (−34, −80)); both sets re-derive from their committed sources twice, byte for byte; the generator writes the committed sources; the notes begin parent_sha256=…, converter=wsf1-expansion/1 isqrt rounded up left: (−80, 40) right: (−81, 40) green
stroke_sets::the_joining_table_picks_the_forms_of_the_test_words (and its unit twin): five words against Unicode's code points for each form, written out; every form the table names is drawn yeh's initial and medial forms swapped in the joining table بَيْت … left: […, 65267, …] right: […, 65268, …] green
stroke_sets::the_expansion_covers_the_pens_path: on a 20-unit grid over every glyph of both sets, a point within ρ − 4 of a centre line is filled, and one farther than ρ + 8 from every centre line is not the left side's outer join arc dropped … is within the pen's path but not filled green
verify.mjs's Annex B check (the Node leg) one hex digit of Annex B's digest changed ANNEX B: sets/stroke-ascii.wos1 hashes to 05476c46…, Annex B states 05476c47…, exit 1 green

What falsification found:

  • A one-unit change to a table entry does not reach the set. At ρ = 80, R_half_up(80·u, 15) moves only when 80·Δu crosses a rounding boundary, about 410 units of Q15. So the digest test stayed green under 3212 → 3213, while the table test went red. The table is pinned byte for byte by annex_a_states_the_table, and an entry change that moves a vertex turns the digest test red. Both are in the table above.
  • The joining test was tautological in its first form: its expectations named the table's own constants, so the swap changed both, and the test stayed green. It now writes Unicode's code points out (commit 14d08568), and the swap turns it red.
  • isqrt(2) returned 2 in the first version: its Newton start, x/2 + 1, does not fall below x at n = 2. The unit test isqrt_is_the_floor caught it. No segment of either set has dx² + dy² = 2, so fixing it changed no generated byte.

Also:

  • stroke_sets::each_set_carries_its_licence: licence=Apache-2.0, rfn=none, licence_sha256 is the SHA-256 of the text after the blank line (the verifier's licence_mismatch check), and source says that no outline is traced from any font.
  • wsf1::tests (unit): isqrt against the definition over 0..20,000 and at the widths dx² + dy² reaches; the table is symmetric under a quarter turn and a reflection, with every |u|² within 2^17 of 2^30; a horizontal stroke is a stadium whose caps hold the 31 directions inside their half-planes; a dot is the 64-gon; the format round-trips.

Cross-checks

  • The independent checker, from main (wai/tools/wai_vector_check.py, merged in #219), over the whole corpus:
    • unmodified: 322 of 331 expected files byte for byte, 88 of 88 payloads and sets rewritten to their own bytes (the two new sets among them), 196 of 196 pinned refusal steps, and 69 of 87 sha256.json entries. The only disagreements were the nine text vectors (.frames present in one only) and their 18 entries, because its list of rendering groups predates text;
    • with "text" added to that list: 331 of 331, 88 of 88, 196 of 196, and 87 of 87, every new frame rendered by the closed-form crossings in big integers. Wall time on the loaded dev Mac was 12 minutes; CPU time 152 s.
  • Looked at. Every new vector was rendered to an image and read: the ASCII set; Arabic, Hebrew and Devanagari in visual order with their forms, marks and conjunct; the mixed line; ruby; the vertical block; the face; the slice's frames.

Legs and gate

  • Every new vector is identical on three engines, locally on the dev Mac:

    • native macOS arm64, debug and release: every test, and vectors verify (331 vectors, 975 generated files);
    • wasm32-wasip2 under wasmtime 49.0.2: every test, and vectors verify;
    • wasm32-unknown-unknown under Node 26.11.1, in the sink's build: verify.mjs (331 vectors and 290 authoring forms), with the stroke set's digest equal to Annex B's.

    CI pins wasmtime 38.0.3 and Node 22.

  • A second instruction-set architecture: a static x86_64-unknown-linux-musl build of the tool ran vectors verify on an x86_64 Linux host. All 331 vectors and 975 generated files are identical, the two sets and their sources among them.

  • The sink's build (--no-default-features --lib) compiles warning-free, with no wsf1.

  • Clippy: clean with --all-targets --features std, except the lint in wai-ops's sha256.rs, which this crate includes by path.

  • The wai-rs gate at 14d08568 (gate.sh d7-5-r2): GREEN. Build, lib tests, carriage, the corpus loop (21 of 21), fast kernels, byte-exact, wai-web wasm32 and the neural gates all pass. The first run (d7-5) was red at one step only: fast-kernels failed before compiling, because cargo's index lookup saw der only up to 0.7.4 while Cargo.lock pins 0.7.10, a transient registry fetch. That step rerun alone is green, and so is the full rerun.

  • The tree-wide tests corpus_provenance, every_decoded_output_states_a_sha256 (which reads the grown sha256.json) and signer_ids_are_labels: green.

  • Public text:

    • check-public-names.sh with the private list: clean on each of the 44 changed files, one per invocation, and at CI scope;
    • check-comparative-prose.sh: clean on each file;
    • check-no-vanity-metrics.sh, check-wai-section-refs.py and check-workflow-concurrency.sh: clean.

Merge notes

  • It touches no file that an open PR edits (#222, #221 D2b, #210, #209, #142, #54), checked against each PR's file list. Main moved to 1946c750 (#220, #218) while this was built, and none of that delta overlaps this branch either. Merge as is; there is no need to merge main in first.
  • Legs for the merge:
    • wai-vector.yml: native ×3, now with the sink's build; wasip2; node; fuzz; checker;
    • wai-conformance.yml, because sha256.json changes;
    • no-vanity-metrics.yml;
    • wai-section-refs.yml, for the extension's new text.
  • For D7-7: the text vectors are where the projection goes. text::test_words() holds the shaper's expected forms. Each block's lang, dir and mode is already in its identity.

🤖 Generated with Claude Code

Adds the in-house outline sets of `wai.graphics.vector`: the default stroke set and the test repertoire, each drawn here as integer centre lines in the stroke source format `WSF1` and expanded by an integer stroker into an ordinary `WOS1`. The corpus gains a `text` group, and stroke-glyph members of `runs` and `motion`. The extension gains §4 and Annexes A and B. This is phase D7-5 of D7. It starts from `main` at `4ab66d24` (with #219 merged), and touches no file that #218 or #220 edits. Nothing is registered. ## What lands - **`src/wsf1.rs`** (feature `author`): - the `WSF1` reader, its ten checks in order, and its writer; - the pinned table of 64 unit vectors in Q15; - the expansion of Annex A. Segment offsets are `(R_rat(−dy·ρ, len), R_rat(dx·ρ, len))` with `len = isqrt(dx² + dy²)`. Caps and the outer side of joins take table directions chosen by exact cross-product signs, placed at `R_half_up(ρ·u, 15)`. The inner side of a join takes its two offset points. One closed contour per stroke path, every point on-curve. - **The default stroke set** (`src/vectors/stroke.rs`): printable ASCII, U+0020–U+007E, each glyph ID its code point. It is drawn from straight segments and ellipse arcs whose points come from the same Q15 table, so the drawing is integers throughout and no outline is traced from any font. Apache-2.0. Its notes carry `parent_sha256` (the source's digest), `converter=wsf1-expansion/1`, `licence`, `licence_sha256`, `copyright`, `rfn=none`, `source`, `glyphs` and `metrics`, then a blank line, then the Apache License 2.0 text. - **`src/builtin/`**: the stroke set's bytes (`include_bytes!` of the corpus file), its SHA-256 and its length, and `hold()`, which puts them in a sink's set store. - **The test repertoire** (`src/vectors/repertoire.rs`), Apache-2.0 and corpus-only: - **Arabic:** the isolated, initial, medial and final forms of beh, teh, yeh, seen, lam and meem; alef's two forms; lam-alef, isolated and final; fatha and sukun; - **Hebrew:** shin, lamed, vav and final mem; - **Devanagari:** ha and da with their headlines, the conjunct na-da as one glyph, the i-matra, the ii-matra and the anusvara; - **a monochrome face** for U+1F600; - **the small shaper the generator uses:** the joining table, the lam-alef rule, the reordered i-matra, the conjunct, and authored mark anchors. Each joining form's connector, and each headline, runs to the edge of its advance, so the pen's round cap overlaps the neighbour by 2ρ, as joins in production fonts overlap. - **`graphics-vector.md`:** - §4: the sink reads `WOS1` only; pinning and carriage; the notes' keys and the licence they carry; the default stroke set; the test repertoire; - Annex A: the `WSF1` layout and checks, why its caps fit `WOS1`'s, the Q15 table as 512 bytes with their SHA-256, and the expansion; - Annex B: the stroke set's and its source's digests and lengths; - the status note: §5 and §6 remain headings. - **`vector-conformance/`:** `sources/` (the two `WSF1` sources); `sets/stroke-ascii.wos1` and `sets/test-repertoire.wos1`, both held; the new vectors below; and `sha256.json`'s new frame entries. The README's provenance line names the two in-house sets, their drawing sources and the generator. The README now lists the groups that render. - **`wasm/`:** the Node leg's module is built with `default-features = false`, a sink's build with no stroker. `verify.mjs` checks the stroke set's SHA-256 against Annex B. - **`wai-vector.yml`:** - `native` gains the step `cargo build --no-default-features --lib`, the sink's build; - the filters gain the root `LICENSE`, whose text the in-house sets carry. | set | SHA-256 | bytes | glyphs | points | contours | |---|---|---|---|---|---| | `sets/stroke-ascii.wos1` (Annex B) | `05476c4658ef7366c7700736487eac4c3c1b7d21264978c7d05bd4c7427736c9` | 105,523 | 95 | 18,368 | 146 | | `sources/stroke-ascii.wsf1` | `4eec759d394d021aec8ce99b41e4b25fecb36372c77459ef62df182b9eb8c783` | 19,181 | 95 | 1,591 centre points | 146 strokes | | `sets/test-repertoire.wos1` | `2310ab64ddcf4d851d4d7c08bbce141fba178f03fdcbb07b05db3863e179846f` | 59,879 | 42 | 9,389 | 92 | | `sources/test-repertoire.wsf1` | `f20c628442ecc778e6d1f1ef3994d767730e5dd827f85cf9542a1b7f18661136` | 15,168 | 42 | 669 centre points | 92 strokes | The Q15 table's 512 bytes have SHA-256 `11c05a1323c8b6dde89a5627d29acecb9163b87dfd169054236fb1d1def66437`. Pen radius 80 outline units; `upem2` 2000. Most of the stroke set's points are round caps, at up to 32 table directions each. **The vectors: 12 new, 331 in all, and 70 new frame digests** (`sha256.json` grows from 66 to 87 entries). | group | vector | sizes | what it pins | |---|---|---|---| | `text` | `latin-ascii` | 320×96, 640×192 | printable ASCII from the held stroke set: one block over three runs, one cluster per glyph | | `text` | `arabic` | 160×64, 320×128 | بَيْت سلام right to left: initial, medial, final and isolated forms by joining type, lam-alef, fatha and sukun on their bases, glyphs in visual order, `byte_first` decreasing | | `text` | `hebrew` | 128×48, 256×96 | שלום right to left, with the final mem | | `text` | `devanagari` | 128×48, 256×96 | हिंदी and हिन्दी: the i-matra before its consonant, the anusvara above, the conjunct na-da, one cluster per syllable | | `text` | `mixed-direction` | 224×48, 448×96 | an Arabic block holding "WAI 2026": an LTR run from the stroke set and an RTL run from the test repertoire, drawing one block | | `text` | `split-lines` | 208×96, 416×192 | one block over three runs and lines: a cluster of zero glyphs (the space at a break) and a cluster of no bytes (an inserted hyphen) | | `text` | `ruby` | 80×56, 160×112 | a Latin ruby block from the stroke set over a Devanagari base from the test repertoire | | `text` | `vertical-rl` | 48×96, 96×192 | `mode = 1`, upright Latin stacked | | `text` | `emoji` | 80×40, 160×80 | U+1F600 drawn by one monochrome glyph: one cluster, four bytes, one glyph | | `runs` | `arabic-joins` | 96×48 | an Arabic joining sequence in one run, large: the join knots of §3.13 | | `runs` | `arabic-joins-split` | 96×48 | the same glyphs split over two runs, which an encoder never writes: source-over across the join | | `motion` | `stroke-slice` | 320×180, 50 frames | a stroke-set lower third (a bar and two lines that slide in on an easing table, hold and fade out as a group) and a credit roll moving up three design pixels a frame | Each payload names its sets as held, by SHA-256 and length. The text layer's projection lands with D7-7, so these vectors pin runs, clusters, blocks, `text_hash`, block identities, costs and frames. ## Where this differs from the brief, and why - **The sources live in `vector-conformance/sources/`, not `sets/src/`.** The merged checker opens every entry of `sets/` as a set, and so did `tests/corpus.rs`. A directory there would crash the checker. - **`author` is a default feature.** It gates `wsf1`, the two drawings, the `text` vectors and the generator's entry points. The library's other generator modules stay compiled, so the recomputation path is unchanged. A sink turns `author` off, and CI now builds it that way twice: natively, and as the Node leg's module. The CLI requires `std` and `author`. - **The checker gains one line,** `"text"` in `RENDER_GROUPS`. The corpus README, which the checker's corpus driver follows, now names `text` among the groups that render. No computation changes. The change was made after the unmodified checker had been run (below), and is a separate commit. - **The existing `runs` and `motion` vectors are kept,** and the stroke-glyph vectors are added beside them. The set-R vectors pin exact geometry (the seam pixel at 255, the 256 phases) that a stroke glyph would blur. - **The full 1280×720 slice document is S-1's.** `motion/stroke-slice` is the same shape at 320×180, so the Python checker renders it in seconds. ## Tests, and the falsification of each Each test was committed first. Then the code it guards was broken, the named test was run and shown red, the code was restored through git, and the test was shown green. All runs are debug, on the dev Mac. The script that did this is not committed. | test | the break | red | after restore | |---|---|---|---| | `stroke_sets::the_stroke_sets_digest_equals_annex_b`: the bundled bytes, `STROKE_ASCII_SHA256`, Annex B, the committed source's digest, and the expansion of that source all agree; the set's glyph IDs are U+0020–U+007E | one Q15 entry, `U[1].sin` 3212 → 3700 | the expansion's digest is `dc928373…`, Annex B states `05476c46…` | green | | `stroke_sets::annex_a_states_the_table`: Annex A's hex block is `unit_table_bytes()`, and its stated SHA-256 matches | `U[1].sin` 3212 → 3213 | `Annex A's bytes are the table the expansion reads` | green | | `stroke_sets::the_expansion_is_deterministic`: offsets worked by hand (`n((100, 200), 90) = (−81, 40)`, since `isqrt(50000) = 223`; `n((−7, 3), 80) = (−34, −80)`); both sets re-derive from their committed sources twice, byte for byte; the generator writes the committed sources; the notes begin `parent_sha256=…`, `converter=wsf1-expansion/1` | `isqrt` rounded up | `left: (−80, 40) right: (−81, 40)` | green | | `stroke_sets::the_joining_table_picks_the_forms_of_the_test_words` (and its unit twin): five words against Unicode's code points for each form, written out; every form the table names is drawn | yeh's initial and medial forms swapped in the joining table | `بَيْت … left: […, 65267, …] right: […, 65268, …]` | green | | `stroke_sets::the_expansion_covers_the_pens_path`: on a 20-unit grid over every glyph of both sets, a point within ρ − 4 of a centre line is filled, and one farther than ρ + 8 from every centre line is not | the left side's outer join arc dropped | `… is within the pen's path but not filled` | green | | `verify.mjs`'s Annex B check (the Node leg) | one hex digit of Annex B's digest changed | `ANNEX B: sets/stroke-ascii.wos1 hashes to 05476c46…, Annex B states 05476c47…`, exit 1 | green | **What falsification found:** - **A one-unit change to a table entry does not reach the set.** At ρ = 80, `R_half_up(80·u, 15)` moves only when `80·Δu` crosses a rounding boundary, about 410 units of Q15. So the digest test stayed green under `3212 → 3213`, while the table test went red. The table is pinned byte for byte by `annex_a_states_the_table`, and an entry change that moves a vertex turns the digest test red. Both are in the table above. - **The joining test was tautological** in its first form: its expectations named the table's own constants, so the swap changed both, and the test stayed green. It now writes Unicode's code points out (commit `14d08568`), and the swap turns it red. - **`isqrt(2)` returned 2** in the first version: its Newton start, `x/2 + 1`, does not fall below `x` at `n = 2`. The unit test `isqrt_is_the_floor` caught it. No segment of either set has `dx² + dy² = 2`, so fixing it changed no generated byte. Also: - `stroke_sets::each_set_carries_its_licence`: `licence=Apache-2.0`, `rfn=none`, `licence_sha256` is the SHA-256 of the text after the blank line (the verifier's `licence_mismatch` check), and `source` says that no outline is traced from any font. - `wsf1::tests` (unit): `isqrt` against the definition over `0..20,000` and at the widths `dx² + dy²` reaches; the table is symmetric under a quarter turn and a reflection, with every `|u|²` within `2^17` of `2^30`; a horizontal stroke is a stadium whose caps hold the 31 directions inside their half-planes; a dot is the 64-gon; the format round-trips. ## Cross-checks - **The independent checker, from `main`** (`wai/tools/wai_vector_check.py`, merged in #219), over the whole corpus: - **unmodified:** 322 of 331 expected files byte for byte, 88 of 88 payloads and sets rewritten to their own bytes (the two new sets among them), 196 of 196 pinned refusal steps, and 69 of 87 `sha256.json` entries. The only disagreements were the nine `text` vectors (`.frames present in one only`) and their 18 entries, because its list of rendering groups predates `text`; - **with `"text"` added to that list:** **331 of 331, 88 of 88, 196 of 196, and 87 of 87**, every new frame rendered by the closed-form crossings in big integers. Wall time on the loaded dev Mac was 12 minutes; CPU time 152 s. - **Looked at.** Every new vector was rendered to an image and read: the ASCII set; Arabic, Hebrew and Devanagari in visual order with their forms, marks and conjunct; the mixed line; ruby; the vertical block; the face; the slice's frames. ## Legs and gate - **Every new vector is identical on three engines, locally on the dev Mac:** - native macOS arm64, debug and release: every test, and `vectors verify` (331 vectors, 975 generated files); - `wasm32-wasip2` under wasmtime 49.0.2: every test, and `vectors verify`; - `wasm32-unknown-unknown` under Node 26.11.1, in the sink's build: `verify.mjs` (331 vectors and 290 authoring forms), with the stroke set's digest equal to Annex B's. CI pins wasmtime 38.0.3 and Node 22. - **A second instruction-set architecture:** a static `x86_64-unknown-linux-musl` build of the tool ran `vectors verify` on an x86_64 Linux host. All 331 vectors and 975 generated files are identical, the two sets and their sources among them. - **The sink's build** (`--no-default-features --lib`) compiles warning-free, with no `wsf1`. - **Clippy:** clean with `--all-targets --features std`, except the lint in `wai-ops`'s `sha256.rs`, which this crate includes by path. - **The wai-rs gate** at `14d08568` (`gate.sh d7-5-r2`): **GREEN**. Build, lib tests, carriage, the corpus loop (21 of 21), fast kernels, byte-exact, wai-web wasm32 and the neural gates all pass. The first run (`d7-5`) was red at one step only: `fast-kernels` failed before compiling, because cargo's index lookup saw `der` only up to 0.7.4 while `Cargo.lock` pins 0.7.10, a transient registry fetch. That step rerun alone is green, and so is the full rerun. - **The tree-wide tests** `corpus_provenance`, `every_decoded_output_states_a_sha256` (which reads the grown `sha256.json`) and `signer_ids_are_labels`: green. - **Public text:** - `check-public-names.sh` with the private list: clean on each of the 44 changed files, one per invocation, and at CI scope; - `check-comparative-prose.sh`: clean on each file; - `check-no-vanity-metrics.sh`, `check-wai-section-refs.py` and `check-workflow-concurrency.sh`: clean. ## Merge notes - **It touches no file that an open PR edits** (#222, #221 D2b, #210, #209, #142, #54), checked against each PR's file list. Main moved to `1946c750` (#220, #218) while this was built, and none of that delta overlaps this branch either. Merge as is; there is no need to merge main in first. - **Legs for the merge:** - `wai-vector.yml`: `native` ×3, now with the sink's build; `wasip2`; `node`; `fuzz`; `checker`; - `wai-conformance.yml`, because `sha256.json` changes; - `no-vanity-metrics.yml`; - `wai-section-refs.yml`, for the extension's new text. - **For D7-7:** the `text` vectors are where the projection goes. `text::test_words()` holds the shaper's expected forms. Each block's `lang`, `dir` and `mode` is already in its identity. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- WSF1 (feature author): the stroke source format, its checks, the pinned Q15 table of
  64 unit vectors and the integer expansion into a polygon-only WOS1.
- The ASCII stroke set, U+0020-U+007E, drawn here as integer centre lines; bundled as
  builtin::STROKE_ASCII with its SHA-256, which Annex B states.
- The test repertoire: Arabic, Hebrew and Devanagari glyphs and a U+1F600 face, drawn
  here, with the joining table and the small shaper the corpus generator uses.
- graphics-vector.md section 4, Annex A (WSF1, the table as bytes, the expansion) and
  Annex B (the digest).
- vector-conformance: sources/, the two sets, the text group, and stroke-glyph members of
  runs and motion; the README's provenance line names the in-house sets.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The checker's list of rendering groups is read from the README's layout. D7-5 adds the
text group to the corpus and to that sentence of the README, so the list gains it. No
computation changes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
wai-vector: the joining test expects Unicode's code points for each form, written out
All checks were successful
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 8m36s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 9s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 25s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 50s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 1m56s
WAI section references / cited sections exist (pull_request) Successful in 11s
wai-vector vector graphics / linux-x86_64 (pull_request) Successful in 1m11s
wai-vector vector graphics / wasm32-wasip2 (wasmtime) (pull_request) Successful in 33s
wai-vector vector graphics / wasm32 under Node (pull_request) Successful in 21s
wai-vector vector graphics / fuzz, peak allocation, comparison count (pull_request) Successful in 16s
wai-vector vector graphics / independent checker (Python) (pull_request) Successful in 2m6s
workflow hygiene / every workflow supersedes its own queue (pull_request) Successful in 5s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 16m37s
standards workspaces build + test / jouleclaw-rs (pull_request) Successful in 7m18s
standards workspaces build + test / aac-rs (pull_request) Successful in 4m4s
standards workspaces build + test / joule-code-rs (pull_request) Successful in 1m51s
standards workspaces build + test / joulecontract-rs (pull_request) Successful in 59s
standards workspaces build + test / map-rs (pull_request) Successful in 57s
standards workspaces build + test / mesh-rs (pull_request) Successful in 58s
standards workspaces build + test / openpay (pull_request) Successful in 4m50s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 16m12s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 41s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 3m51s
wai-vector vector graphics / linux-aarch64 (pull_request) Successful in 2m37s
standards workspaces build + test / jouledesk-rs (pull_request) Successful in 28m23s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 18m30s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 25m56s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 1m0s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 6m6s
wai-vector vector graphics / macos-arm64 (pull_request) Successful in 2m57s
14d0856828
Falsifying it showed the expectations named the table's own constants, so a swapped form
changed the table and its expectation together and the test stayed green.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Transaction-Science/open-standards!224
No description provided.