wai-vector: section 5 and the text layer, its checker, and the three-engine browser leg (D7-7, D7-8) #237

Merged
dcharlot merged 5 commits from wai/d7-text-and-browser into main 2026-10-13 07:57:39 -04:00
Owner

D7-7 and D7-8 of wai.graphics.vector (Draft), with the independent checker extended for the new section. Four commits on current main.

1. Section 5, text and accessibility, and the text layer (D7-7)

  • graphics-vector.md §5.1–§5.8. What a sink exposes to assistive technology and to a text consumer: purposes and their roles, languages, directions and writing modes, groups, ruby, and the cluster rule. §5.4 defines each block's active set (the effective windows of the items that draw it, cut by enclosing groups and the document, sorted, with touching intervals merged), the projection at a frame and the transcript as exact JSON, and the text record: the transcript, then the projection at every frame of every listed rate, one value a line. Text-equivalence is byte equality of those values.
  • wai-vector/src/text.rs, no_std with no dependency, in the sink's build; the CLI's a11y and text commands; vectors verify recomputes every text record.
  • Corpus: a new a11y group, each payload with its text record (<name>.a11y.jsonl) and its digest (text_record) in the expected file; canonical and refusal vectors for TEXT. No existing expected file changes.
  • tests/text_layer.rs: one test for each rule, with the expected JSON written by hand from §5.4 (two digests from an independent SHA-256), each falsified once by the mutation it guards against.

2. The checker, from the extension text alone

tools/wai_vector_check.py learns §5. It was extended by a session that read only the extension, the corpus and the checker, never the reference crate. It computes each text record by two derivations that share neither the interval code nor the JSON writer, compares every file under expected/ (the text records among them), and requires each canonical vector that names a rule to be refused under that rule. It agrees with every expected file of the corpus. Twenty deliberate changes to it each made it disagree.

It found one gap, in an example rather than a rule: §5.4's worked example listed no block's string and no writing mode. The fourth commit states both for each block.

3. Wasm exports and the three-engine browser leg (D7-8)

  • wai-vector/wasm: the sink's exports, wv_alloc, wv_free, wv_open, wv_close, wv_render, wv_out_ptr, wv_out_len, wv_sha256, wv_cost, wv_transcript and wv_projection. No i64 at any export, so an engine without BigInt at the interface can call it.
  • WebAssembly 1.0 only. On wasm32-unknown-unknown the precompiled core and alloc bring in post-1.0 instructions even with an MVP CPU target, so the package is built for wasm32v1-none and the crate is no_std with an allocator of its own. check-mvp.mjs reads the module and refuses any post-1.0 feature; CI shows it refusing the wasm32-unknown-unknown build.
  • build-pkg.sh: the package a page serves.
  • browser/: a harness that runs every vector in Chromium, WebKit and Firefox.
    • It renders every listed frame and hashes it in the page twice (the module's SHA-256 and the platform's), then re-hashes sampled frames in Node.
    • It recomputes the arithmetic vectors and authoring forms in each engine.
    • It compares each a11y payload's transcript and projections with its text record, byte for byte.
    • A missing engine fails the run; it is never skipped.
  • wai-vector.yml gains browser: the harness in a Playwright 1.60.0 container pinned by digest, on the linux runner.

Evidence

Run on the build host (macOS, arm64) at this branch's head, with each job's own commands and paths:

check result
native: tests debug and release, vectors verify, the sink's build without author, vectors gen leaving the tree clean all pass (14 test binaries each build); 355 vectors and 14 text records recomputed; gen leaves the tree clean
wasip2: tests under wasmtime, the tool's build, vectors verify under wasmtime all pass under wasmtime 49 (CI pins 38.0.3)
node: the recomputation build, verify.mjs 355 vectors and 314 authoring forms recomputed
browser: the package (WebAssembly 1.0), the reader refusing the default target's build, the harness in all three engines the package is 1.0 only; the reader refuses the default target's build (sign-extension); Chromium 148, WebKit 26.4 and Firefox 150 each: 314 payloads, 425 frames hashed twice in the page and 247 again in Node, 14 text records (423 lines) byte for byte, 355 vectors and 314 forms recomputed, 0 failures
fuzz: corpus-seeded fuzz, peak allocation and comparison count pass
checker: every expected file 369 of 369 expected files byte for byte; 226 of 226 refusal steps; 87 of 87 sha256.json entries
the wai-rs landing gate (the corpus directory changed, so wai-conformance.yml fires) green
comparative prose, vanity metrics, public names (private list), section references, lockfiles, workflow concurrency all pass

Falsifications (each committed first, mutated, shown red, restored through git, shown green):

  • every row of the D7-7 test table;
  • the harness: div255's pre-bias at 127 (red in all three engines with identical failure lists); a page that reports the expected digest over a changed buffer (Node's re-hash catches it in every engine); a missing engine; purpose 7 written for 200 in the Wasm build only (all three engines red at the projection comparison, the native tests unaffected).

Not verified until this runs in CI

The browser job itself has not run anywhere: the pinned image on the linux runner, rustup inside it, the engines running as root, Linux WebKit's crypto.subtle on http://127.0.0.1, and frame digests from an x86_64-built module. The harness passes in all three engines on macOS arm64.

Known differences from the plan

  • The pre-bias falsification's first failure is a clock vector, and the first composite vector it fails is the fourth: only two of the seven composite vectors ever form a value ≡ 128 (mod 255). A composite vector built to form one would make that row exact.
  • The package target is wasm32v1-none (above), and a site that builds the package needs that target installed.

🤖 Generated with Claude Code

D7-7 and D7-8 of `wai.graphics.vector` (Draft), with the independent checker extended for the new section. Four commits on current main. ## 1. Section 5, text and accessibility, and the text layer (D7-7) - **`graphics-vector.md` §5.1–§5.8.** What a sink exposes to assistive technology and to a text consumer: purposes and their roles, languages, directions and writing modes, groups, ruby, and the cluster rule. §5.4 defines each block's active set (the effective windows of the items that draw it, cut by enclosing groups and the document, sorted, with touching intervals merged), the **projection** at a frame and the **transcript** as exact JSON, and the **text record**: the transcript, then the projection at every frame of every listed rate, one value a line. Text-equivalence is byte equality of those values. - **`wai-vector/src/text.rs`**, `no_std` with no dependency, in the sink's build; the CLI's `a11y` and `text` commands; `vectors verify` recomputes every text record. - **Corpus:** a new `a11y` group, each payload with its text record (`<name>.a11y.jsonl`) and its digest (`text_record`) in the expected file; canonical and refusal vectors for `TEXT`. No existing expected file changes. - **`tests/text_layer.rs`:** one test for each rule, with the expected JSON written by hand from §5.4 (two digests from an independent SHA-256), each falsified once by the mutation it guards against. ## 2. The checker, from the extension text alone `tools/wai_vector_check.py` learns §5. It was extended by a session that read only the extension, the corpus and the checker, never the reference crate. It computes each text record by two derivations that share neither the interval code nor the JSON writer, compares every file under `expected/` (the text records among them), and requires each canonical vector that names a rule to be refused under that rule. It agrees with every expected file of the corpus. Twenty deliberate changes to it each made it disagree. It found one gap, in an example rather than a rule: §5.4's worked example listed no block's string and no writing mode. The fourth commit states both for each block. ## 3. Wasm exports and the three-engine browser leg (D7-8) - **`wai-vector/wasm`:** the sink's exports, `wv_alloc`, `wv_free`, `wv_open`, `wv_close`, `wv_render`, `wv_out_ptr`, `wv_out_len`, `wv_sha256`, `wv_cost`, `wv_transcript` and `wv_projection`. No i64 at any export, so an engine without BigInt at the interface can call it. - **WebAssembly 1.0 only.** On `wasm32-unknown-unknown` the precompiled `core` and `alloc` bring in post-1.0 instructions even with an MVP CPU target, so the package is built for `wasm32v1-none` and the crate is `no_std` with an allocator of its own. `check-mvp.mjs` reads the module and refuses any post-1.0 feature; CI shows it refusing the `wasm32-unknown-unknown` build. - **`build-pkg.sh`:** the package a page serves. - **`browser/`:** a harness that runs every vector in Chromium, WebKit and Firefox. - It renders every listed frame and hashes it in the page twice (the module's SHA-256 and the platform's), then re-hashes sampled frames in Node. - It recomputes the arithmetic vectors and authoring forms in each engine. - It compares each `a11y` payload's transcript and projections with its text record, byte for byte. - A missing engine fails the run; it is never skipped. - **`wai-vector.yml` gains `browser`:** the harness in a Playwright 1.60.0 container pinned by digest, on the `linux` runner. ## Evidence Run on the build host (macOS, arm64) at this branch's head, with each job's own commands and paths: | check | result | |---|---| | `native`: tests debug and release, `vectors verify`, the sink's build without `author`, `vectors gen` leaving the tree clean | all pass (14 test binaries each build); 355 vectors and 14 text records recomputed; `gen` leaves the tree clean | | `wasip2`: tests under wasmtime, the tool's build, `vectors verify` under wasmtime | all pass under wasmtime 49 (CI pins 38.0.3) | | `node`: the recomputation build, `verify.mjs` | 355 vectors and 314 authoring forms recomputed | | `browser`: the package (WebAssembly 1.0), the reader refusing the default target's build, the harness in all three engines | the package is 1.0 only; the reader refuses the default target's build (sign-extension); Chromium 148, WebKit 26.4 and Firefox 150 each: 314 payloads, 425 frames hashed twice in the page and 247 again in Node, 14 text records (423 lines) byte for byte, 355 vectors and 314 forms recomputed, 0 failures | | `fuzz`: corpus-seeded fuzz, peak allocation and comparison count | pass | | `checker`: every expected file | 369 of 369 expected files byte for byte; 226 of 226 refusal steps; 87 of 87 `sha256.json` entries | | the wai-rs landing gate (the corpus directory changed, so `wai-conformance.yml` fires) | green | | comparative prose, vanity metrics, public names (private list), section references, lockfiles, workflow concurrency | all pass | Falsifications (each committed first, mutated, shown red, restored through git, shown green): - every row of the D7-7 test table; - the harness: `div255`'s pre-bias at 127 (red in all three engines with identical failure lists); a page that reports the expected digest over a changed buffer (Node's re-hash catches it in every engine); a missing engine; purpose 7 written for 200 in the Wasm build only (all three engines red at the projection comparison, the native tests unaffected). ## Not verified until this runs in CI The `browser` job itself has not run anywhere: the pinned image on the `linux` runner, rustup inside it, the engines running as root, Linux WebKit's `crypto.subtle` on `http://127.0.0.1`, and frame digests from an x86_64-built module. The harness passes in all three engines on macOS arm64. ## Known differences from the plan - The pre-bias falsification's first failure is a `clock` vector, and the first `composite` vector it fails is the fourth: only two of the seven `composite` vectors ever form a value ≡ 128 (mod 255). A composite vector built to form one would make that row exact. - The package target is `wasm32v1-none` (above), and a site that builds the package needs that target installed. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
graphics-vector.md section 5 (Draft): what a sink exposes to assistive
technology and to a text consumer, and how. Each text block's active set (the
effective windows of the items that draw it, merged), the projection at a
frame and the transcript as exact JSON, the text record and its digest, ruby,
purposes, writing modes and the cluster rule.

- wai-vector: src/text.rs (no_std, no dependency), the a11y and text commands,
  and vectors verify recomputing every text record.
- The corpus: a new a11y group, each payload with its text record
  (<name>.a11y.jsonl) and its digest in the expected file; canonical and
  refusal vectors for TEXT. No existing expected file changes.
- tests/text_layer.rs: each rule of section 5, with expected JSON written by
  hand from the text, each falsified once.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
The independent checker recomputes, from graphics-vector.md section 5, each
a11y payload's text record byte for byte and its digest, by two derivations
that share neither the interval code nor the JSON writer, and compares every
file under expected/, the text records among them. Canonical vectors that name
a rule must be refused under it. It agrees with every expected file of the
corpus; twenty deliberate changes to it each made it disagree.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
- wai/wai-vector/wasm: the sink's exports (wv_alloc, wv_free, wv_open,
  wv_close, wv_render, wv_out_ptr, wv_out_len, wv_sha256, wv_cost,
  wv_transcript, wv_projection), no_std with its own allocator, built for
  wasm32v1-none so the module is WebAssembly 1.0 only; check-mvp.mjs refuses
  any post-1.0 feature, and fails on the wasm32-unknown-unknown build.
- build-pkg.sh: the package a page serves.
- browser/: a harness that renders every listed frame of every vector in
  Chromium, WebKit and Firefox, hashes it in the page twice, re-hashes sampled
  frames in Node, recomputes the arithmetic vectors and authoring forms, and
  compares each a11y payload's transcript and projections with its text record.
  A missing engine fails the run.
- wai-vector.yml: the browser job, in a Playwright container pinned by digest,
  on the linux runner.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
graphics-vector 5.4: the worked example states each block's string and writing mode
All checks were successful
wai-vector vector graphics / macos-arm64 (pull_request) Successful in 1m33s
lockfiles / every Cargo.lock is current (pull_request) Successful in 37s
no-vanity-metrics / Scan committed Markdown for vanity metrics (pull_request) Successful in 3s
no-vanity-metrics / WAI public text names no comparison (pull_request) Successful in 3s
no-vanity-metrics / WAI public text names no company or product (pull_request) Successful in 3s
standards workspaces build + test / arl-rs (pull_request) Successful in 1m8s
standards workspaces build + test / eoc-rs (pull_request) Successful in 1m59s
standards workspaces build + test / jcp-rs (pull_request) Successful in 1m38s
standards workspaces build + test / joule-ui-rs (pull_request) Successful in 1m10s
standards workspaces build + test / joulehook-rs (pull_request) Successful in 1m11s
standards workspaces build + test / proof-rs (pull_request) Successful in 1m19s
standards workspaces build + test / sense-rs (pull_request) Successful in 1m10s
standards workspaces build + test / sandbox-rs (pull_request) Successful in 2m22s
standards workspaces build + test / smart-byte-rs (pull_request) Successful in 4m5s
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 2m8s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 13s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 8m43s
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 27s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 49s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 2m2s
WAI section references / cited sections exist (pull_request) Successful in 10s
wai-vector vector graphics / linux-x86_64 (pull_request) Successful in 1m32s
wai-vector vector graphics / wasm32-wasip2 (wasmtime) (pull_request) Successful in 39s
wai-vector vector graphics / wasm32 under Node (pull_request) Successful in 21s
wai-vector vector graphics / wasm32 in three browser engines (pull_request) Successful in 2m20s
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 2m11s
workflow hygiene / every workflow supersedes its own queue (pull_request) Successful in 4s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 17m4s
4084a31bc6
The independent checker, written from the text alone, could not rebuild the
example's input from its list of blocks: the strings appeared only in the
printed output, and no block stated its writing mode. Each bullet now does.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Merge main into wai/d7-text-and-browser
All checks were successful
WAI HTTP delivery / HTTP corpus · falsify patches 1/3 (pull_request) Has been skipped
WAI HTTP delivery / HTTP corpus · falsify patches 2/3 (pull_request) Has been skipped
WAI HTTP delivery / HTTP corpus · every patch and mutation in exactly one shard (pull_request) Has been skipped
WAI HTTP delivery / HTTP corpus · verified both ways, and the differential (pull_request) Has been skipped
WAI HTTP delivery / cache rules · controls and labelled caches (pull_request) Has been skipped
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (pull_request) Successful in 1m20s
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Successful in 1m17s
wai-pack / wai-pack · does this change touch it (pull_request) Successful in 4s
wai-pack / wai-pack · linux (pull_request) Has been skipped
wai-pack / wai-pack · linux-arm64 (pull_request) Has been skipped
wai-pack / wai-pack · macos (pull_request) Has been skipped
wai-pack / wai-pack · independent verifier (Python) (pull_request) Has been skipped
wai-pack / wai-pack · the floor through a media prober (pull_request) Has been skipped
wai-pack / wai-pack · dependency licences (pull_request) Has been skipped
wai-quantum WebGPU member / shader checks · browser build · wasip2 inertness · MSRV (pull_request) Successful in 2m48s
wai-quantum WebGPU member / member · software Vulkan (linux) (pull_request) Successful in 1m42s
WAI section references / cited sections exist (pull_request) Successful in 10s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 17m17s
wai-vector vector graphics / linux-x86_64 (pull_request) Successful in 1m46s
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Successful in 4m53s
wai-vector vector graphics / wasm32-wasip2 (wasmtime) (pull_request) Successful in 40s
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 / wasm32 in three browser engines (pull_request) Successful in 1m49s
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Successful in 4s
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Has been skipped
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Has been skipped
wai-vector vector graphics / independent checker (Python) (pull_request) Successful in 2m16s
workflow hygiene / every workflow supersedes its own queue (pull_request) Successful in 5s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 2m54s
473d47aaab
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!237
No description provided.