diff --git a/.cargo/mutants.toml b/.cargo/mutants.toml index 374630a6..fcf277be 100644 --- a/.cargo/mutants.toml +++ b/.cargo/mutants.toml @@ -24,6 +24,7 @@ examine_globs = [ "crates/labcolors-core/src/wcag22.rs", "crates/labcolors-core/src/wcag22_evidence.rs", "crates/labcolors-core/src/wcag22_feasibility.rs", + "crates/labcolors-core/src/wcag22_feasibility/explicit.rs", "crates/labcolors-core/src/wcag22/kernel.rs", ] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c4dcaffa..630cc80d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -223,6 +223,12 @@ jobs: - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - name: cargo test run: cargo test --workspace --locked + - name: prove base-only Core capability is independently testable + run: | + cargo check -p labcolors-core --all-targets --no-default-features --features wcag22-feasibility --locked + cargo test -p labcolors-core --lib --no-default-features --features wcag22-feasibility --locked + cargo test -p labcolors-core --test wcag22_feasibility --no-default-features --features wcag22-feasibility --locked + cargo test -p labcolors-core --doc --no-default-features --features wcag22-feasibility --locked - name: prove core capability projection boundary shell: bash run: | @@ -241,7 +247,10 @@ jobs: "labcolors-conformance", ) core = packages["labcolors-core"] - if core["features"].get("default") != ["wcag22-feasibility"]: + if core["features"].get("default") != [ + "wcag22-feasibility", + "wcag22-explicit-feasibility", + ]: raise SystemExit("labcolors-core default capability set drifted") protocol_core = [ @@ -291,6 +300,10 @@ jobs: raise SystemExit( f"{consumer} did not resolve protocol-owned wcag22-feasibility" ) + if 'labcolors-core feature "wcag22-explicit-feasibility"' in feature_tree: + raise SystemExit( + f"{consumer} resolved the Core-only explicit-domain capability" + ) print("core capability projection: PASS") PY @@ -302,10 +315,12 @@ jobs: run: python3 scripts/verify_wcag22_neutral_axis.py - name: verify WCAG22 feasibility identity byte grammar run: python3 scripts/verify_wcag22_feasibility_identity.py - - name: validate committed WCAG22 feasibility benchmark evidence + - name: verify explicit WCAG22 feasibility identity and anti-vacuum mutations + run: python3 scripts/verify_wcag22_explicit_feasibility_identity.py --self-test + - name: validate historical and current WCAG22 feasibility benchmark evidence run: | - artifact="crates/labcolors-core/contracts/wcag22-feasibility-benchmark-v1.json" - protocol=( + historical_artifact="crates/labcolors-core/contracts/wcag22-feasibility-benchmark-v1.json" + historical_protocol=( --admit-revision fea99a1ea4148a5a72423c88220655f7f84213fe --admit-rustc-release 1.96.0 --admit-target-triple aarch64-apple-darwin @@ -315,9 +330,10 @@ jobs: --admit-package-version 0.2.0 --admit-sample-count 5 ) - historical_snapshot=6001cf41e0a8364f25543e7955ceaf64d50129b4 + historical_checker_snapshot=6001cf41e0a8364f25543e7955ceaf64d50129b4 + historical_applicability_snapshot=94efeeeb1811f5515558ab2d79014a5e4c3a570a historical_root="$RUNNER_TEMP/wcag22-feasibility-slice-a-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT" - git worktree add --detach "$historical_root" "$historical_snapshot" + git worktree add --detach "$historical_root" "$historical_checker_snapshot" cleanup_history() { git worktree remove --force "$historical_root" } @@ -325,16 +341,42 @@ jobs: ( cd "$historical_root" python3 scripts/check_wcag22_feasibility_benchmark.py \ - "$artifact" "${protocol[@]}" \ + "$historical_artifact" "${historical_protocol[@]}" \ --verify-current-subjects \ --artifact-sha256 7e9ffcbdd9d5d50fe681f511c34fc5c5dd270e9c475ce23ae56e9776922a3c5e \ --self-test ) cleanup_history + git worktree add --detach "$historical_root" "$historical_applicability_snapshot" + ( + cd "$historical_root" + python3 scripts/check_wcag22_feasibility_applicability.py \ + "$historical_artifact" \ + --artifact-sha256 7e9ffcbdd9d5d50fe681f511c34fc5c5dd270e9c475ce23ae56e9776922a3c5e \ + --self-test + ) + cleanup_history trap - EXIT + + current_artifact="crates/labcolors-core/contracts/wcag22-feasibility-benchmark-v2.json" + current_protocol=( + --admit-revision 965eb42642beb1c072a74886be1d016027afeae5 + --admit-rustc-release 1.96.0 + --admit-target-triple aarch64-apple-darwin + --admit-target-arch aarch64 + --admit-target-os macos + --admit-pointer-width-bits 64 + --admit-package-version 0.2.0 + --admit-sample-count 5 + ) + python3 scripts/check_wcag22_feasibility_benchmark.py \ + "$current_artifact" "${current_protocol[@]}" \ + --verify-current-subjects \ + --artifact-sha256 d8d5c7f3eda834bca9912d835fe3ada13d9dcd5a11cb47a131736716b0b51202 \ + --self-test python3 scripts/check_wcag22_feasibility_applicability.py \ - "$artifact" \ - --artifact-sha256 7e9ffcbdd9d5d50fe681f511c34fc5c5dd270e9c475ce23ae56e9776922a3c5e \ + "$current_artifact" \ + --artifact-sha256 d8d5c7f3eda834bca9912d835fe3ada13d9dcd5a11cb47a131736716b0b51202 \ --self-test audit: @@ -441,7 +483,7 @@ jobs: node-version: ${{ env.NODE_TOOLCHAIN }} cache: npm cache-dependency-path: packages/colors/package-lock.json - - name: "verify committed #295 canonical whole-call WASM boundary evidence" + - name: "verify committed #296-A canonical whole-call WASM boundary evidence" # The admitted JSON binds exact deterministic bytes/shapes and the # reviewed Uint8Array package root to this built Linux x64 WASM. Fresh # latency/maxRSS/pages remain observations only. @@ -449,12 +491,12 @@ jobs: run: >- node bench/wcag22-feasibility-boundary.bench.mjs --verify - - name: independently fingerprint the exact #295 WASM + - name: independently fingerprint the exact #296-A WASM run: | bytes="$(wc -c < packages/colors/pkg/labcolors_bg.wasm | tr -d '[:space:]')" sha256="$(sha256sum packages/colors/pkg/labcolors_bg.wasm | cut -d ' ' -f1)" echo "canonical candidate raw=${bytes}B sha256=${sha256}" - - name: "upload exact #295 verified whole-call evidence" + - name: "upload exact #296-A verified whole-call evidence" uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: wcag22-feasibility-wasm-boundary-${{ github.sha }}-attempt-${{ github.run_attempt }} @@ -464,8 +506,8 @@ jobs: if-no-files-found: error retention-days: 30 - name: enforce measured WASM raw-byte budget - # Issue #295 owns the current exact Linux x64 measurement and zero-headroom - # ceiling. Issue #284 remains the immutable V1 build-recipe/baseline input. + # Issue #296 owns the current exact Linux x64 measurement and zero-headroom + # ceiling. Issues #284/#295 remain immutable historical inputs. # gzip is transport diagnostics only. run: node scripts/check-wasm-size-budget.mjs - name: "@labpics/colors: typecheck + runtime tests" diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fd8db9d..fdf1f9a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,10 @@ Migration-note: [exact alpha / typed Glow](docs/migrations/exact-alpha-glow.md), `[UInt8]`; обе поверхности сохраняют `Success(Feasible | Infeasible | NotEvaluated)` либо typed `Failure` и не воспроизводят математику Core. +- Rust Core принимает также непустой явный конечный набор пар «opaque ID + + финальный sRGB8», канонизирует точные UTF-8-байты ID и использует тот же + exhaustive kernel. Возможность включена в default Core, но не проецируется в + Protocol/WASM/FFI/npm/Swift. - Conformance pack 5.0.0 добавляет ровно одно семейство `wcag22-feasibility.json`: exact 7/2/0/92/59, mixed/all NotApplicable, typed conflict/resource failures и opaque-ID law. Шесть прежних family @@ -71,8 +75,9 @@ Migration-note: [exact alpha / typed Glow](docs/migrations/exact-alpha-glow.md), `evidence/wcag22-srgb8-q55-proof-v1.json` не меняются: это отдельные version domains, а доказанная математика и finite artifact прежние. - WASM size history стала append-only: immutable V1 сохраняет допуск #284 - (`454385 B`), а V2 допускает полный transport #295 как точные `521240 B` / - `d37841…9ca0` с нулевым headroom и ссылкой на неизменяемый V1 build recipe. + (`454385 B`), V2 — transport #295 (`521240 B` / `d37841…9ca0`), а V3 — + Core-срез #296-A (`521231 B` / `779379…e029`). Каждый допуск имеет нулевой + headroom и точную ссылку на неизменяемый V1 build recipe; V1/V2 не переписаны. Canonical whole-call artifact фиксирует 10 крайних форм × 5 свежих процессов; latency, process maxRSS и WASM pages остаются наблюдениями, не SLO. - Conformance pack 4.0.0 добавил `wcag22.json`; pack 5.0.0 добавляет только diff --git a/crates/labcolors-core/Cargo.toml b/crates/labcolors-core/Cargo.toml index aaea841c..95f731e5 100644 --- a/crates/labcolors-core/Cargo.toml +++ b/crates/labcolors-core/Cargo.toml @@ -14,8 +14,9 @@ rust-version.workspace = true [features] # Direct core consumers receive the stable default capability set. A transport # adapter may disable defaults and then enable only capabilities it projects. -default = ["wcag22-feasibility"] +default = ["wcag22-feasibility", "wcag22-explicit-feasibility"] wcag22-feasibility = [] +wcag22-explicit-feasibility = ["wcag22-feasibility"] [dev-dependencies] pretty_assertions = "1.4" @@ -61,3 +62,7 @@ required-features = ["wcag22-feasibility"] [[test]] name = "wcag22_feasibility" required-features = ["wcag22-feasibility"] + +[[test]] +name = "wcag22_explicit_feasibility" +required-features = ["wcag22-explicit-feasibility"] diff --git a/crates/labcolors-core/README.md b/crates/labcolors-core/README.md index 23658fe8..60c1953d 100644 --- a/crates/labcolors-core/README.md +++ b/crates/labcolors-core/README.md @@ -39,21 +39,40 @@ details. вхождения, применимый критерий WCAG 2.2 и точные соседние цвета sRGB8. Core канонизирует декларации, вычисляет каждую пару «кандидат × сосед» и возвращает один запечатанный терминал: `Feasible`, `Infeasible` либо декларационный -`NotEvaluated`. `Infeasible` означает отсутствие решения только в выбранном -зарегистрированном домене; это не утверждение об отсутствии цвета вне домена. - -Возможность Cargo `wcag22-feasibility` включена по умолчанию для прямых -потребителей Core. Транспортные адаптеры отключают неэкспортируемые возможности -и включают каждую возможность вместе с её публичным транспортом и контрактом -соответствия. - -В версии 1 зарегистрирована нейтральная ось кодированной sRGB8: ровно 256 кодов -`[v, v, v]`, где `v` принимает каждое целое значение от 0 до 255. Число 256 — -мощность исчерпывающе перечисляемого 8-битного домена, а не эмпирический порог. -Core не выводит размер текста, компонентную семантику или применимость из ID. -Превышение ресурсов, противоречивая декларация, ошибка выделения памяти и -нарушение инварианта вычислителя или компилятора возвращаются типизированными -ошибками; частичного или запасного результата нет. +`NotEvaluated`. `Infeasible` означает отсутствие решения только в проверенном +конечном домене; это не утверждение об отсутствии цвета вне него. + +Прямой Core по умолчанию включает `wcag22-feasibility` и зависящую от неё +возможность `wcag22-explicit-feasibility`. Транспорты отключают default-features, +а Protocol/WASM/FFI в V1 проецируют только первую возможность с +зарегистрированной нейтральной осью. Поэтому явный клиентский набор остаётся +Core-only API и не попадает в транспортный артефакт; обе формы используют один +математический компилятор. + +В V1 доступны две формы одного компилятора. Совместимый вход `evaluate` +перечисляет зарегистрированную нейтральную ось: ровно 256 кодов `[v, v, v]`, +где `v` принимает каждое целое значение от 0 до 255. Вход +`explicit::evaluate` принимает непустой клиентский набор пар «непрозрачный ID + +неизменяемый финальный `Srgb8`». Core сортирует точные UTF-8-байты ID, отклоняет +их повторы и сам выводит мощность, digest, матрицу и partition. Разные ID с +одинаковыми физическими байтами остаются разными кандидатами. Ни один из входов +не выводит размер текста, компонентную семантику, применимость или предпочтение +из ID; выбор среди допустимых кандидатов в этот контракт не входит. + +Для `C` кандидатов и `E` канонических применимых рёбер выполняется ровно +`W=C×E` атомарных проверок. При `E>0` единственный упакованный буфер имеет +`B=ceil(C×E/8)+ceil(C/8)` байт; при `E=0` он пуст. Эти величины выводятся и +проверяются до выделения памяти. Остальная стоимость не маскируется формулой +`C×E`: отдельно выполняются линейный просмотр деклараций, сортировка кандидатов +и связей сравнением точных байтов ID, сортировка соседей внутри каждой связи и +линейное хеширование канонического результата. Превышение ресурсов, +противоречивая декларация, ошибка выделения памяти и нарушение инварианта +вычислителя или компилятора возвращаются типизированными ошибками; частичного +или запасного результата нет. + +Полные проверки формул `C×E`, случая `E=0`, повторов ID, ресурсных отказов и +запрета частичного результата перечислены в разделе «Конечная компиляция +выполнимости WCAG 2.2» [карты верификации](../../docs/verification-map.md). ```rust # #[cfg(feature = "wcag22-feasibility")] @@ -93,6 +112,44 @@ if let Some(evaluated) = result.evaluated() { # fn main() {} ``` +Клиентский конечный набор использует те же `RelationV1` и атомарный WCAG-путь: + +```rust +# #[cfg(feature = "wcag22-explicit-feasibility")] +# fn explicit_feasibility_example() -> Result<(), Box> { +use labcolors_core::{ + Srgb8, + wcag22::Wcag22CriterionV1, + wcag22_feasibility::{ + OccurrenceId, RelationId, RelationV1, ResourceProfileIdV1, + explicit::{ + CandidateId, CandidateV1, DomainRequestV1, RequestV1, evaluate, + }, + }, +}; + +let domain = DomainRequestV1::try_new(vec![ + CandidateV1::new(CandidateId::try_new("brand/ink")?, Srgb8::new([18, 52, 86])), + CandidateV1::new(CandidateId::try_new("brand/paper")?, Srgb8::new([245, 247, 250])), +])?; +let relation = RelationV1::applicable( + RelationId::try_new("content-on-canvas")?, + OccurrenceId::try_new("article/body")?, + Wcag22CriterionV1::Sc143TextDefault, + vec![Srgb8::new([255; 3])], +)?; +let result = evaluate(RequestV1::try_new( + domain, + vec![relation], + ResourceProfileIdV1::Compile, +)?)?; + +assert_eq!(result.evaluated().map(|value| value.candidates().len()), Some(2)); +# Ok(()) +# } +# fn main() {} +``` + В примере `Sc143TextDefault` означает явно объявленный клиентом критерий SC 1.4.3 для обычного текста с отношением 4.5:1; Core не угадывает его по ID или типографике. `0x75` и `0x76` — вычисленные граничные результаты именно для двух diff --git a/crates/labcolors-core/benches/wcag22_feasibility_admission.rs b/crates/labcolors-core/benches/wcag22_feasibility_admission.rs index cab0b519..b195e9fb 100644 --- a/crates/labcolors-core/benches/wcag22_feasibility_admission.rs +++ b/crates/labcolors-core/benches/wcag22_feasibility_admission.rs @@ -13,7 +13,7 @@ //! //! ```text //! LABCOLORS_WCAG22_BENCH_SAMPLES=5 \ -//! LABCOLORS_WCAG22_BENCH_OUTPUT=/private/tmp/labcolors-wcag22-feasibility-admission-raw-v1.json \ +//! LABCOLORS_WCAG22_BENCH_OUTPUT=/private/tmp/labcolors-wcag22-feasibility-admission-raw-v2.json \ //! cargo bench -p labcolors-core --bench wcag22_feasibility_admission //! ``` @@ -39,8 +39,8 @@ use labcolors_core::wcag22_feasibility::{ #[path = "../src/sha256.rs"] mod subject_sha256; -const ARTIFACT_ID: &str = "wcag22-feasibility-admission-raw-v1"; -const DEFAULT_OUTPUT_FILENAME: &str = "labcolors-wcag22-feasibility-admission-raw-v1.json"; +const ARTIFACT_ID: &str = "wcag22-feasibility-admission-raw-v2"; +const DEFAULT_OUTPUT_FILENAME: &str = "labcolors-wcag22-feasibility-admission-raw-v2.json"; const CANDIDATE_COUNT: u64 = 256; const PAGE_BYTES: u64 = 65_536; const DECISION_SLOT_BYTES: u64 = 32; @@ -71,10 +71,11 @@ const SOURCE_OBJECTS: [(&str, &str); 8] = [ "scripts/check_wcag22_feasibility_benchmark.py", ), ]; -const SUBJECT_PATHS: [&str; 16] = [ +const SUBJECT_PATHS: [&str; 17] = [ "Cargo.toml", "crates/labcolors-core/src/lib.rs", "crates/labcolors-core/src/wcag22_feasibility.rs", + "crates/labcolors-core/src/wcag22_feasibility/explicit.rs", "crates/labcolors-core/src/srgb8.rs", "crates/labcolors-core/src/sha256.rs", "crates/labcolors-core/src/wcag22.rs", diff --git a/crates/labcolors-core/contracts/wcag22-explicit-feasibility-identity-v1.json b/crates/labcolors-core/contracts/wcag22-explicit-feasibility-identity-v1.json new file mode 100644 index 00000000..cc61fcac --- /dev/null +++ b/crates/labcolors-core/contracts/wcag22-explicit-feasibility-identity-v1.json @@ -0,0 +1 @@ +{"artifactId":"wcag22-explicit-feasibility-identity-v1","encoding":{"byteString":"u64-length-then-exact-bytes","candidateOrder":"lexicographic-exact-utf8-bytes-no-normalization","candidateRecord":"length-prefixed-id-then-three-srgb8-octets","evaluationLayoutOrder":["canonicalRelations","applicableRelations","notApplicableRelations","applicableEdges","candidateCount","logicalAssessments","failureMatrixBytes","partitionBytes","packedResultBytes"],"integer":"u64-big-endian","matrixBitOrder":"candidate-major-contiguous-lsb0","partitionBitOrder":"canonical-candidate-index-lsb0","partitionInEvaluation":"u64-length-then-exact-bytes","relationGrammar":"wcag22-feasibility-relations-v1"},"expected":{"domainDigestSha256":"71960b339a5af0421a5562e02aea28217b3f985c88a53f3244ea73f6c19258f4","evaluationIdSha256":"59e69b867d8feb8afae4d28708bd353d0f3a0e89c12b0f34952f2e9a5e8be700","relationSetDigestSha256":"990dbc58252dc518ccf63b2f4b63ef5ae227a2bed48dda9e5e5959f3e2477132"},"fixture":{"canonicalCandidates":[{"candidateId":"é","candidateIdUtf8Hex":"65cc81","emitted":[18,52,86]},{"candidateId":"é","candidateIdUtf8Hex":"c3a9","emitted":[18,52,86]},{"candidateId":"海","candidateIdUtf8Hex":"e6b5b7","emitted":[0,0,0]},{"candidateId":"🎨","candidateIdUtf8Hex":"f09f8ea8","emitted":[255,128,1]}],"canonicalRelations":[{"adjacent":[[0,0,0],[118,118,118],[255,255,255]],"criterion":"sc-1.4.3-text-default","kind":"applicable","occurrenceId":"hover/🎨","relationId":"alpha"},{"kind":"notApplicable","occurrenceId":"ornament","reasonId":"client/не-применимо","relationId":"zeta"}],"declaredCandidates":[{"candidateId":"🎨","emitted":[255,128,1]},{"candidateId":"é","emitted":[18,52,86]},{"candidateId":"海","emitted":[0,0,0]},{"candidateId":"é","emitted":[18,52,86]}],"domainKind":"explicit-srgb8-set-v1","failureMatrixHex":"5b0c","layout":{"applicableEdges":3,"applicableRelations":1,"candidateCount":4,"canonicalRelations":2,"failureMatrixBytes":2,"logicalAssessments":12,"notApplicableRelations":1,"packedResultBytes":3,"partitionBytes":1},"matrixSha256":"f414937d1b17276054be72790c34aef5a4eb5b6dc2132122599d47297bba5507","partitionHex":"00"},"schemaVersion":1} diff --git a/crates/labcolors-core/contracts/wcag22-feasibility-benchmark-v2.json b/crates/labcolors-core/contracts/wcag22-feasibility-benchmark-v2.json new file mode 100644 index 00000000..b9c9c913 --- /dev/null +++ b/crates/labcolors-core/contracts/wcag22-feasibility-benchmark-v2.json @@ -0,0 +1,428 @@ +{ + "schemaVersion": 1, + "artifactId": "wcag22-feasibility-admission-raw-v2", + "claimBoundary": "native-process-observations-and-page-slot-arithmetic-only", + "notMeasured": ["webassembly-runtime-memory", "serialized-output-size", "client-latency"], + "environment": { + "execution": "native-process", + "targetArch": "aarch64", + "targetOs": "macos", + "pointerWidthBits": 64, + "debugAssertions": false, + "packageVersion": "0.2.0", + "allocator": "std::alloc::System", + "allocatorInstrumentationIncludedInElapsedTime": true, + "timer": "std::time::Instant", + "measurementThreads": 1, + "requestConstructionMeasured": false, + "rustcVerbose": "rustc 1.96.0 (ac68faa20 2026-05-25)\nbinary: rustc\ncommit-hash: ac68faa20c58cbccd01ee7208bf3b6e93a7d7f96\ncommit-date: 2026-05-25\nhost: aarch64-apple-darwin\nrelease: 1.96.0\nLLVM version: 22.1.2", + "gitRevision": "965eb42642beb1c072a74886be1d016027afeae5", + "gitTree": "33b9f68e1b376307c51a3ee42bf6adf362168646", + "sourceTreeClean": true, + "sampleCountExplicit": true, + "sourceObjects": { + "workspaceCargo": {"path": "Cargo.toml", "gitObject": "e64e7a0e3875e37dbd8be06c013868da3248b4b3"}, + "workspaceLock": {"path": "Cargo.lock", "gitObject": "bd2faf4108ed85d275ee595ce3824ed199b7c722"}, + "coreCargo": {"path": "crates/labcolors-core/Cargo.toml", "gitObject": "95f731e5b11bc0468f068d6bfab73a1cae5ed7b9"}, + "coreSourceTree": {"path": "crates/labcolors-core/src", "gitObject": "ce447f3557cfb40e64805bdc7ea5b13255552424"}, + "wcag22Srgb8Contract": {"path": "crates/labcolors-core/contracts/wcag22-srgb8-v1.json", "gitObject": "7b3932335398bdbc0760136904cb245305749b09"}, + "wcag22Q55ProofContract": {"path": "crates/labcolors-core/contracts/wcag22-srgb8-q55-proof-v1.json", "gitObject": "ddce1912e8e06ce6bd55c9854acf27a28a31b134"}, + "benchmarkHarness": {"path": "crates/labcolors-core/benches/wcag22_feasibility_admission.rs", "gitObject": "b195e9fb374c4ffe2464ccb2eb128713d4adf55c"}, + "benchmarkChecker": {"path": "scripts/check_wcag22_feasibility_benchmark.py", "gitObject": "d1b9eaa232a635a7abc13485ecb9212187b5bfdb"} + } + }, + "warmupSamples": 0, + "scenarioOrder": "as-emitted", + "sampleCount": 5, + "admissionStatus": "measurement-only-unless-admission-check-passes", + "hardSlo": { + "class": "deterministic-work-and-storage", + "logicalAssessmentLaw": "W=256E", + "packedStorageLaw": "B=0 if A=0, otherwise B=32(E+1)", + "partialTerminalAllowed": false, + "timingThresholdNs": null, + "allRequiredShapesMustComplete": true + }, + "boundedEnvelopeModel": { + "scope": "product-policy-capacity-arithmetic-not-total-memory", + "referenceBoundedBytes": 65536, + "candidateCount": 256, + "decisionSlotBytes": 32, + "partitionBytes": 32, + "reservedPartitionSlots": 1, + "maximumCardinality": 2047, + "maximumLogicalAssessments": 524032, + "maximumPackedResultBytes": 65536 + }, + "subjectManifest": [ + {"path": "Cargo.toml", "sha256": "fb821f899e1d32aeb86cbfd2f9896797569e0bef908356dd6ba7c27c674a6c04"}, + {"path": "crates/labcolors-core/src/lib.rs", "sha256": "a98651f9b4ab45237fbc04bda90cc260b98e6caf6b99fada63407745cdbd3d64"}, + {"path": "crates/labcolors-core/src/wcag22_feasibility.rs", "sha256": "af975d66069196a46d6096855a1c7c039fac8d5f95f17bebc4e735e4182ef4cb"}, + {"path": "crates/labcolors-core/src/wcag22_feasibility/explicit.rs", "sha256": "94fa91bc92e2189dbbb95790b07a03ad8340cf6d52fb1b24d030ee0b57a50fe8"}, + {"path": "crates/labcolors-core/src/srgb8.rs", "sha256": "9a37ea3f25f7ab3e2ae064e5a5b5c7d51a880a537c297169d6014fa32e593e17"}, + {"path": "crates/labcolors-core/src/sha256.rs", "sha256": "09827c91997db66ade30ec7b68b422b4204004296e17b2ee6de49af9ea5b002a"}, + {"path": "crates/labcolors-core/src/wcag22.rs", "sha256": "3e648301d01515f221a783625fa18f41859989f00ceda1cfc3360205d29ab852"}, + {"path": "crates/labcolors-core/src/wcag22/kernel.rs", "sha256": "c97980c1ca2c7ea9cabff9c8d2fb7282773cca180ae15948391c29c9d6196040"}, + {"path": "crates/labcolors-core/src/wcag22/q55_data.rs", "sha256": "af4d23d6b70c45ce6efa839e7dda4bb0a61f6aae43cb805af6fa9b29e6c3bae2"}, + {"path": "crates/labcolors-core/src/wcag22_evidence.rs", "sha256": "3c5a75b07254c6071a64700af208a64987d0f0ea9698eadc54a9e74585ce1f72"}, + {"path": "crates/labcolors-core/src/numerics.rs", "sha256": "ef10c32534f8e3af7c895aaf465a142184ed651d11634d6c0bbb60db4ff2304f"}, + {"path": "crates/labcolors-core/contracts/wcag22-srgb8-v1.json", "sha256": "b4bb7e5f17a99f2c911fdbe3da23a48b049277b796291094950f14680cc3cc7b"}, + {"path": "crates/labcolors-core/contracts/wcag22-srgb8-q55-proof-v1.json", "sha256": "d269e9de689009bb955788bf8762fce56680bf616fc0459b6526a367875a6a08"}, + {"path": "crates/labcolors-core/Cargo.toml", "sha256": "ee56ab2f64b6b87934f5e7c81b99106bd0fff0c6ae83cf8692ae24bed6a84c55"}, + {"path": "Cargo.lock", "sha256": "bab0b2ff01eab4f4f392b4509a909248fd415338e53e3f74563a6b6e0550af4d"}, + {"path": "crates/labcolors-core/benches/wcag22_feasibility_admission.rs", "sha256": "ff713b781927447bfe19ebe26851d4d3f4c0f1173b6d663806b8dedada6a7aa8"}, + {"path": "scripts/check_wcag22_feasibility_benchmark.py", "sha256": "fb70cf986badff7b1e1e257ea6e6fea0a17d719142481a8153bad507f96fe66c"} + ], + "profileLimits": { + "profileId": "compile-v1", + "rawRelations": 2047, + "rawAdjacentEntries": 2047, + "opaqueUtf8Bytes": 65536, + "canonicalRelations": 2047, + "applicableEdges": 2047, + "logicalAssessments": 524032, + "packedResultBytes": 65536 + }, + "scenarios": [ + { + "name": "minimum-evaluated", + "shape": { + "rawRelations": 1, + "rawAdjacentEntries": 1, + "opaqueUtf8Bytes": 2, + "canonicalRelations": 1, + "applicableRelations": 1, + "applicableEdges": 1 + }, + "expected": { + "terminal": "feasible", + "logicalAssessments": 256, + "packedResultBytes": 64, + "feasibleCandidates": 7 + }, + "observedIdentity": { + "terminal": "feasible", + "domainDigestSha256": "9634ac326979b23c2103ffcd92a2b890427ea8914a97b264b0c73409640f8466", + "relationSetDigestSha256": "770f11c7b289f17427541399fa49f4e747a4e84d04a3689e3a7e8f0cea968ecc", + "evaluationIdSha256": "c2ff3ed8394f040a1e01ccf92c07442f8fb06f2e8fcb735412c8a4803a81c5db", + "logicalAssessments": 256, + "assessmentIteratorLen": 256, + "derivedPackedResultBytes": 64, + "feasibleCandidates": 7 + }, + "samples": [ + {"index": 0, "elapsedNs": 32500, "allocationCalls": 1, "allocatedBytes": 64, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 2792, "endLiveBytes": 2856, "peakLiveBytes": 2856, "peakAdditionalLiveBytes": 64}, + {"index": 1, "elapsedNs": 19875, "allocationCalls": 1, "allocatedBytes": 64, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 2792, "endLiveBytes": 2856, "peakLiveBytes": 2856, "peakAdditionalLiveBytes": 64}, + {"index": 2, "elapsedNs": 19334, "allocationCalls": 1, "allocatedBytes": 64, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 2792, "endLiveBytes": 2856, "peakLiveBytes": 2856, "peakAdditionalLiveBytes": 64}, + {"index": 3, "elapsedNs": 19292, "allocationCalls": 1, "allocatedBytes": 64, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 2792, "endLiveBytes": 2856, "peakLiveBytes": 2856, "peakAdditionalLiveBytes": 64}, + {"index": 4, "elapsedNs": 19250, "allocationCalls": 1, "allocatedBytes": 64, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 2792, "endLiveBytes": 2856, "peakLiveBytes": 2856, "peakAdditionalLiveBytes": 64} + ] + }, + { + "name": "maximum-applicable-edges", + "shape": { + "rawRelations": 1, + "rawAdjacentEntries": 2047, + "opaqueUtf8Bytes": 19, + "canonicalRelations": 1, + "applicableRelations": 1, + "applicableEdges": 2047 + }, + "expected": { + "terminal": "infeasible", + "logicalAssessments": 524032, + "packedResultBytes": 65536, + "feasibleCandidates": 0 + }, + "observedIdentity": { + "terminal": "infeasible", + "domainDigestSha256": "9634ac326979b23c2103ffcd92a2b890427ea8914a97b264b0c73409640f8466", + "relationSetDigestSha256": "3498263d9b09b28b10b0a76147bff13bef9844f44b4fe70a3585e6404efac947", + "evaluationIdSha256": "b6011501962a5f2eb55817d2ddab783f37d0eef036ace829bec2675511af394d", + "logicalAssessments": 524032, + "assessmentIteratorLen": 524032, + "derivedPackedResultBytes": 65536, + "feasibleCandidates": 0 + }, + "samples": [ + {"index": 0, "elapsedNs": 16821000, "allocationCalls": 1, "allocatedBytes": 65536, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 9347, "endLiveBytes": 74883, "peakLiveBytes": 74883, "peakAdditionalLiveBytes": 65536}, + {"index": 1, "elapsedNs": 16702834, "allocationCalls": 1, "allocatedBytes": 65536, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 9347, "endLiveBytes": 74883, "peakLiveBytes": 74883, "peakAdditionalLiveBytes": 65536}, + {"index": 2, "elapsedNs": 16682334, "allocationCalls": 1, "allocatedBytes": 65536, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 9347, "endLiveBytes": 74883, "peakLiveBytes": 74883, "peakAdditionalLiveBytes": 65536}, + {"index": 3, "elapsedNs": 15782167, "allocationCalls": 1, "allocatedBytes": 65536, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 9347, "endLiveBytes": 74883, "peakLiveBytes": 74883, "peakAdditionalLiveBytes": 65536}, + {"index": 4, "elapsedNs": 15507375, "allocationCalls": 1, "allocatedBytes": 65536, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 9347, "endLiveBytes": 74883, "peakLiveBytes": 74883, "peakAdditionalLiveBytes": 65536} + ] + }, + { + "name": "maximum-raw-duplicate-relations", + "shape": { + "rawRelations": 2047, + "rawAdjacentEntries": 2047, + "opaqueUtf8Bytes": 26611, + "canonicalRelations": 1, + "applicableRelations": 1, + "applicableEdges": 1 + }, + "expected": { + "terminal": "feasible", + "logicalAssessments": 256, + "packedResultBytes": 64, + "feasibleCandidates": 7 + }, + "observedIdentity": { + "terminal": "feasible", + "domainDigestSha256": "9634ac326979b23c2103ffcd92a2b890427ea8914a97b264b0c73409640f8466", + "relationSetDigestSha256": "2d8c3d7dce6f7caee600851331aabbd45692a0cb530f1d58f08afb6f0a9aacc2", + "evaluationIdSha256": "83fa34dd186f1692438b5594fcae93b5d10746891e7e400b62f063b4f459cc1a", + "logicalAssessments": 256, + "assessmentIteratorLen": 256, + "derivedPackedResultBytes": 64, + "feasibleCandidates": 7 + }, + "samples": [ + {"index": 0, "elapsedNs": 150292, "allocationCalls": 1, "allocatedBytes": 64, "deallocationCalls": 4092, "deallocatedBytes": 14322, "baselineLiveBytes": 165237, "endLiveBytes": 150979, "peakLiveBytes": 165237, "peakAdditionalLiveBytes": 0}, + {"index": 1, "elapsedNs": 145667, "allocationCalls": 1, "allocatedBytes": 64, "deallocationCalls": 4092, "deallocatedBytes": 14322, "baselineLiveBytes": 165237, "endLiveBytes": 150979, "peakLiveBytes": 165237, "peakAdditionalLiveBytes": 0}, + {"index": 2, "elapsedNs": 147084, "allocationCalls": 1, "allocatedBytes": 64, "deallocationCalls": 4092, "deallocatedBytes": 14322, "baselineLiveBytes": 165237, "endLiveBytes": 150979, "peakLiveBytes": 165237, "peakAdditionalLiveBytes": 0}, + {"index": 3, "elapsedNs": 144375, "allocationCalls": 1, "allocatedBytes": 64, "deallocationCalls": 4092, "deallocatedBytes": 14322, "baselineLiveBytes": 165237, "endLiveBytes": 150979, "peakLiveBytes": 165237, "peakAdditionalLiveBytes": 0}, + {"index": 4, "elapsedNs": 149541, "allocationCalls": 1, "allocatedBytes": 64, "deallocationCalls": 4092, "deallocatedBytes": 14322, "baselineLiveBytes": 165237, "endLiveBytes": 150979, "peakLiveBytes": 165237, "peakAdditionalLiveBytes": 0} + ] + }, + { + "name": "maximum-raw-adjacent-duplicates", + "shape": { + "rawRelations": 1, + "rawAdjacentEntries": 2047, + "opaqueUtf8Bytes": 2, + "canonicalRelations": 1, + "applicableRelations": 1, + "applicableEdges": 1 + }, + "expected": { + "terminal": "feasible", + "logicalAssessments": 256, + "packedResultBytes": 64, + "feasibleCandidates": 7 + }, + "observedIdentity": { + "terminal": "feasible", + "domainDigestSha256": "9634ac326979b23c2103ffcd92a2b890427ea8914a97b264b0c73409640f8466", + "relationSetDigestSha256": "770f11c7b289f17427541399fa49f4e747a4e84d04a3689e3a7e8f0cea968ecc", + "evaluationIdSha256": "c2ff3ed8394f040a1e01ccf92c07442f8fb06f2e8fcb735412c8a4803a81c5db", + "logicalAssessments": 256, + "assessmentIteratorLen": 256, + "derivedPackedResultBytes": 64, + "feasibleCandidates": 7 + }, + "samples": [ + {"index": 0, "elapsedNs": 32458, "allocationCalls": 1, "allocatedBytes": 64, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 10130, "endLiveBytes": 10194, "peakLiveBytes": 10194, "peakAdditionalLiveBytes": 64}, + {"index": 1, "elapsedNs": 32500, "allocationCalls": 1, "allocatedBytes": 64, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 10130, "endLiveBytes": 10194, "peakLiveBytes": 10194, "peakAdditionalLiveBytes": 64}, + {"index": 2, "elapsedNs": 32584, "allocationCalls": 1, "allocatedBytes": 64, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 10130, "endLiveBytes": 10194, "peakLiveBytes": 10194, "peakAdditionalLiveBytes": 64}, + {"index": 3, "elapsedNs": 32500, "allocationCalls": 1, "allocatedBytes": 64, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 10130, "endLiveBytes": 10194, "peakLiveBytes": 10194, "peakAdditionalLiveBytes": 64}, + {"index": 4, "elapsedNs": 32625, "allocationCalls": 1, "allocatedBytes": 64, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 10130, "endLiveBytes": 10194, "peakLiveBytes": 10194, "peakAdditionalLiveBytes": 64} + ] + }, + { + "name": "maximum-canonical-applicable-relations", + "shape": { + "rawRelations": 2047, + "rawAdjacentEntries": 2047, + "opaqueUtf8Bytes": 20470, + "canonicalRelations": 2047, + "applicableRelations": 2047, + "applicableEdges": 2047 + }, + "expected": { + "terminal": "feasible", + "logicalAssessments": 524032, + "packedResultBytes": 65536, + "feasibleCandidates": 7 + }, + "observedIdentity": { + "terminal": "feasible", + "domainDigestSha256": "9634ac326979b23c2103ffcd92a2b890427ea8914a97b264b0c73409640f8466", + "relationSetDigestSha256": "2c034157301d1ebd68d207201ff4f18e38400a1bddea7824a77079a0ed96d41c", + "evaluationIdSha256": "db69a53550117574de7167b6f40dcfb8e213f5710dcd6ef16d6487259bd6bdc5", + "logicalAssessments": 524032, + "assessmentIteratorLen": 524032, + "derivedPackedResultBytes": 65536, + "feasibleCandidates": 7 + }, + "samples": [ + {"index": 0, "elapsedNs": 16653000, "allocationCalls": 1, "allocatedBytes": 65536, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 223321, "endLiveBytes": 288857, "peakLiveBytes": 288857, "peakAdditionalLiveBytes": 65536}, + {"index": 1, "elapsedNs": 16323000, "allocationCalls": 1, "allocatedBytes": 65536, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 223321, "endLiveBytes": 288857, "peakLiveBytes": 288857, "peakAdditionalLiveBytes": 65536}, + {"index": 2, "elapsedNs": 16465458, "allocationCalls": 1, "allocatedBytes": 65536, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 223321, "endLiveBytes": 288857, "peakLiveBytes": 288857, "peakAdditionalLiveBytes": 65536}, + {"index": 3, "elapsedNs": 16451000, "allocationCalls": 1, "allocatedBytes": 65536, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 223321, "endLiveBytes": 288857, "peakLiveBytes": 288857, "peakAdditionalLiveBytes": 65536}, + {"index": 4, "elapsedNs": 16482792, "allocationCalls": 1, "allocatedBytes": 65536, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 223321, "endLiveBytes": 288857, "peakLiveBytes": 288857, "peakAdditionalLiveBytes": 65536} + ] + }, + { + "name": "maximum-combined-applicable-envelope", + "shape": { + "rawRelations": 2047, + "rawAdjacentEntries": 2047, + "opaqueUtf8Bytes": 65536, + "canonicalRelations": 2047, + "applicableRelations": 2047, + "applicableEdges": 2047 + }, + "expected": { + "terminal": "feasible", + "logicalAssessments": 524032, + "packedResultBytes": 65536, + "feasibleCandidates": 7 + }, + "observedIdentity": { + "terminal": "feasible", + "domainDigestSha256": "9634ac326979b23c2103ffcd92a2b890427ea8914a97b264b0c73409640f8466", + "relationSetDigestSha256": "647fbd1e61b7b10a58a32f00c6f9f2c1fd4deca9e685e6f682f26ec792dc5a84", + "evaluationIdSha256": "1d71ce6dea709b3977c252e814d0981dfeda2d3d1c42e9c2a1ff60848b4c118a", + "logicalAssessments": 524032, + "assessmentIteratorLen": 524032, + "derivedPackedResultBytes": 65536, + "feasibleCandidates": 7 + }, + "samples": [ + {"index": 0, "elapsedNs": 16747125, "allocationCalls": 1, "allocatedBytes": 65536, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 262646, "endLiveBytes": 328182, "peakLiveBytes": 328182, "peakAdditionalLiveBytes": 65536}, + {"index": 1, "elapsedNs": 16653125, "allocationCalls": 1, "allocatedBytes": 65536, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 262646, "endLiveBytes": 328182, "peakLiveBytes": 328182, "peakAdditionalLiveBytes": 65536}, + {"index": 2, "elapsedNs": 16642500, "allocationCalls": 1, "allocatedBytes": 65536, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 262646, "endLiveBytes": 328182, "peakLiveBytes": 328182, "peakAdditionalLiveBytes": 65536}, + {"index": 3, "elapsedNs": 16648458, "allocationCalls": 1, "allocatedBytes": 65536, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 262646, "endLiveBytes": 328182, "peakLiveBytes": 328182, "peakAdditionalLiveBytes": 65536}, + {"index": 4, "elapsedNs": 16670875, "allocationCalls": 1, "allocatedBytes": 65536, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 262646, "endLiveBytes": 328182, "peakLiveBytes": 328182, "peakAdditionalLiveBytes": 65536} + ] + }, + { + "name": "maximum-canonical-not-applicable-relations", + "shape": { + "rawRelations": 2047, + "rawAdjacentEntries": 0, + "opaqueUtf8Bytes": 36846, + "canonicalRelations": 2047, + "applicableRelations": 0, + "applicableEdges": 0 + }, + "expected": { + "terminal": "not-evaluated", + "logicalAssessments": 0, + "packedResultBytes": 0, + "feasibleCandidates": null + }, + "observedIdentity": { + "terminal": "not-evaluated", + "domainDigestSha256": "9634ac326979b23c2103ffcd92a2b890427ea8914a97b264b0c73409640f8466", + "relationSetDigestSha256": "7e5f031ce0c8ef68c6667d9ae05bcf9818d8d34b1095fdbbc2dd7bc8f8748089", + "evaluationIdSha256": null, + "logicalAssessments": 0, + "assessmentIteratorLen": 0, + "derivedPackedResultBytes": 0, + "feasibleCandidates": null + }, + "samples": [ + {"index": 0, "elapsedNs": 635500, "allocationCalls": 0, "allocatedBytes": 0, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 234356, "endLiveBytes": 234356, "peakLiveBytes": 234356, "peakAdditionalLiveBytes": 0}, + {"index": 1, "elapsedNs": 631708, "allocationCalls": 0, "allocatedBytes": 0, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 234356, "endLiveBytes": 234356, "peakLiveBytes": 234356, "peakAdditionalLiveBytes": 0}, + {"index": 2, "elapsedNs": 631834, "allocationCalls": 0, "allocatedBytes": 0, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 234356, "endLiveBytes": 234356, "peakLiveBytes": 234356, "peakAdditionalLiveBytes": 0}, + {"index": 3, "elapsedNs": 610750, "allocationCalls": 0, "allocatedBytes": 0, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 234356, "endLiveBytes": 234356, "peakLiveBytes": 234356, "peakAdditionalLiveBytes": 0}, + {"index": 4, "elapsedNs": 611708, "allocationCalls": 0, "allocatedBytes": 0, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 234356, "endLiveBytes": 234356, "peakLiveBytes": 234356, "peakAdditionalLiveBytes": 0} + ] + }, + { + "name": "maximum-combined-not-applicable-envelope", + "shape": { + "rawRelations": 2047, + "rawAdjacentEntries": 0, + "opaqueUtf8Bytes": 65536, + "canonicalRelations": 2047, + "applicableRelations": 0, + "applicableEdges": 0 + }, + "expected": { + "terminal": "not-evaluated", + "logicalAssessments": 0, + "packedResultBytes": 0, + "feasibleCandidates": null + }, + "observedIdentity": { + "terminal": "not-evaluated", + "domainDigestSha256": "9634ac326979b23c2103ffcd92a2b890427ea8914a97b264b0c73409640f8466", + "relationSetDigestSha256": "68d40f750b9e1e7520290ce21a5077b5eb19d44308dd5c44076a080486182bc2", + "evaluationIdSha256": null, + "logicalAssessments": 0, + "assessmentIteratorLen": 0, + "derivedPackedResultBytes": 0, + "feasibleCandidates": null + }, + "samples": [ + {"index": 0, "elapsedNs": 823375, "allocationCalls": 0, "allocatedBytes": 0, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 283884, "endLiveBytes": 283884, "peakLiveBytes": 283884, "peakAdditionalLiveBytes": 0}, + {"index": 1, "elapsedNs": 794292, "allocationCalls": 0, "allocatedBytes": 0, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 283884, "endLiveBytes": 283884, "peakLiveBytes": 283884, "peakAdditionalLiveBytes": 0}, + {"index": 2, "elapsedNs": 793125, "allocationCalls": 0, "allocatedBytes": 0, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 283884, "endLiveBytes": 283884, "peakLiveBytes": 283884, "peakAdditionalLiveBytes": 0}, + {"index": 3, "elapsedNs": 791792, "allocationCalls": 0, "allocatedBytes": 0, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 283884, "endLiveBytes": 283884, "peakLiveBytes": 283884, "peakAdditionalLiveBytes": 0}, + {"index": 4, "elapsedNs": 793042, "allocationCalls": 0, "allocatedBytes": 0, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 283884, "endLiveBytes": 283884, "peakLiveBytes": 283884, "peakAdditionalLiveBytes": 0} + ] + }, + { + "name": "maximum-mixed-relations", + "shape": { + "rawRelations": 2047, + "rawAdjacentEntries": 1023, + "opaqueUtf8Bytes": 28662, + "canonicalRelations": 2047, + "applicableRelations": 1023, + "applicableEdges": 1023 + }, + "expected": { + "terminal": "feasible", + "logicalAssessments": 261888, + "packedResultBytes": 32768, + "feasibleCandidates": 7 + }, + "observedIdentity": { + "terminal": "feasible", + "domainDigestSha256": "9634ac326979b23c2103ffcd92a2b890427ea8914a97b264b0c73409640f8466", + "relationSetDigestSha256": "dbe4493668b49a058babc3188b84fc40f321bfd80d8bef22ec9d63f775033eed", + "evaluationIdSha256": "ab188519042404430aa7e71b4766ebf3b64f22fd88cff7898e7bf7ae65b298c2", + "logicalAssessments": 261888, + "assessmentIteratorLen": 261888, + "derivedPackedResultBytes": 32768, + "feasibleCandidates": 7 + }, + "samples": [ + {"index": 0, "elapsedNs": 8671041, "allocationCalls": 1, "allocatedBytes": 32768, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 230041, "endLiveBytes": 262809, "peakLiveBytes": 262809, "peakAdditionalLiveBytes": 32768}, + {"index": 1, "elapsedNs": 8658500, "allocationCalls": 1, "allocatedBytes": 32768, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 230041, "endLiveBytes": 262809, "peakLiveBytes": 262809, "peakAdditionalLiveBytes": 32768}, + {"index": 2, "elapsedNs": 8661250, "allocationCalls": 1, "allocatedBytes": 32768, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 230041, "endLiveBytes": 262809, "peakLiveBytes": 262809, "peakAdditionalLiveBytes": 32768}, + {"index": 3, "elapsedNs": 8660291, "allocationCalls": 1, "allocatedBytes": 32768, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 230041, "endLiveBytes": 262809, "peakLiveBytes": 262809, "peakAdditionalLiveBytes": 32768}, + {"index": 4, "elapsedNs": 8729750, "allocationCalls": 1, "allocatedBytes": 32768, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 230041, "endLiveBytes": 262809, "peakLiveBytes": 262809, "peakAdditionalLiveBytes": 32768} + ] + }, + { + "name": "maximum-opaque-utf8-bytes", + "shape": { + "rawRelations": 1, + "rawAdjacentEntries": 0, + "opaqueUtf8Bytes": 65536, + "canonicalRelations": 1, + "applicableRelations": 0, + "applicableEdges": 0 + }, + "expected": { + "terminal": "not-evaluated", + "logicalAssessments": 0, + "packedResultBytes": 0, + "feasibleCandidates": null + }, + "observedIdentity": { + "terminal": "not-evaluated", + "domainDigestSha256": "9634ac326979b23c2103ffcd92a2b890427ea8914a97b264b0c73409640f8466", + "relationSetDigestSha256": "d89fd489c0f419f8e6ef2ac97f164d2ee1b4b20dc961895da8061688aa24cc2c", + "evaluationIdSha256": null, + "logicalAssessments": 0, + "assessmentIteratorLen": 0, + "derivedPackedResultBytes": 0, + "feasibleCandidates": null + }, + "samples": [ + {"index": 0, "elapsedNs": 336375, "allocationCalls": 0, "allocatedBytes": 0, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 71923, "endLiveBytes": 71923, "peakLiveBytes": 71923, "peakAdditionalLiveBytes": 0}, + {"index": 1, "elapsedNs": 335458, "allocationCalls": 0, "allocatedBytes": 0, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 71923, "endLiveBytes": 71923, "peakLiveBytes": 71923, "peakAdditionalLiveBytes": 0}, + {"index": 2, "elapsedNs": 335208, "allocationCalls": 0, "allocatedBytes": 0, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 71923, "endLiveBytes": 71923, "peakLiveBytes": 71923, "peakAdditionalLiveBytes": 0}, + {"index": 3, "elapsedNs": 334708, "allocationCalls": 0, "allocatedBytes": 0, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 71923, "endLiveBytes": 71923, "peakLiveBytes": 71923, "peakAdditionalLiveBytes": 0}, + {"index": 4, "elapsedNs": 335334, "allocationCalls": 0, "allocatedBytes": 0, "deallocationCalls": 0, "deallocatedBytes": 0, "baselineLiveBytes": 71923, "endLiveBytes": 71923, "peakLiveBytes": 71923, "peakAdditionalLiveBytes": 0} + ] + } + ] +} diff --git a/crates/labcolors-core/src/wcag22_feasibility.rs b/crates/labcolors-core/src/wcag22_feasibility.rs index 143bd1b7..18c9fa35 100644 --- a/crates/labcolors-core/src/wcag22_feasibility.rs +++ b/crates/labcolors-core/src/wcag22_feasibility.rs @@ -1,4 +1,4 @@ -//! Bounded complete WCAG 2.2 enumeration over registered finite colour domains. +//! Bounded complete WCAG 2.2 enumeration over finite colour domains. //! //! Client identifiers and applicability remain opaque declarations. Core owns //! canonicalization, exhaustive pair evaluation, packed decisions and sealed @@ -17,6 +17,10 @@ use crate::wcag22::{ wcag22_profile_v1, }; +#[path = "wcag22_feasibility/explicit.rs"] +#[cfg(feature = "wcag22-explicit-feasibility")] +pub mod explicit; + const CANDIDATE_COUNT: u64 = 256; const PARTITION_BYTES: u64 = CANDIDATE_COUNT / 8; @@ -119,7 +123,7 @@ pub enum ResourceDimensionV1 { RawRelations, /// Applicable adjacent entries before per-relation deduplication. RawAdjacentEntries, - /// Aggregate relation, occurrence and reason UTF-8 bytes. + /// Aggregate candidate, relation, occurrence and reason UTF-8 bytes. OpaqueUtf8Bytes, /// Relations after canonical duplicate removal. CanonicalRelations, @@ -315,6 +319,15 @@ pub enum InvalidRequestV1 { EmptyOccurrenceId, /// No relations were declared. EmptyRelations, + /// An explicit candidate ID was empty. + #[cfg(feature = "wcag22-explicit-feasibility")] + EmptyCandidateId, + /// No explicit candidates were declared. + #[cfg(feature = "wcag22-explicit-feasibility")] + EmptyCandidates, + /// The same explicit candidate ID occurred more than once. + #[cfg(feature = "wcag22-explicit-feasibility")] + DuplicateCandidateId { candidate_id: explicit::CandidateId }, /// An applicable relation had no adjacent colour. EmptyAdjacentSet { relation_id: RelationId }, /// The same relation ID described different canonical declarations. @@ -329,6 +342,14 @@ impl fmt::Display for InvalidRequestV1 { Self::EmptyRelationId => formatter.write_str("relation ID must be non-empty"), Self::EmptyOccurrenceId => formatter.write_str("occurrence ID must be non-empty"), Self::EmptyRelations => formatter.write_str("at least one relation is required"), + #[cfg(feature = "wcag22-explicit-feasibility")] + Self::EmptyCandidateId => formatter.write_str("candidate ID must be non-empty"), + #[cfg(feature = "wcag22-explicit-feasibility")] + Self::EmptyCandidates => formatter.write_str("at least one candidate is required"), + #[cfg(feature = "wcag22-explicit-feasibility")] + Self::DuplicateCandidateId { candidate_id } => { + write!(formatter, "candidate ID {candidate_id} is duplicated") + } Self::EmptyAdjacentSet { relation_id } => { write!( formatter, @@ -372,13 +393,13 @@ pub enum EvaluatorInvariantV1 { pub enum CompilerInvariantV1 { /// Derived raw/canonical layout counts violated their construction laws. LayoutMismatch, - /// Observed evaluator cells differed from the preflighted `256E` work. + /// Observed evaluator cells differed from the preflighted `C×E` work. AssessmentCardinalityMismatch { expected: u64, observed: u64 }, - /// Observed candidates differed from the registered domain count. + /// Observed candidates differed from the declared finite-domain count. CandidateCardinalityMismatch { expected: u64, observed: u64 }, /// Packed storage rejected a cell proved addressable by preflight. DecisionStorageRejectedCell, - /// Packed storage rejected the fixed 256-bit partition. + /// Packed storage rejected a partition bit proved addressable by preflight. DecisionStorageRejectedPartition, /// Completed matrix, partition or proof counters disagreed. CompleteResultMismatch, @@ -516,29 +537,90 @@ struct WorkLayoutV1 { packed_result_bytes: u64, } +/// Private boundary implemented by every finite sRGB8 enumeration. Domain +/// implementations own only candidate identity/order; the compiler below owns +/// relation canonicalization, exhaustive evaluation and packed evidence. +trait FiniteSrgb8DomainV1: Sized { + type Packing: PackedDomainV1 + fmt::Debug; + + fn raw_opaque_utf8_bytes(&self) -> Result; + fn canonicalize(&mut self) -> Result<(), InvalidRequestV1>; + fn candidates(&self) -> impl ExactSizeIterator + '_; + + fn candidate_count(&self) -> Result { + u64::try_from(self.candidates().len()).map_err(|_| InvalidRequestV1::ArithmeticOverflow) + } +} + +impl FiniteSrgb8DomainV1 for DomainIdV1 { + type Packing = NeutralAxisPackingV1; + + fn raw_opaque_utf8_bytes(&self) -> Result { + Ok(0) + } + + fn canonicalize(&mut self) -> Result<(), InvalidRequestV1> { + Ok(()) + } + + fn candidates(&self) -> impl ExactSizeIterator + '_ { + DomainIdV1::candidates(*self) + } +} + fn overflow() -> InvalidRequestV1 { InvalidRequestV1::ArithmeticOverflow } -fn checked_logical_assessments_v1(applicable_edges: u64) -> Result { - CANDIDATE_COUNT +fn checked_logical_assessments_for_domain_v1( + candidate_count: u64, + applicable_edges: u64, +) -> Result { + candidate_count .checked_mul(applicable_edges) .ok_or_else(overflow) } -fn checked_packed_result_bytes_v1( +#[cfg(test)] +fn checked_logical_assessments_v1(applicable_edges: u64) -> Result { + checked_logical_assessments_for_domain_v1(CANDIDATE_COUNT, applicable_edges) +} + +fn checked_bit_bytes_v1(bits: u64) -> Result { + (bits / 8) + .checked_add(u64::from(bits % 8 != 0)) + .ok_or_else(overflow) +} + +fn checked_packed_result_bytes_for_domain_v1( + candidate_count: u64, applicable_relations: u64, applicable_edges: u64, ) -> Result { if applicable_relations == 0 { return Ok(0); } - applicable_edges - .checked_add(1) - .and_then(|value| PARTITION_BYTES.checked_mul(value)) + let logical_assessments = + checked_logical_assessments_for_domain_v1(candidate_count, applicable_edges)?; + let matrix_bytes = checked_bit_bytes_v1(logical_assessments)?; + let partition_bytes = checked_bit_bytes_v1(candidate_count)?; + matrix_bytes + .checked_add(partition_bytes) .ok_or_else(overflow) } +#[cfg(test)] +fn checked_packed_result_bytes_v1( + applicable_relations: u64, + applicable_edges: u64, +) -> Result { + checked_packed_result_bytes_for_domain_v1( + CANDIDATE_COUNT, + applicable_relations, + applicable_edges, + ) +} + fn resource_value( raw: RawInputCountsV1, canonical: CanonicalCountsV1, @@ -569,12 +651,31 @@ fn resource_limit(limits: ResourceLimitsV1, dimension: ResourceDimensionV1) -> u } } +#[cfg(test)] fn checked_layout_v1( profile_id: ResourceProfileIdV1, raw: RawInputCountsV1, canonical: CanonicalCountsV1, limits: ResourceLimitsV1, addressable_byte_limit: u64, +) -> Result { + checked_layout_for_domain_v1( + profile_id, + CANDIDATE_COUNT, + raw, + canonical, + limits, + addressable_byte_limit, + ) +} + +fn checked_layout_for_domain_v1( + profile_id: ResourceProfileIdV1, + candidate_count: u64, + raw: RawInputCountsV1, + canonical: CanonicalCountsV1, + limits: ResourceLimitsV1, + addressable_byte_limit: u64, ) -> Result { let total_relations = canonical .applicable_relations @@ -603,21 +704,28 @@ fn checked_layout_v1( )); } - let logical_assessments = checked_logical_assessments_v1(canonical.applicable_edges)?; + if candidate_count == 0 { + return Err(ErrorV1::CompilerInvariantViolation( + CompilerInvariantV1::LayoutMismatch, + )); + } + let logical_assessments = + checked_logical_assessments_for_domain_v1(candidate_count, canonical.applicable_edges)?; let failure_matrix_bytes = if canonical.applicable_relations == 0 { 0 } else { - PARTITION_BYTES - .checked_mul(canonical.applicable_edges) - .ok_or_else(overflow)? + checked_bit_bytes_v1(logical_assessments)? }; let partition_bytes = if canonical.applicable_relations == 0 { 0 } else { - PARTITION_BYTES + checked_bit_bytes_v1(candidate_count)? }; - let packed_result_bytes = - checked_packed_result_bytes_v1(canonical.applicable_relations, canonical.applicable_edges)?; + let packed_result_bytes = checked_packed_result_bytes_for_domain_v1( + candidate_count, + canonical.applicable_relations, + canonical.applicable_edges, + )?; for dimension in [ ResourceDimensionV1::RawRelations, @@ -662,7 +770,7 @@ fn checked_layout_v1( applicable_relations: canonical.applicable_relations, not_evaluated_relations: canonical.not_evaluated_relations, applicable_edges: canonical.applicable_edges, - candidate_count: CANDIDATE_COUNT, + candidate_count, logical_assessments, failure_matrix_bytes, partition_bytes, @@ -670,6 +778,24 @@ fn checked_layout_v1( }) } +fn checked_layout_for_finite_domain_v1( + profile_id: ResourceProfileIdV1, + domain: &D, + raw: RawInputCountsV1, + canonical: CanonicalCountsV1, + limits: ResourceLimitsV1, + addressable_byte_limit: u64, +) -> Result { + checked_layout_for_domain_v1( + profile_id, + domain.candidate_count()?, + raw, + canonical, + limits, + addressable_byte_limit, + ) +} + fn add_checked(target: &mut u64, value: u64) -> Result<(), ErrorV1> { *target = target.checked_add(value).ok_or_else(overflow)?; Ok(()) @@ -679,11 +805,11 @@ fn usize_as_u64(value: usize) -> Result { u64::try_from(value).map_err(|_| overflow().into()) } -fn raw_counts(request: &RequestV1) -> Result { - let raw_relations = usize_as_u64(request.relations.len())?; +fn raw_relation_counts(relations: &[RelationV1]) -> Result { + let raw_relations = usize_as_u64(relations.len())?; let mut raw_adjacent_entries = 0_u64; let mut opaque_utf8_bytes = 0_u64; - for relation in &request.relations { + for relation in relations { add_checked( &mut opaque_utf8_bytes, usize_as_u64(relation.relation_id.as_str().len())?, @@ -709,6 +835,20 @@ fn raw_counts(request: &RequestV1) -> Result { }) } +#[cfg(test)] +fn raw_counts(request: &RequestV1) -> Result { + raw_counts_for_domain(&request.domain_id, &request.relations) +} + +fn raw_counts_for_domain( + domain: &D, + relations: &[RelationV1], +) -> Result { + let mut raw = raw_relation_counts(relations)?; + add_checked(&mut raw.opaque_utf8_bytes, domain.raw_opaque_utf8_bytes()?)?; + Ok(raw) +} + fn check_raw_limits(profile_id: ResourceProfileIdV1, raw: RawInputCountsV1) -> Result<(), ErrorV1> { for (dimension, requested) in [ (ResourceDimensionV1::RawRelations, raw.raw_relations), @@ -731,33 +871,35 @@ fn check_raw_limits(profile_id: ResourceProfileIdV1, raw: RawInputCountsV1) -> R Ok(()) } +#[cfg(test)] fn canonicalize(request: &mut RequestV1) -> Result { - for relation in &mut request.relations { + canonicalize_relations(&mut request.relations) +} + +fn canonicalize_relations(relations: &mut Vec) -> Result { + for relation in relations.iter_mut() { if let RelationKindV1::Applicable { adjacent, .. } = &mut relation.kind { adjacent.sort_unstable(); adjacent.dedup(); } } - request - .relations - .sort_unstable_by(|left, right| left.relation_id.cmp(&right.relation_id)); + relations.sort_unstable_by(|left, right| left.relation_id.cmp(&right.relation_id)); - let conflict_index = request - .relations + let conflict_index = relations .windows(2) .position(|pair| pair[0].relation_id == pair[1].relation_id && pair[0] != pair[1]); if let Some(conflict_index) = conflict_index { // `RelationId` uses Arc: this is a fixed-size ownership clone, // never a proportional copy of the opaque client bytes. - let relation_id = request.relations[conflict_index].relation_id.clone(); + let relation_id = relations[conflict_index].relation_id.clone(); return Err(InvalidRequestV1::ConflictingRelationId { relation_id }.into()); } - request.relations.dedup(); + relations.dedup(); let mut applicable_relations = 0_u64; let mut not_evaluated_relations = 0_u64; let mut applicable_edges = 0_u64; - for relation in &request.relations { + for relation in relations.iter() { match &relation.kind { RelationKindV1::Applicable { adjacent, .. } => { add_checked(&mut applicable_relations, 1)?; @@ -767,7 +909,7 @@ fn canonicalize(request: &mut RequestV1) -> Result { } } Ok(CanonicalCountsV1 { - canonical_relations: usize_as_u64(request.relations.len())?, + canonical_relations: usize_as_u64(relations.len())?, applicable_relations, not_evaluated_relations, applicable_edges, @@ -915,7 +1057,13 @@ trait DecisionStorage { logical_index: u64, decision: Wcag22ApplicableDecisionV1, ) -> Result<(), ()>; - fn finish(&mut self, passing_partition: [u8; 32]) -> Result<(), ()>; + #[cfg(feature = "wcag22-explicit-feasibility")] + fn write_feasible_candidate( + &mut self, + matrix_bytes: u64, + candidate_index: u64, + ) -> Result<(), ()>; + fn finish(&mut self, partition: &[u8]) -> Result<(), ()>; } #[derive(Debug, Default)] @@ -923,6 +1071,13 @@ struct PackedDecisionStorage { bytes: Vec, } +impl PackedDecisionStorage { + #[cfg(feature = "wcag22-explicit-feasibility")] + fn into_bytes(self) -> Vec { + self.bytes + } +} + impl DecisionStorage for PackedDecisionStorage { fn try_reserve_exact(&mut self, requested_bytes: usize) -> Result<(), ()> { self.bytes @@ -946,42 +1101,128 @@ impl DecisionStorage for PackedDecisionStorage { Ok(()) } - fn finish(&mut self, passing_partition: [u8; 32]) -> Result<(), ()> { - let start = self.bytes.len().checked_sub(32).ok_or(())?; + #[cfg(feature = "wcag22-explicit-feasibility")] + fn write_feasible_candidate( + &mut self, + matrix_bytes: u64, + candidate_index: u64, + ) -> Result<(), ()> { + let byte_index = matrix_bytes + .checked_add(candidate_index / 8) + .and_then(|value| usize::try_from(value).ok()) + .ok_or(())?; + let bit = (candidate_index % 8) as u8; + let byte = self.bytes.get_mut(byte_index).ok_or(())?; + *byte |= 1_u8 << bit; + Ok(()) + } + + fn finish(&mut self, partition: &[u8]) -> Result<(), ()> { + let start = self.bytes.len().checked_sub(partition.len()).ok_or(())?; let destination = self.bytes.get_mut(start..).ok_or(())?; - if destination.len() != passing_partition.len() { + if destination.len() != partition.len() { return Err(()); } - destination.copy_from_slice(&passing_partition); + destination.copy_from_slice(partition); Ok(()) } } +trait PackedDomainV1 { + type Partition: Default + fmt::Debug; + + fn record_feasible( + partition: &mut Self::Partition, + storage: &mut S, + matrix_bytes: u64, + candidate_index: usize, + ) -> Result<(), ()>; + + fn finish(partition: &Self::Partition, storage: &mut S) -> Result<(), ()>; +} + #[derive(Debug)] -struct KernelEvaluatedV1 { - domain_id: DomainIdV1, +struct NeutralAxisPackingV1; + +impl PackedDomainV1 for NeutralAxisPackingV1 { + type Partition = [u8; PARTITION_BYTES as usize]; + + fn record_feasible( + partition: &mut Self::Partition, + _storage: &mut S, + _matrix_bytes: u64, + candidate_index: usize, + ) -> Result<(), ()> { + // The associated type seals this packing to DomainIdV1, whose exact + // iterator yields only 0..256; bounds safety therefore cannot depend on + // client data. The post-loop check only validates that private iterator + // contract after this write, without a branch in every hot cell. + partition[candidate_index / 8] |= 1_u8 << (candidate_index % 8); + Ok(()) + } + + fn finish(partition: &Self::Partition, storage: &mut S) -> Result<(), ()> { + storage.finish(partition) + } +} + +#[derive(Debug)] +#[cfg(feature = "wcag22-explicit-feasibility")] +struct VariablePackingV1; + +#[cfg(feature = "wcag22-explicit-feasibility")] +impl PackedDomainV1 for VariablePackingV1 { + type Partition = (); + + fn record_feasible( + _partition: &mut Self::Partition, + storage: &mut S, + matrix_bytes: u64, + candidate_index: usize, + ) -> Result<(), ()> { + let candidate_index = u64::try_from(candidate_index).map_err(|_| ())?; + storage.write_feasible_candidate(matrix_bytes, candidate_index) + } + + fn finish( + _partition: &Self::Partition, + _storage: &mut S, + ) -> Result<(), ()> { + Ok(()) + } +} + +#[derive(Debug)] +struct KernelEvaluatedV1 { + domain: D, resource_profile_id: ResourceProfileIdV1, relations: Vec, layout: WorkLayoutV1, observed_candidates: u64, observed_assessments: u64, passing_candidates: u64, - partition: [u8; 32], + partition: ::Partition, atomic_evidence: AtomicEvidenceBindingV1, } #[derive(Debug)] -struct KernelNotEvaluatedV1 { - domain_id: DomainIdV1, +struct KernelNotEvaluatedV1 { + domain: D, resource_profile_id: ResourceProfileIdV1, relations: Vec, layout: WorkLayoutV1, } #[derive(Debug)] -enum KernelResultV1 { - Evaluated(KernelEvaluatedV1), - NotEvaluated(KernelNotEvaluatedV1), +enum KernelResultV1 { + Evaluated(KernelEvaluatedV1), + NotEvaluated(KernelNotEvaluatedV1), +} + +struct KernelRequestV1 { + domain: D, + relations: Vec, + resource_profile_id: ResourceProfileIdV1, } fn evaluator_error( @@ -1003,15 +1244,38 @@ fn compiler_result_error() -> ErrorV1 { } fn evaluate_with( - mut request: RequestV1, + request: RequestV1, + evaluator: &mut E, + storage: &mut S, +) -> Result, ErrorV1> { + evaluate_domain_with( + KernelRequestV1 { + domain: request.domain_id, + relations: request.relations, + resource_profile_id: request.resource_profile_id, + }, + evaluator, + storage, + ) +} + +fn evaluate_domain_with( + mut request: KernelRequestV1, evaluator: &mut E, storage: &mut S, -) -> Result { - let raw = raw_counts(&request)?; +) -> Result, ErrorV1> +where + D: FiniteSrgb8DomainV1, + E: PairEvaluator, + S: DecisionStorage, +{ + let raw = raw_counts_for_domain(&request.domain, &request.relations)?; check_raw_limits(request.resource_profile_id, raw)?; - let canonical = canonicalize(&mut request)?; - let layout = checked_layout_v1( + request.domain.canonicalize()?; + let canonical = canonicalize_relations(&mut request.relations)?; + let layout = checked_layout_for_finite_domain_v1( request.resource_profile_id, + &request.domain, raw, canonical, ResourceLimitsV1::for_profile(request.resource_profile_id), @@ -1034,7 +1298,7 @@ fn evaluate_with( if layout.applicable_relations == 0 { return Ok(KernelResultV1::NotEvaluated(KernelNotEvaluatedV1 { - domain_id: request.domain_id, + domain: request.domain, resource_profile_id: request.resource_profile_id, relations: request.relations, layout, @@ -1047,19 +1311,25 @@ fn evaluate_with( CompilerInvariantV1::LayoutMismatch, )); }; + let first_candidate = request.domain.candidates().next().ok_or({ + ErrorV1::CompilerInvariantViolation(CompilerInvariantV1::CandidateCardinalityMismatch { + expected: layout.candidate_count, + observed: 0, + }) + })?; let expected_evidence = expected_atomic_evidence_binding_v1().map_err(|source| { evaluator_error( - Srgb8::new([0; 3]), + first_candidate, first_applicable.0, first_applicable.1, EvaluatorInvariantV1::Source(source), ) })?; - let mut partition = [0_u8; 32]; let mut logical_index = 0_u64; let mut observed_candidates = 0_u64; let mut passing_candidates = 0_u64; - for (candidate_index, candidate) in request.domain_id.candidates().enumerate() { + let mut partition = ::Partition::default(); + for (candidate_index, candidate) in request.domain.candidates().enumerate() { let mut candidate_passes = true; for relation in &request.relations { let RelationKindV1::Applicable { @@ -1141,7 +1411,17 @@ fn evaluate_with( } } if candidate_passes { - partition[candidate_index / 8] |= 1_u8 << (candidate_index % 8); + D::Packing::record_feasible( + &mut partition, + storage, + layout.failure_matrix_bytes, + candidate_index, + ) + .map_err(|()| { + ErrorV1::CompilerInvariantViolation( + CompilerInvariantV1::DecisionStorageRejectedPartition, + ) + })?; passing_candidates += 1; } observed_candidates += 1; @@ -1162,12 +1442,12 @@ fn evaluate_with( }, )); } - storage.finish(partition).map_err(|()| { + D::Packing::finish(&partition, storage).map_err(|()| { ErrorV1::CompilerInvariantViolation(CompilerInvariantV1::DecisionStorageRejectedPartition) })?; Ok(KernelResultV1::Evaluated(KernelEvaluatedV1 { - domain_id: request.domain_id, + domain: request.domain, resource_profile_id: request.resource_profile_id, relations: request.relations, layout, @@ -1194,10 +1474,23 @@ fn packed_bit(bytes: &[u8], logical_index: u64) -> bool { bytes[byte_index] & (1_u8 << bit) != 0 } -fn validate_complete_result_v1( +#[cfg(feature = "wcag22-explicit-feasibility")] +fn unused_tail_bits_are_zero(bytes: &[u8], used_bits: u64) -> bool { + let remainder = (used_bits % 8) as u8; + if remainder == 0 { + return true; + } + let Some(last) = bytes.last() else { + return false; + }; + let used_mask = ((1_u16 << remainder) - 1) as u8; + last & !used_mask == 0 +} + +fn validate_neutral_complete_result_v1( layout: WorkLayoutV1, matrix: &[u8], - partition: &[u8; 32], + partition: &[u8; PARTITION_BYTES as usize], counters: EvaluationProofCountersV1, ) -> Result<(), CompilerInvariantV1> { let expected_matrix_bytes = usize::try_from(layout.failure_matrix_bytes) @@ -1208,7 +1501,7 @@ fn validate_complete_result_v1( if counters.logical_assessments != layout.logical_assessments { return Err(CompilerInvariantV1::CompleteResultMismatch); } - let mut derived_partition = [0_u8; 32]; + let mut derived_partition = [0_u8; PARTITION_BYTES as usize]; let mut passing = 0_u64; for candidate in 0_u64..CANDIDATE_COUNT { let row_start = candidate @@ -1226,16 +1519,64 @@ fn validate_complete_result_v1( passing += 1; } } - if &derived_partition != partition { + if &derived_partition != partition || counters.passing_candidates != passing { return Err(CompilerInvariantV1::CompleteResultMismatch); } + if counters + .passing_candidates + .checked_add(counters.failing_candidates) + != Some(CANDIDATE_COUNT) + { + return Err(CompilerInvariantV1::CompleteResultMismatch); + } + Ok(()) +} + +#[cfg(feature = "wcag22-explicit-feasibility")] +fn validate_variable_complete_result_v1( + layout: WorkLayoutV1, + matrix: &[u8], + partition: &[u8], + counters: EvaluationProofCountersV1, +) -> Result<(), CompilerInvariantV1> { + let expected_matrix_bytes = usize::try_from(layout.failure_matrix_bytes) + .map_err(|_| CompilerInvariantV1::CompleteResultMismatch)?; + let expected_partition_bytes = usize::try_from(layout.partition_bytes) + .map_err(|_| CompilerInvariantV1::CompleteResultMismatch)?; + if matrix.len() != expected_matrix_bytes + || partition.len() != expected_partition_bytes + || !unused_tail_bits_are_zero(matrix, layout.logical_assessments) + || !unused_tail_bits_are_zero(partition, layout.candidate_count) + { + return Err(CompilerInvariantV1::CompleteResultMismatch); + } + if counters.logical_assessments != layout.logical_assessments { + return Err(CompilerInvariantV1::CompleteResultMismatch); + } + let mut passing = 0_u64; + for candidate in 0_u64..layout.candidate_count { + let row_start = candidate + .checked_mul(layout.applicable_edges) + .ok_or(CompilerInvariantV1::CompleteResultMismatch)?; + let mut row_passes = true; + for edge in 0..layout.applicable_edges { + let failed = packed_bit(matrix, row_start + edge); + row_passes &= !failed; + } + if row_passes != packed_bit(partition, candidate) { + return Err(CompilerInvariantV1::CompleteResultMismatch); + } + if row_passes { + passing += 1; + } + } if counters.passing_candidates != passing { return Err(CompilerInvariantV1::CompleteResultMismatch); } if counters .passing_candidates .checked_add(counters.failing_candidates) - != Some(CANDIDATE_COUNT) + != Some(layout.candidate_count) { return Err(CompilerInvariantV1::CompleteResultMismatch); } @@ -1298,7 +1639,7 @@ fn evaluation_id( binding: &AtomicEvidenceBindingV1, layout: WorkLayoutV1, matrix_digest: &[u8; 32], - partition: &[u8; 32], + partition: &[u8], ) -> EvaluationIdV1 { let mut hasher = Hasher::new(); hasher.update(EVALUATION_SEPARATOR); @@ -1324,6 +1665,70 @@ fn evaluation_id( EvaluationIdV1(*hasher.finalize().as_bytes()) } +#[cfg(feature = "wcag22-explicit-feasibility")] +struct SealedPackedV1 { + packed: Vec, + matrix_digest: [u8; 32], + matrix_bytes: usize, +} + +#[cfg(feature = "wcag22-explicit-feasibility")] +impl SealedPackedV1 { + fn partition(&self) -> &[u8] { + &self.packed[self.matrix_bytes..] + } +} + +#[cfg(feature = "wcag22-explicit-feasibility")] +fn seal_evaluated_v1( + result: &KernelEvaluatedV1, + packed: Vec, +) -> Result { + let matrix_bytes = usize::try_from(result.layout.failure_matrix_bytes).map_err(|_| { + ErrorV1::ResourceLimitExceeded { + profile_id: result.resource_profile_id, + dimension: ResourceDimensionV1::PackedResultBytes, + requested: result.layout.failure_matrix_bytes, + limit: usize::MAX as u64, + } + })?; + let packed_bytes = usize::try_from(result.layout.packed_result_bytes).map_err(|_| { + ErrorV1::ResourceLimitExceeded { + profile_id: result.resource_profile_id, + dimension: ResourceDimensionV1::PackedResultBytes, + requested: result.layout.packed_result_bytes, + limit: usize::MAX as u64, + } + })?; + if packed.len() != packed_bytes || matrix_bytes > packed_bytes { + return Err(compiler_result_error()); + } + let (matrix, partition) = packed.split_at(matrix_bytes); + let Some(failing_candidates) = result + .layout + .candidate_count + .checked_sub(result.passing_candidates) + else { + return Err(compiler_result_error()); + }; + validate_variable_complete_result_v1( + result.layout, + matrix, + partition, + EvaluationProofCountersV1 { + logical_assessments: result.observed_assessments, + passing_candidates: result.passing_candidates, + failing_candidates, + }, + ) + .map_err(ErrorV1::CompilerInvariantViolation)?; + Ok(SealedPackedV1 { + matrix_digest: *sha256::digest(matrix).as_bytes(), + packed, + matrix_bytes, + }) +} + /// Sealed evidence for one complete evaluated terminal. #[derive(Debug, Clone, PartialEq, Eq)] pub struct EvaluationProofV1 { @@ -1479,27 +1884,36 @@ impl<'a> AssessmentV1<'a> { } } -struct AssessmentIter<'a> { - record: &'a EvaluatedV1, - candidate: u16, +struct AssessmentCellV1<'a> { + candidate_index: u64, + relation: &'a RelationV1, + adjacent: Srgb8, + decision: Wcag22ApplicableDecisionV1, +} + +struct AssessmentCursorV1<'a> { + relations: &'a [RelationV1], + matrix: &'a [u8], + candidate_index: u64, + candidate_count: u64, relation_index: usize, adjacent_index: usize, logical_index: u64, remaining: usize, } -impl<'a> Iterator for AssessmentIter<'a> { - type Item = AssessmentV1<'a>; +impl<'a> Iterator for AssessmentCursorV1<'a> { + type Item = AssessmentCellV1<'a>; fn next(&mut self) -> Option { - while self.candidate < 256 { - if self.relation_index >= self.record.relations.len() { - self.candidate += 1; + while self.candidate_index < self.candidate_count { + if self.relation_index >= self.relations.len() { + self.candidate_index += 1; self.relation_index = 0; self.adjacent_index = 0; continue; } - let relation = &self.record.relations[self.relation_index]; + let relation = &self.relations[self.relation_index]; let RelationKindV1::Applicable { adjacent, .. } = &relation.kind else { self.relation_index += 1; self.adjacent_index = 0; @@ -1511,8 +1925,7 @@ impl<'a> Iterator for AssessmentIter<'a> { continue; } let adjacent_value = adjacent[self.adjacent_index]; - let candidate_value = self.candidate as u8; - let decision = if packed_bit(self.record.failure_matrix(), self.logical_index) { + let decision = if packed_bit(self.matrix, self.logical_index) { Wcag22ApplicableDecisionV1::Fail } else { Wcag22ApplicableDecisionV1::Pass @@ -1520,9 +1933,9 @@ impl<'a> Iterator for AssessmentIter<'a> { self.adjacent_index += 1; self.logical_index += 1; self.remaining -= 1; - return Some(AssessmentV1 { - candidate: Srgb8::new([candidate_value; 3]), - relation_id: &relation.relation_id, + return Some(AssessmentCellV1 { + candidate_index: self.candidate_index, + relation, adjacent: adjacent_value, decision, }); @@ -1535,6 +1948,31 @@ impl<'a> Iterator for AssessmentIter<'a> { } } +impl ExactSizeIterator for AssessmentCursorV1<'_> {} + +struct AssessmentIter<'a> { + cursor: AssessmentCursorV1<'a>, +} + +impl<'a> Iterator for AssessmentIter<'a> { + type Item = AssessmentV1<'a>; + + fn next(&mut self) -> Option { + let cell = self.cursor.next()?; + let candidate = cell.candidate_index as u8; + Some(AssessmentV1 { + candidate: Srgb8::new([candidate; 3]), + relation_id: &cell.relation.relation_id, + adjacent: cell.adjacent, + decision: cell.decision, + }) + } + + fn size_hint(&self) -> (usize, Option) { + self.cursor.size_hint() + } +} + impl ExactSizeIterator for AssessmentIter<'_> {} /// Complete evaluated terminal record. It has no public constructor. @@ -1613,12 +2051,16 @@ impl EvaluatedV1 { /// Full candidate-major `256E` matrix without per-cell allocation. pub fn assessments(&self) -> impl ExactSizeIterator> + '_ { AssessmentIter { - record: self, - candidate: 0, - relation_index: 0, - adjacent_index: 0, - logical_index: 0, - remaining: self.layout.logical_assessments as usize, + cursor: AssessmentCursorV1 { + relations: &self.relations, + matrix: self.failure_matrix(), + candidate_index: 0, + candidate_count: self.layout.candidate_count, + relation_index: 0, + adjacent_index: 0, + logical_index: 0, + remaining: self.layout.logical_assessments as usize, + }, } } } @@ -1715,10 +2157,10 @@ pub fn evaluate(request: RequestV1) -> Result { match evaluate_with(request, &mut evaluator, &mut storage)? { KernelResultV1::NotEvaluated(result) => { debug_assert_eq!(result.layout.applicable_relations, 0); - let domain_digest = domain_digest(result.domain_id); + let domain_digest = domain_digest(result.domain); let relation_set_digest = relation_set_digest(&result.relations); Ok(FeasibilityV1::NotEvaluated(NotEvaluatedV1 { - domain_id: result.domain_id, + domain_id: result.domain, relations: result.relations, resource_profile_id: result.resource_profile_id, domain_digest, @@ -1748,17 +2190,18 @@ pub fn evaluate(request: RequestV1) -> Result { return Err(compiler_result_error()); } let (matrix, packed_partition) = storage.bytes.split_at(matrix_length); - if packed_partition != result.partition { + let partition = result.partition; + if packed_partition != partition { return Err(compiler_result_error()); } let Some(failing_candidates) = CANDIDATE_COUNT.checked_sub(result.passing_candidates) else { return Err(compiler_result_error()); }; - validate_complete_result_v1( + validate_neutral_complete_result_v1( result.layout, matrix, - &result.partition, + &partition, EvaluationProofCountersV1 { logical_assessments: result.observed_assessments, passing_candidates: result.passing_candidates, @@ -1767,7 +2210,7 @@ pub fn evaluate(request: RequestV1) -> Result { ) .map_err(ErrorV1::CompilerInvariantViolation)?; - let domain_digest = domain_digest(result.domain_id); + let domain_digest = domain_digest(result.domain); let relation_set_digest = relation_set_digest(&result.relations); let binding = result.atomic_evidence; let matrix_digest = *sha256::digest(matrix).as_bytes(); @@ -1777,12 +2220,12 @@ pub fn evaluate(request: RequestV1) -> Result { &binding, result.layout, &matrix_digest, - &result.partition, + &partition, ); let proof = EvaluationProofV1 { evaluation_id, resource_profile_id: result.resource_profile_id, - domain_id: result.domain_id, + domain_id: result.domain, domain_digest, domain_count: result.observed_candidates, domain_first: Srgb8::new([0; 3]), @@ -1794,11 +2237,11 @@ pub fn evaluate(request: RequestV1) -> Result { applicable_edges: result.layout.applicable_edges, logical_assessments: result.observed_assessments, matrix_digest, - partition: result.partition, + partition, atomic_evidence: binding, }; let evaluated = EvaluatedV1 { - domain_id: result.domain_id, + domain_id: result.domain, relations: result.relations, layout: result.layout, packed: storage.bytes, diff --git a/crates/labcolors-core/src/wcag22_feasibility/explicit.rs b/crates/labcolors-core/src/wcag22_feasibility/explicit.rs new file mode 100644 index 00000000..66ae402c --- /dev/null +++ b/crates/labcolors-core/src/wcag22_feasibility/explicit.rs @@ -0,0 +1,719 @@ +//! Client-declared finite sRGB8 feasibility (#296-A). + +use core::fmt; +use std::sync::Arc; + +use crate::Srgb8; +use crate::numerics::{NumericalArtifactIdV2, NumericalErrorBoundIdV2, NumericalProofIdV2}; +use crate::sha256::Hasher; +use crate::wcag22::{Wcag22ApplicableDecisionV1, Wcag22ProfileIdV1}; + +use super::{ + AssessmentCellV1, AssessmentCursorV1, AtomicEvidenceBindingV1, AtomicPairEvaluator, + DomainDigestV1, ErrorV1, EvaluationIdV1, FiniteSrgb8DomainV1, InvalidRequestV1, + KernelRequestV1, KernelResultV1, PackedDecisionStorage, RelationId, RelationSetDigestV1, + RelationV1, ResourceProfileIdV1, VariablePackingV1, WorkLayoutV1, evaluate_domain_with, + hash_len_prefixed, hash_u64, packed_bit, relation_set_digest, seal_evaluated_v1, +}; + +const DOMAIN_SEPARATOR: &[u8] = b"labcolors/wcag22-feasibility/domain/explicit-srgb8-set/v1\0"; +const EVALUATION_SEPARATOR: &[u8] = + b"labcolors/wcag22-feasibility/evaluation/explicit-srgb8-set/v1\0"; + +/// Opaque client-owned candidate identity. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct CandidateId(Arc); + +impl CandidateId { + /// Construct a non-empty opaque ID. Core uses the exact UTF-8 bytes and + /// never normalizes or interprets the text. + pub fn try_new(value: impl Into) -> Result { + let value = value.into(); + if value.is_empty() { + return Err(InvalidRequestV1::EmptyCandidateId); + } + Ok(Self(value.into())) + } + + /// Exact client bytes used for canonical ordering and identity. + pub fn as_str(&self) -> &str { + self.0.as_ref() + } +} + +impl fmt::Display for CandidateId { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str(self.as_str()) + } +} + +/// One explicit physical candidate. The ID remains opaque to Core. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CandidateV1 { + candidate_id: CandidateId, + emitted: Srgb8, +} + +impl CandidateV1 { + /// Bind an opaque client ID to one exact final encoded-sRGB8 value. + pub const fn new(candidate_id: CandidateId, emitted: Srgb8) -> Self { + Self { + candidate_id, + emitted, + } + } + + /// Opaque client-owned identity. + pub const fn candidate_id(&self) -> &CandidateId { + &self.candidate_id + } + + /// Exact final physical bytes evaluated by Core. + pub const fn emitted(&self) -> Srgb8 { + self.emitted + } +} + +/// Owned client-declared finite domain before Core canonicalization. +#[derive(Debug)] +pub struct DomainRequestV1 { + candidates: Vec, +} + +impl DomainRequestV1 { + /// Construct a non-empty declared set. Duplicate IDs are rejected at the + /// compiler boundary after raw resource preflight and before evaluation. + pub fn try_new(candidates: Vec) -> Result { + if candidates.is_empty() { + return Err(InvalidRequestV1::EmptyCandidates); + } + Ok(Self { candidates }) + } + + fn into_candidates(self) -> Vec { + self.candidates + } +} + +impl FiniteSrgb8DomainV1 for DomainRequestV1 { + type Packing = VariablePackingV1; + + fn raw_opaque_utf8_bytes(&self) -> Result { + let mut bytes = 0_u64; + for candidate in &self.candidates { + let length = u64::try_from(candidate.candidate_id.as_str().len()) + .map_err(|_| InvalidRequestV1::ArithmeticOverflow)?; + bytes = bytes + .checked_add(length) + .ok_or(InvalidRequestV1::ArithmeticOverflow)?; + } + Ok(bytes) + } + + fn canonicalize(&mut self) -> Result<(), InvalidRequestV1> { + self.candidates.sort_unstable_by(|left, right| { + left.candidate_id + .as_str() + .as_bytes() + .cmp(right.candidate_id.as_str().as_bytes()) + }); + if let Some(pair) = self + .candidates + .windows(2) + .find(|pair| pair[0].candidate_id == pair[1].candidate_id) + { + return Err(InvalidRequestV1::DuplicateCandidateId { + candidate_id: pair[0].candidate_id.clone(), + }); + } + Ok(()) + } + + fn candidates(&self) -> impl ExactSizeIterator + '_ { + self.candidates.iter().map(CandidateV1::emitted) + } +} + +/// Core-owned versioned kind of declared finite domain. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum DomainKindV1 { + /// Canonical set of opaque IDs bound to exact final sRGB8 values. + ExplicitSrgb8Set, +} + +impl DomainKindV1 { + /// Stable semantic key. + pub const fn key(self) -> &'static str { + match self { + Self::ExplicitSrgb8Set => "explicit-srgb8-set-v1", + } + } +} + +/// Owned bounded-compilation request for one explicit finite domain. +#[derive(Debug)] +pub struct RequestV1 { + domain: DomainRequestV1, + relations: Vec, + resource_profile_id: ResourceProfileIdV1, +} + +impl RequestV1 { + /// Construct a locally well-formed request. Aggregate resource bounds and + /// duplicate candidate IDs are checked by [`evaluate`] before any pair is + /// evaluated. + pub fn try_new( + domain: DomainRequestV1, + relations: Vec, + resource_profile_id: ResourceProfileIdV1, + ) -> Result { + if relations.is_empty() { + return Err(InvalidRequestV1::EmptyRelations); + } + Ok(Self { + domain, + relations, + resource_profile_id, + }) + } +} + +/// Domain-neutral evidence descriptor: kind, canonical content and exact +/// finite cardinality. It deliberately has no neutral-only first/last fields. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct DomainDescriptorV1 { + kind: DomainKindV1, + digest: DomainDigestV1, + candidate_count: u64, +} + +impl DomainDescriptorV1 { + /// Versioned domain kind. + pub const fn kind(&self) -> DomainKindV1 { + self.kind + } + + /// Canonical `(candidate ID, emitted sRGB8)` content digest. + pub const fn digest(&self) -> DomainDigestV1 { + self.digest + } + + /// Exact number of exhaustively evaluated declared candidates. + pub const fn candidate_count(&self) -> u64 { + self.candidate_count + } +} + +fn domain_digest(domain: &DomainRequestV1, candidate_count: u64) -> DomainDigestV1 { + let mut hasher = Hasher::new(); + hasher.update(DOMAIN_SEPARATOR); + hash_len_prefixed(&mut hasher, DomainKindV1::ExplicitSrgb8Set.key().as_bytes()); + hash_u64(&mut hasher, candidate_count); + for candidate in &domain.candidates { + hash_len_prefixed(&mut hasher, candidate.candidate_id.as_str().as_bytes()); + hasher.update(&candidate.emitted.bytes()); + } + DomainDigestV1(*hasher.finalize().as_bytes()) +} + +fn evaluation_id( + domain_digest: DomainDigestV1, + relation_digest: RelationSetDigestV1, + binding: &AtomicEvidenceBindingV1, + layout: WorkLayoutV1, + matrix_digest: &[u8; 32], + partition: &[u8], +) -> EvaluationIdV1 { + let mut hasher = Hasher::new(); + hasher.update(EVALUATION_SEPARATOR); + hasher.update(domain_digest.as_bytes()); + hasher.update(relation_digest.as_bytes()); + hash_len_prefixed(&mut hasher, binding.profile_id.key().as_bytes()); + hash_len_prefixed(&mut hasher, binding.artifact_id.key().as_bytes()); + hash_len_prefixed(&mut hasher, binding.bound_id.key().as_bytes()); + hash_len_prefixed(&mut hasher, binding.proof_id.key().as_bytes()); + hasher.update(&binding.proof_sha256); + for value in [ + layout.canonical_relations, + layout.applicable_relations, + layout.not_evaluated_relations, + layout.applicable_edges, + layout.candidate_count, + layout.logical_assessments, + layout.failure_matrix_bytes, + layout.partition_bytes, + layout.packed_result_bytes, + ] { + hash_u64(&mut hasher, value); + } + hasher.update(matrix_digest); + hash_len_prefixed(&mut hasher, partition); + EvaluationIdV1(*hasher.finalize().as_bytes()) +} + +#[derive(Debug)] +struct EvaluationProofDataV1 { + resource_profile_id: ResourceProfileIdV1, + canonical_relations: u64, + applicable_relations: u64, + not_applicable_relations: u64, + applicable_edges: u64, + logical_assessments: u64, + matrix_digest: [u8; 32], + atomic_evidence: AtomicEvidenceBindingV1, +} + +/// Borrowed sealed view of one complete explicit-domain proof. +#[derive(Debug, Clone, Copy)] +pub struct EvaluationProofV1<'a> { + record: &'a EvaluatedV1, +} + +impl EvaluationProofV1<'_> { + /// Semantic result identity; resource policy is excluded from it. + pub const fn evaluation_id(&self) -> EvaluationIdV1 { + self.record.evaluation_id + } + + /// Operational policy that admitted this computation. + pub const fn resource_profile_id(&self) -> ResourceProfileIdV1 { + self.record.proof.resource_profile_id + } + + /// Domain-neutral explicit-set descriptor. + pub const fn domain(&self) -> &DomainDescriptorV1 { + &self.record.domain + } + + /// Canonical declared-relation digest. + pub const fn relation_set_digest(&self) -> RelationSetDigestV1 { + self.record.relation_set_digest + } + + /// Exact canonical relation count. + pub const fn canonical_relations(&self) -> u64 { + self.record.proof.canonical_relations + } + + /// Exact applicable relation count. + pub const fn applicable_relations(&self) -> u64 { + self.record.proof.applicable_relations + } + + /// Exact client-declared NotApplicable relation count. + pub const fn not_applicable_relations(&self) -> u64 { + self.record.proof.not_applicable_relations + } + + /// Exact flattened canonical edge count. + pub const fn applicable_edges(&self) -> u64 { + self.record.proof.applicable_edges + } + + /// Exact number of evaluated candidate-edge cells. + pub const fn logical_assessments(&self) -> u64 { + self.record.proof.logical_assessments + } + + /// SHA-256 of the exact packed candidate-major failure matrix. + pub const fn matrix_digest(&self) -> &[u8; 32] { + &self.record.proof.matrix_digest + } + + /// Exact variable-width feasible-candidate partition, LSB0 by canonical + /// candidate index. + pub fn partition(&self) -> &[u8] { + self.record.partition() + } + + /// Exact WCAG evaluator profile bound into every atomic assessment. + pub const fn profile_id(&self) -> Wcag22ProfileIdV1 { + self.record.proof.atomic_evidence.profile_id + } + + /// Exact finite numerical artifact used by the atomic evaluator. + pub const fn artifact_id(&self) -> NumericalArtifactIdV2 { + self.record.proof.atomic_evidence.artifact_id + } + + /// Exact numerical error-bound law used by the atomic evaluator. + pub const fn bound_id(&self) -> NumericalErrorBoundIdV2 { + self.record.proof.atomic_evidence.bound_id + } + + /// Exact complete-domain numerical proof used by the atomic evaluator. + pub const fn proof_id(&self) -> NumericalProofIdV2 { + self.record.proof.atomic_evidence.proof_id + } + + /// SHA-256 of the exact #284 proof-file bytes. + pub const fn proof_sha256(&self) -> &[u8; 32] { + &self.record.proof.atomic_evidence.proof_sha256 + } +} + +/// Zero-allocation view of one explicit candidate-major cell. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct AssessmentV1<'a> { + candidate: &'a CandidateV1, + relation_id: &'a RelationId, + adjacent: Srgb8, + decision: Wcag22ApplicableDecisionV1, +} + +impl<'a> AssessmentV1<'a> { + /// Opaque canonical candidate identity. + pub const fn candidate_id(self) -> &'a CandidateId { + &self.candidate.candidate_id + } + + /// Exact final physical bytes. + pub const fn emitted(self) -> Srgb8 { + self.candidate.emitted + } + + /// Opaque canonical relation identity. + pub const fn relation_id(self) -> &'a RelationId { + self.relation_id + } + + /// Exact declared adjacent colour. + pub const fn adjacent(self) -> Srgb8 { + self.adjacent + } + + /// Atomic #284 decision. + pub const fn decision(self) -> Wcag22ApplicableDecisionV1 { + self.decision + } +} + +struct AssessmentIter<'a> { + candidates: &'a [CandidateV1], + cursor: AssessmentCursorV1<'a>, +} + +impl<'a> Iterator for AssessmentIter<'a> { + type Item = AssessmentV1<'a>; + + fn next(&mut self) -> Option { + let AssessmentCellV1 { + candidate_index, + relation, + adjacent, + decision, + } = self.cursor.next()?; + let candidate = usize::try_from(candidate_index) + .ok() + .and_then(|index| self.candidates.get(index)) + .expect("sealed explicit candidate cardinality must match the assessment cursor"); + Some(AssessmentV1 { + candidate, + relation_id: &relation.relation_id, + adjacent, + decision, + }) + } + + fn size_hint(&self) -> (usize, Option) { + self.cursor.size_hint() + } +} + +impl ExactSizeIterator for AssessmentIter<'_> {} + +/// Complete evaluated explicit-domain terminal. It has no public constructor. +#[derive(Debug)] +pub struct EvaluatedV1 { + candidates: Vec, + relations: Vec, + layout: WorkLayoutV1, + packed: Vec, + domain: DomainDescriptorV1, + relation_set_digest: RelationSetDigestV1, + evaluation_id: EvaluationIdV1, + proof: EvaluationProofDataV1, +} + +impl EvaluatedV1 { + /// Exact candidate-major packed failure matrix. For candidate `c` and edge + /// `e`, bit index is `cE + e`; one means Fail. + pub fn failure_matrix(&self) -> &[u8] { + let length = self.layout.failure_matrix_bytes as usize; + &self.packed[..length] + } + + fn partition(&self) -> &[u8] { + let length = self.layout.failure_matrix_bytes as usize; + &self.packed[length..] + } + + /// Canonical explicit domain descriptor. + pub const fn domain(&self) -> &DomainDescriptorV1 { + &self.domain + } + + /// Canonical explicit domain digest. + pub const fn domain_digest(&self) -> DomainDigestV1 { + self.domain.digest + } + + /// Canonical relation-set digest. + pub const fn relation_set_digest(&self) -> RelationSetDigestV1 { + self.relation_set_digest + } + + /// Semantic evaluated-result identity. + pub const fn evaluation_id(&self) -> EvaluationIdV1 { + self.evaluation_id + } + + /// Borrow the sealed complete-enumeration proof without copying its + /// variable partition. + pub const fn proof(&self) -> EvaluationProofV1<'_> { + EvaluationProofV1 { record: self } + } + + /// Canonical candidates in exact candidate-ID byte order. + pub fn candidates(&self) -> &[CandidateV1] { + &self.candidates + } + + /// Canonical declared graph retained exactly once by the result. + pub fn relations(&self) -> &[RelationV1] { + &self.relations + } + + /// Every feasible candidate in canonical candidate-ID byte order. + pub fn feasible_candidates(&self) -> impl Iterator { + (0_u64..) + .zip(self.candidates.iter()) + .filter(move |(index, _)| packed_bit(self.partition(), *index)) + .map(|(_, candidate)| candidate) + } + + /// Every infeasible candidate in canonical candidate-ID byte order. + pub fn infeasible_candidates(&self) -> impl Iterator { + (0_u64..) + .zip(self.candidates.iter()) + .filter(move |(index, _)| !packed_bit(self.partition(), *index)) + .map(|(_, candidate)| candidate) + } + + /// Full candidate-major `C×E` matrix without per-cell allocation. + pub fn assessments(&self) -> impl ExactSizeIterator> + '_ { + AssessmentIter { + candidates: &self.candidates, + cursor: AssessmentCursorV1 { + relations: &self.relations, + matrix: self.failure_matrix(), + candidate_index: 0, + candidate_count: self.layout.candidate_count, + relation_index: 0, + adjacent_index: 0, + logical_index: 0, + remaining: self.layout.logical_assessments as usize, + }, + } + } +} + +/// Canonical declaration-only explicit-domain terminal. +#[derive(Debug)] +pub struct NotEvaluatedV1 { + candidates: Vec, + relations: Vec, + resource_profile_id: ResourceProfileIdV1, + domain: DomainDescriptorV1, + relation_set_digest: RelationSetDigestV1, +} + +impl NotEvaluatedV1 { + /// Canonical explicit domain descriptor. + pub const fn domain(&self) -> &DomainDescriptorV1 { + &self.domain + } + + /// Canonical explicit candidates. + pub fn candidates(&self) -> &[CandidateV1] { + &self.candidates + } + + /// Canonical relation-set digest. + pub const fn relation_set_digest(&self) -> RelationSetDigestV1 { + self.relation_set_digest + } + + /// Operational policy that admitted canonicalization. + pub const fn resource_profile_id(&self) -> ResourceProfileIdV1 { + self.resource_profile_id + } + + /// Canonical declaration-only graph. + pub fn relations(&self) -> &[RelationV1] { + &self.relations + } +} + +/// Exhaustive explicit-domain terminal algebra. +#[derive(Debug)] +#[non_exhaustive] +pub enum FeasibilityV1 { + /// Complete evaluation with a non-empty feasible partition. + #[non_exhaustive] + Feasible(EvaluatedV1), + /// Complete evaluation with an empty feasible partition. + #[non_exhaustive] + Infeasible(EvaluatedV1), + /// Canonical request contained no applicable relation. + #[non_exhaustive] + NotEvaluated(NotEvaluatedV1), +} + +impl FeasibilityV1 { + /// Whether complete evaluation found at least one feasible candidate. + pub const fn is_feasible(&self) -> bool { + matches!(self, Self::Feasible(..)) + } + + /// Whether complete evaluation found no feasible candidate. + pub const fn is_infeasible(&self) -> bool { + matches!(self, Self::Infeasible(..)) + } + + /// Whether no relation was applicable and therefore no pair was evaluated. + pub const fn is_not_evaluated(&self) -> bool { + matches!(self, Self::NotEvaluated(..)) + } + + /// Borrow the complete evaluated record from either evaluated terminal. + pub const fn evaluated(&self) -> Option<&EvaluatedV1> { + match self { + Self::Feasible(value) | Self::Infeasible(value) => Some(value), + Self::NotEvaluated(..) => None, + } + } + + /// Borrow the declaration-only record when no relation was applicable. + pub const fn not_evaluated(&self) -> Option<&NotEvaluatedV1> { + match self { + Self::NotEvaluated(value) => Some(value), + Self::Feasible(..) | Self::Infeasible(..) => None, + } + } +} + +/// Canonicalize and exhaustively evaluate one client-declared finite set. +pub fn evaluate(request: RequestV1) -> Result { + let mut evaluator = AtomicPairEvaluator::new(); + let mut storage = PackedDecisionStorage::default(); + let request = KernelRequestV1 { + domain: request.domain, + relations: request.relations, + resource_profile_id: request.resource_profile_id, + }; + match evaluate_domain_with(request, &mut evaluator, &mut storage)? { + KernelResultV1::NotEvaluated(result) => { + let candidate_count = result.layout.candidate_count; + let digest = domain_digest(&result.domain, candidate_count); + let domain = DomainDescriptorV1 { + kind: DomainKindV1::ExplicitSrgb8Set, + digest, + candidate_count, + }; + let relation_set_digest = relation_set_digest(&result.relations); + Ok(FeasibilityV1::NotEvaluated(NotEvaluatedV1 { + candidates: result.domain.into_candidates(), + relations: result.relations, + resource_profile_id: result.resource_profile_id, + domain, + relation_set_digest, + })) + } + KernelResultV1::Evaluated(result) => { + let sealed = seal_evaluated_v1(&result, storage.into_bytes())?; + let digest = domain_digest(&result.domain, result.observed_candidates); + let domain = DomainDescriptorV1 { + kind: DomainKindV1::ExplicitSrgb8Set, + digest, + candidate_count: result.observed_candidates, + }; + let relation_set_digest = relation_set_digest(&result.relations); + let evaluation_id = evaluation_id( + digest, + relation_set_digest, + &result.atomic_evidence, + result.layout, + &sealed.matrix_digest, + sealed.partition(), + ); + let proof = EvaluationProofDataV1 { + resource_profile_id: result.resource_profile_id, + canonical_relations: result.layout.canonical_relations, + applicable_relations: result.layout.applicable_relations, + not_applicable_relations: result.layout.not_evaluated_relations, + applicable_edges: result.layout.applicable_edges, + logical_assessments: result.observed_assessments, + matrix_digest: sealed.matrix_digest, + atomic_evidence: result.atomic_evidence, + }; + let passing_candidates = result.passing_candidates; + let evaluated = EvaluatedV1 { + candidates: result.domain.into_candidates(), + relations: result.relations, + layout: result.layout, + packed: sealed.packed, + domain, + relation_set_digest, + evaluation_id, + proof, + }; + if passing_candidates == 0 { + Ok(FeasibilityV1::Infeasible(evaluated)) + } else { + Ok(FeasibilityV1::Feasible(evaluated)) + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::wcag22::Wcag22CriterionV1; + use crate::wcag22_feasibility::OccurrenceId; + + #[test] + #[should_panic(expected = "sealed explicit candidate cardinality")] + fn assessment_iterator_cannot_hide_a_broken_sealed_cardinality() { + let candidates = [CandidateV1::new( + CandidateId::try_new("only-candidate").unwrap(), + Srgb8::new([0; 3]), + )]; + let relations = [RelationV1::applicable( + RelationId::try_new("relation").unwrap(), + OccurrenceId::try_new("occurrence").unwrap(), + Wcag22CriterionV1::Sc143TextDefault, + vec![Srgb8::new([255; 3])], + ) + .unwrap()]; + let matrix = [0_u8]; + let mut iterator = AssessmentIter { + candidates: &candidates, + cursor: AssessmentCursorV1 { + relations: &relations, + matrix: &matrix, + candidate_index: 0, + candidate_count: 2, + relation_index: 0, + adjacent_index: 0, + logical_index: 0, + remaining: 2, + }, + }; + + assert!(iterator.next().is_some()); + let _ = iterator.next(); + } +} diff --git a/crates/labcolors-core/src/wcag22_feasibility_tests.rs b/crates/labcolors-core/src/wcag22_feasibility_tests.rs index f30c459e..835d90be 100644 --- a/crates/labcolors-core/src/wcag22_feasibility_tests.rs +++ b/crates/labcolors-core/src/wcag22_feasibility_tests.rs @@ -10,10 +10,12 @@ //! is the exact evaluator-call and assessment cardinality. `B = 0` //! when `A = 0`; only an evaluated result (`A > 0`) owns the 32-byte //! candidate partition and therefore has `B = 32(E + 1)`. -//! - `evaluate_with(request, evaluator, storage)` owns canonicalization and the -//! exact `256 * E` traversal while `PairEvaluator` and `DecisionStorage` are -//! replaceable private dependencies. The production wrapper supplies the -//! sealed #284 adapter and packed storage. +//! - `evaluate_domain_with(request, evaluator, storage)` owns canonicalization +//! and the exact `C * E` traversal for any finite sRGB8 domain; +//! `evaluate_with` is its byte-identical 256-neutral compatibility wrapper. +//! `PairEvaluator` and `DecisionStorage` are replaceable private +//! dependencies. The production wrapper supplies the sealed #284 adapter and +//! packed storage. //! //! The expected private records used below are deliberately plain structs. //! `RawInputCountsV1`, `CanonicalCountsV1`, `ResourceLimitsV1` and @@ -310,6 +312,245 @@ fn packed_storage_is_exactly_32_times_e_plus_one() { } } +#[test] +fn variable_domain_layout_is_contiguous_ceil_bit_arithmetic() { + for (candidates, edges, matrix_bytes, partition_bytes) in [ + (1, 1, 1, 1), + (1, 8, 1, 1), + (3, 3, 2, 1), + (7, 9, 8, 1), + (8, 9, 9, 1), + (9, 9, 11, 2), + (17, 7, 15, 3), + ] { + let layout = checked_layout_for_domain_v1( + PROFILE, + candidates, + RawInputCountsV1 { + raw_relations: 1, + raw_adjacent_entries: edges, + opaque_utf8_bytes: candidates, + }, + CanonicalCountsV1 { + canonical_relations: 1, + applicable_relations: 1, + not_evaluated_relations: 0, + applicable_edges: edges, + }, + unlimited(), + u64::MAX, + ) + .unwrap(); + assert_eq!(layout.logical_assessments, candidates * edges); + assert_eq!(layout.failure_matrix_bytes, matrix_bytes); + assert_eq!(layout.partition_bytes, partition_bytes); + assert_eq!(layout.packed_result_bytes, matrix_bytes + partition_bytes); + } + + assert!(matches!( + checked_logical_assessments_for_domain_v1(u64::MAX, 2), + Err(InvalidRequestV1::ArithmeticOverflow) + )); + assert!(matches!( + checked_packed_result_bytes_for_domain_v1(u64::MAX, 1, 2), + Err(InvalidRequestV1::ArithmeticOverflow) + )); + + let zero_domain = checked_layout_for_domain_v1( + PROFILE, + 0, + RawInputCountsV1 { + raw_relations: 1, + raw_adjacent_entries: 1, + opaque_utf8_bytes: 0, + }, + CanonicalCountsV1 { + canonical_relations: 1, + applicable_relations: 1, + not_evaluated_relations: 0, + applicable_edges: 1, + }, + unlimited(), + u64::MAX, + ) + .expect_err("a parsed finite domain cannot be empty"); + assert!(matches!( + zero_domain, + ErrorV1::CompilerInvariantViolation(CompilerInvariantV1::LayoutMismatch) + )); +} + +#[cfg(feature = "wcag22-explicit-feasibility")] +fn explicit_domain(count: u16) -> explicit::DomainRequestV1 { + explicit::DomainRequestV1::try_new( + (0..count) + .map(|index| { + explicit::CandidateV1::new( + explicit::CandidateId::try_new(format!("candidate/{index:03}")).unwrap(), + Srgb8::new([index as u8; 3]), + ) + }) + .collect(), + ) + .unwrap() +} + +#[test] +#[cfg(feature = "wcag22-explicit-feasibility")] +fn explicit_kernel_executes_exact_c_times_e_and_reserves_one_exact_buffer() { + let request = KernelRequestV1 { + domain: explicit_domain(3), + relations: vec![applicable_relation( + "three-edges", + vec![grey(0), grey(118), grey(255)], + Wcag22CriterionV1::Sc143TextDefault, + )], + resource_profile_id: PROFILE, + }; + let (mut evaluator, calls) = ProbeEvaluator::new(EvaluatorMode::AllPass); + let mut storage = ProbeStorage::default(); + let result = evaluate_domain_with(request, &mut evaluator, &mut storage).unwrap(); + let result = match result { + KernelResultV1::Evaluated(value) => value, + KernelResultV1::NotEvaluated(_) => panic!("three applicable edges must be evaluated"), + }; + + assert_eq!(result.layout.candidate_count, 3); + assert_eq!(result.layout.logical_assessments, 9); + assert_eq!(result.layout.failure_matrix_bytes, 2); + assert_eq!(result.layout.partition_bytes, 1); + assert_eq!(result.layout.packed_result_bytes, 3); + assert_eq!(calls.get(), 9); + assert_eq!(storage.reserve_calls, 1); + assert_eq!(storage.reserved_bytes, Some(3)); + assert_eq!(storage.writes, 9); + assert_eq!(storage.partition_writes, 3); + assert_eq!( + storage.finish_calls, 0, + "variable packing writes its partition in-place and has no finalization step", + ); +} + +#[test] +#[cfg(feature = "wcag22-explicit-feasibility")] +fn explicit_allocation_failure_precedes_the_first_atomic_call() { + let request = KernelRequestV1 { + domain: explicit_domain(3), + relations: vec![applicable_relation( + "allocation", + vec![grey(255)], + Wcag22CriterionV1::Sc143TextDefault, + )], + resource_profile_id: PROFILE, + }; + let (mut evaluator, calls) = ProbeEvaluator::new(EvaluatorMode::AllPass); + let mut storage = ProbeStorage::allocation_failure(); + let error = evaluate_domain_with(request, &mut evaluator, &mut storage).unwrap_err(); + + assert!(matches!( + error, + ErrorV1::AllocationFailed { + profile_id: PROFILE, + requested_bytes: 2, + } + )); + assert_eq!(storage.reserve_calls, 1); + assert_eq!(storage.reserved_bytes, Some(2)); + assert_eq!(calls.get(), 0); + assert_eq!(storage.writes, 0); + assert_eq!(storage.partition_writes, 0); + assert_eq!(storage.finish_calls, 0); +} + +#[test] +#[cfg(feature = "wcag22-explicit-feasibility")] +fn explicit_kernel_reuses_the_single_atomic_invariant_error_algebra() { + let request = KernelRequestV1 { + domain: explicit_domain(1), + relations: vec![applicable_relation( + "shared-error", + vec![grey(255)], + Wcag22CriterionV1::Sc143TextDefault, + )], + resource_profile_id: PROFILE, + }; + let (mut evaluator, calls) = ProbeEvaluator::new(EvaluatorMode::ForegroundMismatch); + let mut storage = ProbeStorage::default(); + let error = evaluate_domain_with(request, &mut evaluator, &mut storage).unwrap_err(); + + assert!(matches!( + error, + ErrorV1::EvaluatorInvariantViolation { + candidate, + adjacent, + violation: EvaluatorInvariantV1::InputMismatch, + .. + } if candidate == Srgb8::new([0; 3]) && adjacent == grey(255) + )); + assert_eq!(calls.get(), 1); + assert_eq!(storage.writes, 0); + assert_eq!(storage.finish_calls, 0); +} + +#[test] +#[cfg(feature = "wcag22-explicit-feasibility")] +fn explicit_compile_profile_maximum_completes_and_plus_one_fails_before_evaluation() { + let edges = PROFILE.limit(ResourceDimensionV1::ApplicableEdges); + let work_limit = PROFILE.limit(ResourceDimensionV1::LogicalAssessments); + let packed_limit = PROFILE.limit(ResourceDimensionV1::PackedResultBytes); + let candidates = work_limit / edges; + assert_eq!((candidates, edges), (256, 2_047)); + assert_eq!(work_limit % edges, 0); + + let adjacent = (0..edges) + .map(|code| Srgb8::new([0, (code >> 8) as u8, code as u8])) + .collect::>(); + let relation = || { + applicable_relation( + "maximum-envelope", + adjacent.clone(), + Wcag22CriterionV1::Sc143TextDefault, + ) + }; + + let request = KernelRequestV1 { + domain: explicit_domain(candidates as u16), + relations: vec![relation()], + resource_profile_id: PROFILE, + }; + let (mut evaluator, calls) = ProbeEvaluator::new(EvaluatorMode::AllPass); + let mut storage = ProbeStorage::default(); + let result = evaluate_domain_with(request, &mut evaluator, &mut storage).unwrap(); + let result = match result { + KernelResultV1::Evaluated(value) => value, + KernelResultV1::NotEvaluated(_) => panic!("the maximum applicable shape must evaluate"), + }; + assert_eq!(result.layout.logical_assessments, work_limit); + assert_eq!(result.layout.packed_result_bytes, packed_limit); + assert_eq!(storage.reserved_bytes, Some(packed_limit)); + assert_eq!(calls.get(), work_limit); + + let oversized = KernelRequestV1 { + domain: explicit_domain((candidates + 1) as u16), + relations: vec![relation()], + resource_profile_id: PROFILE, + }; + let (mut evaluator, calls) = ProbeEvaluator::new(EvaluatorMode::AllPass); + let mut storage = ProbeStorage::default(); + let error = evaluate_domain_with(oversized, &mut evaluator, &mut storage).unwrap_err(); + assert!(matches!( + error, + ErrorV1::ResourceLimitExceeded { + profile_id: PROFILE, + dimension: ResourceDimensionV1::LogicalAssessments, + requested, + limit, + } if requested == (candidates + 1) * edges && limit == work_limit + )); + assert_eq!(calls.get(), 0); + assert_eq!(storage.reserve_calls, 0); +} + #[test] fn all_not_applicable_has_zero_work_and_zero_packed_result_bytes() { let layout = checked_layout_v1( @@ -389,6 +630,7 @@ enum EvaluatorMode { struct ProbeEvaluator { mode: EvaluatorMode, calls: Rc>, + evidence: AtomicEvidenceBindingV1, } impl ProbeEvaluator { @@ -398,6 +640,8 @@ impl ProbeEvaluator { Self { mode, calls: Rc::clone(&calls), + evidence: expected_atomic_evidence_binding_v1() + .expect("the committed #284 evidence binding is valid"), }, calls, ) @@ -427,8 +671,7 @@ impl PairEvaluator for ProbeEvaluator { let mut foreground = candidate.bytes(); let mut background = adjacent.bytes(); let mut actual_criterion = criterion; - let mut evidence = expected_atomic_evidence_binding_v1() - .expect("the committed #284 evidence binding is valid"); + let mut evidence = self.evidence.clone(); match &self.mode { EvaluatorMode::AllPass | EvaluatorMode::SourceError(_) => {} EvaluatorMode::AllFail => {} @@ -470,6 +713,8 @@ struct ProbeStorage { reserve_calls: u64, reserved_bytes: Option, writes: u64, + #[cfg(feature = "wcag22-explicit-feasibility")] + partition_writes: u64, finish_calls: u64, } @@ -513,7 +758,17 @@ impl DecisionStorage for ProbeStorage { if self.fail_write { Err(()) } else { Ok(()) } } - fn finish(&mut self, _passing_partition: [u8; 32]) -> Result<(), ()> { + #[cfg(feature = "wcag22-explicit-feasibility")] + fn write_feasible_candidate( + &mut self, + _matrix_bytes: u64, + _candidate_index: u64, + ) -> Result<(), ()> { + self.partition_writes += 1; + Ok(()) + } + + fn finish(&mut self, _partition: &[u8]) -> Result<(), ()> { self.finish_calls += 1; if self.fail_finish { Err(()) } else { Ok(()) } } @@ -839,32 +1094,43 @@ fn matrix_partition_and_proof_invariants_reject_single_field_mutations() { passing_candidates: CANDIDATES, failing_candidates: 0, }; - validate_complete_result_v1(layout, &all_pass_matrix, &all_pass_partition, proof) + validate_neutral_complete_result_v1(layout, &all_pass_matrix, &all_pass_partition, proof) .expect("the exact all-pass matrix, partition and counts agree"); assert!( - validate_complete_result_v1(layout, &all_pass_matrix[..31], &all_pass_partition, proof,) - .is_err(), + validate_neutral_complete_result_v1( + layout, + &all_pass_matrix[..31], + &all_pass_partition, + proof, + ) + .is_err(), "matrix length is part of the proof" ); let mut one_failed_cell = all_pass_matrix; one_failed_cell[0] ^= 1; assert!( - validate_complete_result_v1(layout, &one_failed_cell, &all_pass_partition, proof).is_err(), + validate_neutral_complete_result_v1(layout, &one_failed_cell, &all_pass_partition, proof,) + .is_err(), "the candidate partition must be derived from every matrix cell" ); let mut one_missing_candidate = all_pass_partition; one_missing_candidate[0] ^= 1; assert!( - validate_complete_result_v1(layout, &all_pass_matrix, &one_missing_candidate, proof) - .is_err(), + validate_neutral_complete_result_v1( + layout, + &all_pass_matrix, + &one_missing_candidate, + proof, + ) + .is_err(), "partition bytes and proof counts cannot diverge" ); assert!( - validate_complete_result_v1( + validate_neutral_complete_result_v1( layout, &all_pass_matrix, &all_pass_partition, @@ -877,7 +1143,7 @@ fn matrix_partition_and_proof_invariants_reject_single_field_mutations() { "the proof must attest the exact W cells" ); assert!( - validate_complete_result_v1( + validate_neutral_complete_result_v1( layout, &all_pass_matrix, &all_pass_partition, @@ -892,6 +1158,57 @@ fn matrix_partition_and_proof_invariants_reject_single_field_mutations() { ); } +#[test] +#[cfg(feature = "wcag22-explicit-feasibility")] +fn variable_matrix_and_partition_tail_bits_are_part_of_the_proof() { + let layout = checked_layout_for_domain_v1( + PROFILE, + 3, + RawInputCountsV1 { + raw_relations: 1, + raw_adjacent_entries: 3, + opaque_utf8_bytes: 3, + }, + CanonicalCountsV1 { + canonical_relations: 1, + applicable_relations: 1, + not_evaluated_relations: 0, + applicable_edges: 3, + }, + unlimited(), + u64::MAX, + ) + .unwrap(); + let matrix = [0_u8; 2]; + let partition = [0b0000_0111]; + let counters = EvaluationProofCountersV1 { + logical_assessments: 9, + passing_candidates: 3, + failing_candidates: 0, + }; + validate_variable_complete_result_v1(layout, &matrix, &partition, counters).unwrap(); + + let mut matrix_tail = matrix; + matrix_tail[1] |= 1 << 1; + assert!( + validate_variable_complete_result_v1(layout, &matrix_tail, &partition, counters).is_err(), + "unused matrix bits cannot carry covert state" + ); + + let partition_tail = [partition[0] | (1 << 3)]; + assert!( + validate_variable_complete_result_v1(layout, &matrix, &partition_tail, counters).is_err(), + "unused partition bits cannot carry covert state" + ); + + let mut used_cell = matrix; + used_cell[0] |= 1; + assert!( + validate_variable_complete_result_v1(layout, &used_cell, &partition, counters).is_err(), + "a row decision and its feasible bit must agree" + ); +} + fn hex(bytes: &[u8]) -> String { const HEX: &[u8; 16] = b"0123456789abcdef"; let mut output = String::with_capacity(bytes.len() * 2); diff --git a/crates/labcolors-core/tests/wcag22_explicit_feasibility.rs b/crates/labcolors-core/tests/wcag22_explicit_feasibility.rs new file mode 100644 index 00000000..01646ad5 --- /dev/null +++ b/crates/labcolors-core/tests/wcag22_explicit_feasibility.rs @@ -0,0 +1,540 @@ +//! Public RED contract for client-declared finite sRGB8 feasibility (#296-A). +//! +//! This target intentionally uses only the public Core API. The explicit set +//! owns opaque candidate identities; WCAG mathematics remains the existing +//! proof-bound atomic evaluator. + +use std::fs; +use std::process::Command; + +use labcolors_core::Srgb8; +use labcolors_core::wcag22::{Wcag22ClientDeclaredNotApplicableV1, Wcag22CriterionV1}; +use labcolors_core::wcag22_feasibility::explicit::{ + CandidateId, CandidateV1, DomainKindV1, DomainRequestV1, EvaluatedV1, FeasibilityV1, RequestV1, + evaluate, +}; +use labcolors_core::wcag22_feasibility::{ + DomainIdV1, ErrorV1, InvalidRequestV1, OccurrenceId, RelationId, RelationV1, + RequestV1 as NeutralRequestV1, ResourceProfileIdV1, evaluate as evaluate_neutral, +}; +use proptest::prelude::*; +use proptest::test_runner::{Config, RngAlgorithm, TestRng, TestRunner}; + +#[path = "../src/sha256.rs"] +#[allow(dead_code)] +mod fixture_sha256; + +const PROFILE: ResourceProfileIdV1 = ResourceProfileIdV1::Compile; +const IDENTITY_FIXTURE: &str = + include_str!("../contracts/wcag22-explicit-feasibility-identity-v1.json"); + +fn candidate(id: &str, emitted: [u8; 3]) -> CandidateV1 { + CandidateV1::new( + CandidateId::try_new(id).expect("test candidate ID is non-empty"), + Srgb8::new(emitted), + ) +} + +fn relation_id(value: &str) -> RelationId { + RelationId::try_new(value).expect("test relation ID is non-empty") +} + +fn occurrence_id(value: &str) -> OccurrenceId { + OccurrenceId::try_new(value).expect("test occurrence ID is non-empty") +} + +fn applicable(adjacent: Vec) -> RelationV1 { + RelationV1::applicable( + relation_id("contrast"), + occurrence_id("occurrence"), + Wcag22CriterionV1::Sc143TextDefault, + adjacent, + ) + .expect("test relation has adjacency") +} + +fn request(candidates: Vec, relations: Vec) -> RequestV1 { + let domain = DomainRequestV1::try_new(candidates).expect("test domain is non-empty"); + RequestV1::try_new(domain, relations, PROFILE).expect("test request has relations") +} + +fn evaluated(result: &FeasibilityV1) -> &EvaluatedV1 { + result.evaluated().expect("expected an evaluated terminal") +} + +fn hex(bytes: &[u8]) -> String { + const DIGITS: &[u8; 16] = b"0123456789abcdef"; + let mut output = String::with_capacity(bytes.len() * 2); + for byte in bytes { + output.push(char::from(DIGITS[usize::from(byte >> 4)])); + output.push(char::from(DIGITS[usize::from(byte & 0x0f)])); + } + output +} + +fn check_property(strategy: S, body: impl Fn(S::Value) -> Result<(), TestCaseError>) +where + S::Value: std::fmt::Debug, +{ + let mut runner = TestRunner::new_with_rng( + Config { + cases: 64, + failure_persistence: None, + ..Config::default() + }, + TestRng::deterministic_rng(RngAlgorithm::ChaCha), + ); + runner + .run(&strategy, body) + .expect("explicit feasibility property failed with a minimized counterexample"); +} + +fn assert_downstream_rejected(source: &str, expected_fragments: &[&str]) { + let temp = tempfile::tempdir().unwrap(); + fs::create_dir(temp.path().join("src")).unwrap(); + let package_dir = env!("CARGO_MANIFEST_DIR") + .replace('\\', "\\\\") + .replace('"', "\\\""); + fs::write( + temp.path().join("Cargo.toml"), + format!( + "[package]\nname = \"forge-explicit-feasibility\"\nversion = \"0.0.0\"\n\ + edition = \"2024\"\n\n[dependencies]\n\ + labcolors-core = {{ path = \"{package_dir}\" }}\n" + ), + ) + .unwrap(); + fs::write(temp.path().join("src/main.rs"), source).unwrap(); + + let output = Command::new(env!("CARGO")) + .arg("check") + .arg("--offline") + .env("CARGO_TARGET_DIR", temp.path().join("target")) + .current_dir(temp.path()) + .output() + .unwrap(); + assert!( + !output.status.success(), + "forged explicit proof unexpectedly compiled" + ); + let stderr = String::from_utf8_lossy(&output.stderr); + for fragment in expected_fragments { + assert!( + stderr.contains(fragment), + "expected downstream rejection mentioning {fragment:?}, stderr:\n{stderr}" + ); + } +} + +#[test] +fn empty_candidate_shapes_are_rejected_before_compilation() { + assert!(matches!( + CandidateId::try_new(""), + Err(InvalidRequestV1::EmptyCandidateId) + )); + assert!(matches!( + DomainRequestV1::try_new(Vec::new()), + Err(InvalidRequestV1::EmptyCandidates) + )); +} + +#[test] +fn candidate_order_is_canonical_but_duplicate_ids_are_never_deduplicated() { + let canonical = evaluate(request( + vec![candidate("alpha", [0x75; 3]), candidate("zeta", [0x76; 3])], + vec![applicable(vec![Srgb8::new([0; 3]), Srgb8::new([255; 3])])], + )) + .unwrap(); + let permuted = evaluate(request( + vec![candidate("zeta", [0x76; 3]), candidate("alpha", [0x75; 3])], + vec![applicable(vec![Srgb8::new([255; 3]), Srgb8::new([0; 3])])], + )) + .unwrap(); + + let canonical = evaluated(&canonical); + let permuted = evaluated(&permuted); + assert_eq!(canonical.domain_digest(), permuted.domain_digest()); + assert_eq!(canonical.evaluation_id(), permuted.evaluation_id()); + assert_eq!(canonical.failure_matrix(), permuted.failure_matrix()); + assert_eq!(canonical.proof().partition(), permuted.proof().partition()); + assert_eq!( + canonical + .candidates() + .iter() + .map(|value| value.candidate_id().as_str()) + .collect::>(), + ["alpha", "zeta"] + ); + + let error = evaluate(request( + vec![candidate("same", [1, 2, 3]), candidate("same", [4, 5, 6])], + vec![applicable(vec![Srgb8::new([255; 3])])], + )) + .expect_err("duplicate candidate IDs are contradictory, not duplicate noise"); + assert!(matches!( + error, + ErrorV1::InvalidRequest(InvalidRequestV1::DuplicateCandidateId { candidate_id }) + if candidate_id.as_str() == "same" + )); +} + +#[test] +fn same_emitted_bytes_under_distinct_ids_remain_distinct_matrix_rows() { + let result = evaluate(request( + vec![ + candidate("first", [0x75; 3]), + candidate("second", [0x75; 3]), + ], + vec![applicable(vec![Srgb8::new([0; 3])])], + )) + .unwrap(); + let record = evaluated(&result); + + assert_eq!(record.candidates().len(), 2); + assert_eq!(record.assessments().len(), 2); + assert_eq!(record.proof().domain().candidate_count(), 2); + assert_eq!( + record + .assessments() + .map(|value| (value.candidate_id().as_str(), value.emitted().bytes())) + .collect::>(), + [("first", [0x75; 3]), ("second", [0x75; 3])] + ); +} + +#[test] +fn variable_bit_layout_uses_one_contiguous_matrix_and_zero_tail_bits() { + let result = evaluate(request( + vec![ + candidate("a", [0; 3]), + candidate("b", [0x76; 3]), + candidate("c", [255; 3]), + ], + vec![applicable(vec![ + Srgb8::new([0; 3]), + Srgb8::new([0x76; 3]), + Srgb8::new([255; 3]), + ])], + )) + .unwrap(); + let record = evaluated(&result); + let proof = record.proof(); + + assert_eq!(proof.domain().kind(), DomainKindV1::ExplicitSrgb8Set); + assert_eq!(proof.domain().kind().key(), "explicit-srgb8-set-v1"); + assert_eq!(proof.domain().candidate_count(), 3); + assert_eq!(proof.applicable_edges(), 3); + assert_eq!(proof.logical_assessments(), 9); + assert_eq!(record.failure_matrix().len(), 2, "ceil(3*3/8)"); + assert_eq!(proof.partition().len(), 1, "ceil(3/8)"); + assert_eq!(record.failure_matrix()[1] & 0b1111_1110, 0); + assert_eq!(proof.partition()[0] & 0b1111_1000, 0); + assert_eq!(record.assessments().len(), 9); +} + +#[test] +fn variable_domain_crosses_the_256_candidate_boundary_without_truncation() { + const CANDIDATES: usize = 513; + let result = evaluate(request( + (0..CANDIDATES) + .map(|index| candidate(&format!("candidate/{index:03}"), [255; 3])) + .collect(), + vec![applicable(vec![Srgb8::new([0; 3])])], + )) + .unwrap(); + let record = evaluated(&result); + + assert_eq!(record.proof().domain().candidate_count(), 513); + assert_eq!(record.proof().logical_assessments(), 513); + assert_eq!(record.failure_matrix(), [0_u8; 65]); + assert_eq!(record.proof().partition()[..64], [u8::MAX; 64]); + assert_eq!(record.proof().partition()[64], 1); + assert_eq!(record.assessments().len(), 513); + assert_eq!(record.feasible_candidates().count(), 513); + assert_eq!( + record.candidates().last().unwrap().candidate_id().as_str(), + "candidate/512", + ); +} + +#[test] +fn full_explicit_neutral_set_matches_every_neutral_v1_physical_bit() { + let adjacent = vec![Srgb8::new([0x76; 3])]; + let neutral = evaluate_neutral( + NeutralRequestV1::try_new( + DomainIdV1::Srgb8NeutralAxis, + vec![applicable(adjacent.clone())], + PROFILE, + ) + .unwrap(), + ) + .unwrap(); + let explicit = evaluate(request( + (0_u16..256) + .map(|value| { + let value = value as u8; + candidate(&format!("grey/{value:03}"), [value; 3]) + }) + .collect(), + vec![applicable(adjacent)], + )) + .unwrap(); + let neutral = neutral.evaluated().unwrap(); + let explicit = evaluated(&explicit); + + assert_eq!(explicit.failure_matrix(), neutral.failure_matrix()); + assert_eq!(explicit.proof().partition(), neutral.proof().partition()); + assert_eq!( + explicit + .assessments() + .map(|value| (value.emitted(), value.adjacent(), value.decision())) + .collect::>(), + neutral + .assessments() + .map(|value| (value.candidate(), value.adjacent(), value.decision())) + .collect::>() + ); + assert_ne!(explicit.domain_digest(), neutral.domain_digest()); + assert_ne!(explicit.evaluation_id(), neutral.evaluation_id()); +} + +#[test] +fn no_applicable_relation_is_the_only_not_evaluated_terminal() { + let relation = RelationV1::not_applicable( + relation_id("ornament"), + occurrence_id("decorative"), + Wcag22ClientDeclaredNotApplicableV1::try_new("client-declared").unwrap(), + ); + let result = evaluate(request( + vec![candidate("one", [1, 2, 3]), candidate("two", [4, 5, 6])], + vec![relation], + )) + .unwrap(); + + assert!(result.is_not_evaluated()); + let record = result.not_evaluated().unwrap(); + assert_eq!(record.domain().kind(), DomainKindV1::ExplicitSrgb8Set); + assert_eq!(record.domain().candidate_count(), 2); + assert_eq!(record.candidates().len(), 2); + assert_eq!(record.relations().len(), 1); +} + +#[test] +fn candidate_ids_share_the_existing_opaque_byte_envelope_without_a_new_limit() { + let tiny_relation = || { + RelationV1::applicable( + relation_id("r"), + occurrence_id("o"), + Wcag22CriterionV1::Sc143TextDefault, + vec![Srgb8::new([255; 3])], + ) + .unwrap() + }; + let at_limit = evaluate(request( + vec![candidate(&"x".repeat(65_534), [0; 3])], + vec![tiny_relation()], + )); + assert!( + at_limit.is_ok(), + "candidate ID plus relation/occurrence is 65536 bytes" + ); + + let error = evaluate(request( + vec![candidate(&"x".repeat(65_535), [0; 3])], + vec![tiny_relation()], + )) + .expect_err("65537 aggregate opaque bytes must fail before evaluation"); + assert!(matches!( + error, + ErrorV1::ResourceLimitExceeded { + dimension: labcolors_core::wcag22_feasibility::ResourceDimensionV1::OpaqueUtf8Bytes, + requested: 65_537, + limit: 65_536, + .. + } + )); +} + +#[test] +fn property_variable_c_times_e_is_complete_canonical_and_exactly_packed() { + check_property((1_u8..18, 1_u8..10, any::()), |(c, e, reverse)| { + let mut candidates = (0..c) + .map(|index| { + candidate( + &format!("candidate/{index:03}"), + [index, index.wrapping_mul(17), index.wrapping_mul(31)], + ) + }) + .collect::>(); + if reverse { + candidates.reverse(); + } + let adjacent = (0..e) + .map(|index| Srgb8::new([index, index.wrapping_mul(11), 255 - index])) + .collect(); + let result = evaluate(request(candidates, vec![applicable(adjacent)])).unwrap(); + let record = evaluated(&result); + let work = u64::from(c) * u64::from(e); + let matrix_bytes = work.div_ceil(8) as usize; + let partition_bytes = u64::from(c).div_ceil(8) as usize; + + prop_assert_eq!(record.proof().logical_assessments(), work); + prop_assert_eq!(record.assessments().len(), work as usize); + prop_assert_eq!(record.failure_matrix().len(), matrix_bytes); + prop_assert_eq!(record.proof().partition().len(), partition_bytes); + prop_assert_eq!( + record.feasible_candidates().count() + record.infeasible_candidates().count(), + usize::from(c) + ); + prop_assert_eq!( + record + .candidates() + .windows(2) + .all(|pair| pair[0].candidate_id().as_str().as_bytes() + < pair[1].candidate_id().as_str().as_bytes()), + true + ); + let matrix_tail = (work % 8) as u8; + if matrix_tail != 0 { + let used = ((1_u16 << matrix_tail) - 1) as u8; + prop_assert_eq!(record.failure_matrix().last().unwrap() & !used, 0); + } + let partition_tail = c % 8; + if partition_tail != 0 { + let used = ((1_u16 << partition_tail) - 1) as u8; + prop_assert_eq!(record.proof().partition().last().unwrap() & !used, 0); + } + Ok(()) + }); +} + +#[test] +fn exact_unicode_bytes_and_emitted_bytes_both_belong_to_domain_identity() { + let compile = |id: &str, emitted: [u8; 3]| { + evaluate(request( + vec![candidate(id, emitted)], + vec![applicable(vec![Srgb8::new([255; 3])])], + )) + .unwrap() + }; + let composed = compile("caf\u{e9}", [1, 2, 3]); + let decomposed = compile("cafe\u{301}", [1, 2, 3]); + let changed_bytes = compile("caf\u{e9}", [1, 2, 4]); + + assert_ne!( + evaluated(&composed).domain_digest(), + evaluated(&decomposed).domain_digest(), + "Core must not normalize opaque Unicode IDs" + ); + assert_ne!( + evaluated(&composed).domain_digest(), + evaluated(&changed_bytes).domain_digest(), + "the emitted physical bytes are part of the declared-domain identity" + ); + assert_ne!( + evaluated(&composed).evaluation_id(), + evaluated(&changed_bytes).evaluation_id() + ); +} + +#[test] +fn production_identity_matches_the_independent_unicode_oracle_fixture() { + assert_eq!( + fixture_sha256::digest(IDENTITY_FIXTURE.as_bytes()).to_hex(), + "92a03a0ac961163b1e0e69f3166026544af3b7c3acf89fb4d13eaaa462952d7f" + ); + let applicable = RelationV1::applicable( + relation_id("alpha"), + occurrence_id("hover/🎨"), + Wcag22CriterionV1::Sc143TextDefault, + vec![ + Srgb8::new([255; 3]), + Srgb8::new([0; 3]), + Srgb8::new([118; 3]), + Srgb8::new([0; 3]), + ], + ) + .unwrap(); + let not_applicable = RelationV1::not_applicable( + relation_id("zeta"), + occurrence_id("ornament"), + Wcag22ClientDeclaredNotApplicableV1::try_new("client/не-применимо").unwrap(), + ); + let result = evaluate(request( + vec![ + candidate("🎨", [255, 128, 1]), + candidate("é", [18, 52, 86]), + candidate("海", [0, 0, 0]), + candidate("e\u{301}", [18, 52, 86]), + ], + vec![not_applicable, applicable], + )) + .unwrap(); + let record = evaluated(&result); + + assert_eq!( + record + .candidates() + .iter() + .map(|value| value.candidate_id().as_str()) + .collect::>(), + ["e\u{301}", "é", "海", "🎨"] + ); + assert_eq!(record.failure_matrix(), [0x5b, 0x0c]); + assert_eq!(record.proof().partition(), [0x00]); + assert_eq!( + hex(record.domain_digest().as_bytes()), + "71960b339a5af0421a5562e02aea28217b3f985c88a53f3244ea73f6c19258f4" + ); + assert_eq!( + hex(record.relation_set_digest().as_bytes()), + "990dbc58252dc518ccf63b2f4b63ef5ae227a2bed48dda9e5e5959f3e2477132" + ); + assert_eq!( + hex(record.evaluation_id().as_bytes()), + "59e69b867d8feb8afae4d28708bd353d0f3a0e89c12b0f34952f2e9a5e8be700" + ); + assert_eq!( + hex(record.proof().matrix_digest()), + "f414937d1b17276054be72790c34aef5a4eb5b6dc2132122599d47297bba5507" + ); +} + +#[test] +fn explicit_terminals_and_proof_views_cannot_be_forged_or_rewrapped_downstream() { + assert_downstream_rejected( + r#"use labcolors_core::wcag22_feasibility::explicit::{ + DomainDescriptorV1, EvaluatedV1, EvaluationProofV1, FeasibilityV1, + NotEvaluatedV1, +}; + +fn wrap_feasible(value: EvaluatedV1) -> FeasibilityV1 { + FeasibilityV1::Feasible(value) +} + +fn wrap_infeasible(value: EvaluatedV1) -> FeasibilityV1 { + FeasibilityV1::Infeasible(value) +} + +fn wrap_not_evaluated(value: NotEvaluatedV1) -> FeasibilityV1 { + FeasibilityV1::NotEvaluated(value) +} + +fn main() { + let _domain = DomainDescriptorV1 {}; + let _proof: EvaluationProofV1<'static> = EvaluationProofV1 {}; + let _evaluated = EvaluatedV1 {}; + let _not_evaluated = NotEvaluatedV1 {}; +} +"#, + &[ + "Feasible", + "Infeasible", + "NotEvaluated", + "DomainDescriptorV1", + "EvaluationProofV1", + "EvaluatedV1", + "NotEvaluatedV1", + ], + ); +} diff --git a/crates/labcolors-wasm/Cargo.toml b/crates/labcolors-wasm/Cargo.toml index ce030846..670d7f02 100644 --- a/crates/labcolors-wasm/Cargo.toml +++ b/crates/labcolors-wasm/Cargo.toml @@ -16,7 +16,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] # Direct Core use stays isolated from optional compiler capabilities. The -# versioned protocol crate is the single owner of WCAG22 feasibility. +# versioned protocol crate owns only the registered-domain WCAG22 projection. labcolors-core = { path = "../labcolors-core", default-features = false } labcolors-protocol = { path = "../labcolors-protocol" } wasm-bindgen = { workspace = true } diff --git a/docs/NAMING.md b/docs/NAMING.md index 01a62b08..106e7f87 100644 --- a/docs/NAMING.md +++ b/docs/NAMING.md @@ -16,7 +16,7 @@ | членов workspace (Cargo.toml `members`, глоб развёрнут по ФС) | 6 | | крейтов семейства в crates/ | 5 | | экспорт-субпутей package.json @labpics/colors | 8 | -| python-скриптов scripts/*.py | 9 | +| python-скриптов scripts/*.py | 10 | | маркдаун-доков docs/**/*.md (включая этот канон) | 12 | | векторов conformance/vectors/*.json (включая manifest) | 8 | | файлов вне закона имён | 4 | @@ -72,8 +72,11 @@ исходный текст не меняет хэш. - `verify_wcag22_neutral_axis.py` независимо выводит точные фикстуры нейтральной оси; `verify_wcag22_feasibility_identity.py` воспроизводит канонические - прообразы и упаковку битов; `check_wcag22_feasibility_benchmark.py` проверяет - с отказом при любом расхождении неизменяемый первичный допуск; + прообразы фиксированного домена; + `verify_wcag22_explicit_feasibility_identity.py` независимо проверяет точные + UTF-8 ID, переменную непрерывную LSB0-упаковку, partition и SHA-грамматику + явного конечного домена; `check_wcag22_feasibility_benchmark.py` проверяет с + отказом при любом расхождении неизменяемый первичный допуск; `check_wcag22_feasibility_applicability.py` отдельно доказывает его текущую применимость, сохраняя полное Core source tree и точные внешние dependency- записи; изменяться могут только source-less не-Core workspace-записи без diff --git a/docs/verification-map.md b/docs/verification-map.md index 47c1b2f4..6e502439 100644 --- a/docs/verification-map.md +++ b/docs/verification-map.md @@ -90,23 +90,33 @@ | право выпускать терминальное доказательство связано с фактической типизированной строкой WCAG registry | скомпилированная Rust-проба читает действующую строку; Python канонизирует 10 значимых для выпуска полей через length-prefix/SHA-256; 10 мутаций полей + 2 мутации hex/count транспорта обязаны отказать | независимая локальная привязка допуска (в proof всего 15 негативных контролей) | | один вердикт и его доказательство сохраняются через Core → FFI/WASM → JS/Swift/контракт соответствия | `wcag22_transport_*`, `wasm_parity`, `wcag22.test.mjs`, Swift conformance, зафиксированный набор из шести `wcag22.json`; release verifier повторно проверяет байты доказательств | дифференциальный межграничный оракул | -## Конечная компиляция выполнимости WCAG 2.2 — `wcag22_feasibility.rs` (#295) +## Конечная компиляция выполнимости WCAG 2.2 — `wcag22_feasibility.rs` (#295, #296-A) Модуль канонизирует непрозрачные клиентские декларации и полностью перечисляет -зарегистрированный конечный домен. Он не ранжирует кандидаты, не выводит -применимость или размер текста из ID и не заменяет перцептивную цель решателя. +либо зарегистрированную нейтральную ось, либо явно объявленный клиентом конечный +набор финальных sRGB8. Оба входа используют один приватный kernel. Он не +ранжирует кандидаты, не выводит применимость или размер текста из ID и не +заменяет перцептивную цель решателя. | инвариант | чем верифицирован | оракул | |---|---|---| | все 256 нейтралей проверяются против каждого канонического применимого соседа; граничные множества для 4.5:1 и 3:1 совпадают с независимо пересчитанным эталоном | `verify_wcag22_neutral_axis.py`; `production_vectors_are_bound_to_the_exact_independent_oracle_fixture`; тесты полной матрицы и границ | независимая `Fraction`-арифметика с адаптивными точными границами корня пятой степени; производственный Q55 и Rust-вычислитель не импортируются | | перестановки и точные дубликаты не меняют канонические ID содержимого; изменение непрозрачного ID меняет идентичность, но не физическое разбиение | `verify_wcag22_feasibility_identity.py`; `exact_identity_preimages_match_the_independent_cross_language_fixture`; тесты свойств и фиксации текущего поведения канонизации | независимая Python-транскрипция точной байтовой грамматики и SHA-256 + внутренние метаморфные тесты | | терминал появляется только после полного `W=256E`; упакованное хранилище равно `B=0` при `A=0`, иначе `B=32(E+1)`; частичный терминал и неявная подстановка результата отсутствуют | тесты с внедрением отказов вычислителя, хранилища, выделения памяти и полноты; `check_wcag22_feasibility_benchmark.py` проверяет полный набор граничных форм, точные счётчики и SHA-256 графа зависимостей | типизированные негативные контроли + прямые измерения скомпилированного процесса текущей платформы; прошедшее время не является порогом приёмки | +| явный домен сортируется по точным UTF-8-байтам непрозрачного ID; повторы ID запрещены, одинаковый sRGB8 под разными ID сохраняет две строки; ID и физические байты входят в domain identity | `verify_wcag22_explicit_feasibility_identity.py`; Unicode-фикстура, permutation/property/duplicate positive-control и compile-fail тесты | независимая Python-транскрипция канонизации, length-prefix/SHA-256 и непрерывной LSB0-упаковки; 6 мутаций + 2 инвариантных контроля | +| для явного домена Core выводит `W=C×E`, `M=ceil(W/8)`, `P=ceil(C/8)` и при `E>0` один буфер `B=M+P`; при `E=0` результат не вычисляется и `B=0` | инструментированный приватный kernel доказывает точные вызовы/reserve/writes; property покрывает переменные `C/E`; совместная грань при максимальном `E` (`C=256,E=2047,W=524032,B=65536`) завершается, а `C+1` при том же `E` отклоняется до evaluation/allocation; отдельный public-тест успешно проходит `C=513,E=1` и границу 64-го байта partition; полный явный набор нейтралей побитно совпадает с V1 | проверяемая целочисленная арифметика + differential к прежнему публичному пути + негативные storage/tail-bit контроли | + +Строки транспорта ниже доказывают только уже отгруженный вход +зарегистрированной нейтральной оси V1. Явный клиентский набор в этом релизе +Core-only; эти evidence не расширяются на его отсутствующие +Protocol/WASM/FFI/npm/Swift проекции. + | transport V1 принимает только strict UTF-8 JSON bytes, сначала применяет выведенный предел envelope, затем сохраняет полную Core-алгебру как `Success(feasibility) \| Failure(error)` | `labcolors-protocol`: exact-limit witness, limit+1 decoder-spy, strict-schema/error-algebra tests и compile-fail запрет forged outcome | литеральная грамматика + Core resource-profile SSOT + тип-уровневый негативный контроль | | WASM/npm не повторяет Core-математику, preflight-ит `Uint8Array.byteLength` до избегаемой ABI-копии и переносит domain/relations один раз с candidate-major LSB0 matrix | `wasm_parity::committed_conformance_pack_replays_in_wasm32`; feasibility boundary tests; `wcag22-feasibility.test.mjs` с независимым LSB0 reduction и mutation subject | дифференциальный public-boundary replay pack 5 + независимый packed consumer | | UniFFI/Swift вызывает тот же protocol byte path, preflight-ит `Data`/`[UInt8]` до сырой FFI-копии и исчерпывающе декодирует terminal/error algebra | FFI mechanical-shell tests; Swift pack-5 replay, limit+1 bridge spy, structural mutation tests и extreme-shape whole-call observations | побайтный FFI/protocol differential + независимый Swift packed consumer | | conformance pack 5 добавляет только feasibility-family; прежние шесть family остаются byte-identical, а 13 новых outcomes воспроизводятся canonical protocol encoder побайтно | `pack_v5_contract`; `reference_runner::protocol_reproduces_committed_wcag22_feasibility_exactly`; release verifier/clean-install replay | SHA-256 immutable-family guards + дифференциальный protocol/public-package replay | -| исторический native-допуск остаётся неизменным, а текущая применимость не зависит от добавленных transport-крейтов | старый checker исполняется в чистом snapshot `6001cf4`; отдельный applicability verifier фиксирует полный Core source tree, Core package record и все sourced lock records, разрешая только изменения source-less non-Core workspace-записей | exact Git objects + fail-closed Cargo.lock V4 parser и мутационные негативные контроли | -| канонический WASM transport воспроизводим побайтно и завершает все граничные whole-call формы | append-only size V2: `521240 B` + точный SHA без headroom; committed whole-call artifact: 10 форм × 5 fresh-process samples, exact request/outcome hashes и source/pack/toolchain bindings | независимые Linux `wc`/`sha256sum` + повторный package-root verifier; latency/maxRSS/pages не превращаются в пороги | +| исторический native-допуск V1 остаётся неизменным; V2 заново измеряет текущий neutral-вход через общий finite-domain kernel | V1 checker исполняется в чистом измеренном snapshot `6001cf4`, его более поздний applicability verifier — в последнем применимом `94efeee`; V2 привязан к source SHA `965eb42`, artifact SHA `d8d5c7f3eda834bca9912d835fe3ada13d9dcd5a11cb47a131736716b0b51202` и полному текущему Core source tree; applicability разрешает только изменения source-less non-Core workspace-записей | exact Git objects + fail-closed Cargo.lock V4 parser, 13 V2 checker-мутаций и сырые native-наблюдения без timing threshold | +| канонический WASM transport воспроизводим побайтно и завершает все граничные whole-call формы | append-only size V3: `521231 B` + точный SHA без headroom; V1/V2 неизменны; committed whole-call artifact: 10 форм × 5 fresh-process samples, exact request/outcome hashes и source/pack/toolchain bindings | независимые Linux `wc`/`sha256sum` + повторный package-root verifier; latency/maxRSS/pages не превращаются в пороги | Здесь `E` — число канонических применимых рёбер «связь × сосед» (`E∈{0,1,…,2047}`), `A` — число применимых связей (`A∈{0,1,…,E}`; @@ -128,14 +138,18 @@ `65504/32=2047`. Это граница продуктовой политики результата, а не утверждение о полной памяти WebAssembly. -Чувствительность явна: для другого зарегистрированного домена мощности `D` -число вычислений станет `W=DE`, а закон `B` будет зависеть от явно выбранной -схемы битовой упаковки. V1 не заявляет такую обобщённую реализацию: другой домен -требует новой версионированной регистрации, ресурсного профиля, точного закона -хранения, оракула и артефакта допуска. Формулы V1 нельзя молча переносить на -него. - -Артефакт измерений не доказывает полную память WebAssembly, размер +Для явного домена `C` — выведенное Core число канонических клиентских записей, +а не переданная клиентом метаинформация. При `E>0` непрерывная candidate-major +LSB0-матрица занимает `ceil(C×E/8)` байт без выравнивания строк, partition — +`ceil(C/8)` байт; неиспользуемые хвостовые биты обязаны быть нулевыми. Полнота +означает только перебор всех членов объявленного набора, никогда не весь gamut. +Зарегистрированная нейтральная ось остаётся частным случаем `C=256` с прежними +байтами публичного V1-контракта. + +Артефакт V2 измеряет прежнюю нейтральную ось через общий kernel и потому +проверяет отсутствие регрессии уже работающего пути. Он не подменяет точные +тесты переменного `C×E` и не выдаёт время neutral-сортировки за оценку стоимости +клиентских ID. Артефакт не доказывает полную память WebAssembly, размер сериализованного результата или клиентскую задержку: эти величины явно находятся вне границы доказанного утверждения. diff --git a/packages/colors/README.md b/packages/colors/README.md index 59fffc6a..3329eb3c 100644 --- a/packages/colors/README.md +++ b/packages/colors/README.md @@ -283,6 +283,9 @@ diagnostics и legacy `wcagRatio` не могут изменить этот ве кандидаты проходят все эти ограничения?». Она не выбирает лучший цвет, не угадывает применимость и не понимает семантику ID. +Эта npm-граница принимает только зарегистрированную нейтральную ось V1. Явные +клиентские наборы sRGB8 доступны в Rust Core и этим transport API не принимаются. + ```ts import init, { evaluateWcag22Feasibility, @@ -463,8 +466,11 @@ Raw-размер WASM — hard gate с append-only историей. Неизм `bench/wasm-size-budget-v2.json` отдельно допускает полный transport #295: ровно `521240 B`, SHA-256 `d37841bfb2615d05c8366b08dcc7e5aed1bbd3cf27c3db67896108c5ec9c9ca0`. -V2 ссылается на точные байты V1 и его build recipe; новый ceiling равен -измерению, поэтому произвольного запаса нет и история #284 не переписана. +Текущий `bench/wasm-size-budget-v3.json` повторно допускает Core-срез #296-A: +ровно `521231 B`, SHA-256 +`779379e914909ff1ddbb5afdd6554d026b586f3c71ef6b2cfeba3468bf93e029`. +Каждый ceiling равен своему каноническому Linux-x64 измерению, произвольного +запаса нет; V1/V2 остаются побайтно неизменными. Release-equivalent Linux x64 CI требует одновременно точный размер и SHA. Отдельный `bench/wcag22-feasibility-wasm-boundary-v1.json` фиксирует 10 крайних diff --git a/packages/colors/bench/wasm-size-budget-v3.json b/packages/colors/bench/wasm-size-budget-v3.json new file mode 100644 index 00000000..2dfae67c --- /dev/null +++ b/packages/colors/bench/wasm-size-budget-v3.json @@ -0,0 +1,21 @@ +{ + "schemaVersion": 3, + "budgetId": "labcolors-wasm-raw-issue-296-v3", + "artifact": "packages/colors/pkg/labcolors_bg.wasm", + "buildRecipe": { + "path": "packages/colors/bench/wasm-size-budget-v1.json", + "fileSha256": "4f7340fc8cfd0ccb97377c385f2f8d8e7a9ef2c5ba96177f518c5d07de2825e1", + "recipeSha256": "0ea74cb070e0a5facb7280f6124930a0bb673ee4dcee9c99fff110db6c9389d4" + }, + "measurement": { + "issue": 296, + "measurementPlatform": "linux-x64", + "rawBytes": 521231, + "sha256": "779379e914909ff1ddbb5afdd6554d026b586f3c71ef6b2cfeba3468bf93e029" + }, + "policy": { + "maxRawBytes": 521231, + "derivation": "exact-accepted-issue-296-slice-a-measurement", + "gzip": "diagnostic-only" + } +} diff --git a/packages/colors/bench/wcag22-feasibility-wasm-boundary-v1.json b/packages/colors/bench/wcag22-feasibility-wasm-boundary-v1.json index bda8e8e1..1485658f 100644 --- a/packages/colors/bench/wcag22-feasibility-wasm-boundary-v1.json +++ b/packages/colors/bench/wcag22-feasibility-wasm-boundary-v1.json @@ -1 +1 @@ -{"schemaVersion":1,"artifactId":"wcag22-feasibility-wasm-whole-call-v1","claimBoundary":"canonical-wasm-package-root-whole-call-observations-only","claims":{"admission":"canonical-linux-x64-exact-wasm-only","hardGates":["completion","request-and-outcome-bytes","sha256-binding","terminal-algebra","packed-shape","candidate-major-lsb0-pack-oracle","no-proportional-dto"],"timingThresholdNs":null,"latency":"observation-only-no-production-threshold","memory":"process maxRSS is total-process high-water including V8; post-call WASM pages are linear-memory high-water observations; neither is total operation memory"},"environment":{"execution":"fresh-node-child-process-per-sample","platform":"linux-x64","nodeVersion":"v24.14.0","sampleCount":5,"requestConstructionMeasured":false,"timer":"process.hrtime.bigint","packageRootApi":"packages/colors/index.js","canonicalCandidate":true,"rustToolchain":"1.96.0","wasmPack":"0.13.1","wasmBindgen":"0.2.126","target":"wasm32-unknown-unknown","cargoProfile":"release","wasmOpt":"-Oz"},"bindings":{"coreAdmission":{"path":"crates/labcolors-core/contracts/wcag22-feasibility-benchmark-v1.json","schemaVersion":1,"artifactId":"wcag22-feasibility-admission-raw-v1","profileId":"compile-v1","sha256":"7e9ffcbdd9d5d50fe681f511c34fc5c5dd270e9c475ce23ae56e9776922a3c5e"},"packOracle":{"path":"conformance/vectors/wcag22-feasibility.json","sha256":"ae2caec47a7b650e73b8d4029a69b4e401dfb7cc199db579c0f95106eebe8dc3","caseId":"text-default-seven","requestSha256":"8da46d4cb835c4e3c89b8f56ec4025457e0e4308ed28adcfa7beb134858e3d6f","outcomeSha256":"9dbe5499abfd07cd915da6da0ca1578278aebc33b0f24c41c6318858026ff442","manifestPath":"conformance/vectors/manifest.json","manifestSha256":"6b873967ba648b4ec160fd3e909a3359d6ffcb0f129bf38711aa74f3dff5a63d","packVersion":"5.0.0","packDigest":"48d00cd5"},"runtimeSources":{"harness":{"path":"packages/colors/bench/wcag22-feasibility-boundary.bench.mjs","sha256":"edad6ca3e5f5361c176831cbdb5b6caec0a8c552d21ac7937c8aa86480452f49"},"packageManifest":{"path":"packages/colors/package.json","sha256":"a3fa15d4ee232abb8ff392a47bd1b2768198255106e9435da2ab2ef0777c9fdf"},"packageRoot":{"path":"packages/colors/index.js","sha256":"5b414bfc3abca90996003d30a87d868b652dc298dd0deec3c1110a746fe3f833"},"wasmGlue":{"path":"packages/colors/pkg/labcolors.js","sha256":"b9933eff570f8120726341933dc765efa0c2cba7d37a83cef83f3bd1f13db9d7"},"adaptTheme":{"path":"packages/colors/adapt-theme.js","sha256":"5629566a0dce766c7101852a2b19b58066916427db11b7844b29b3b45133b06a"},"applyTheme":{"path":"packages/colors/apply-theme.js","sha256":"36a4c598042cd2be8579ef7a315ee598a25954c4e990aeec6382177bab53a76a"},"effectiveBackground":{"path":"packages/colors/effective-bg.js","sha256":"03cf758df0cdb34c840bedfab8a996eb44a8ad9b603bf241c3a8a9f88b359460"},"watchTheme":{"path":"packages/colors/watch-theme.js","sha256":"f0287092f1ed4d413886b2650e930afcb527ecb3ad6536dbe548347d12159ebd"}},"wasmToolchain":{"path":"packages/colors/bench/wasm-size-budget-v1.json","schemaVersion":2,"budgetId":"labcolors-wasm-raw-issue-284-v1","recipeSha256":"0ea74cb070e0a5facb7280f6124930a0bb673ee4dcee9c99fff110db6c9389d4"},"wasm":{"path":"packages/colors/pkg/labcolors_bg.wasm","bytes":521240,"sha256":"d37841bfb2615d05c8366b08dcc7e5aed1bbd3cf27c3db67896108c5ec9c9ca0"}},"limits":{"maxRequestBytes":657380,"profileId":"compile-v1","rawRelations":2047,"rawAdjacentEntries":2047,"opaqueUtf8Bytes":65536,"canonicalRelations":2047,"applicableEdges":2047,"logicalAssessments":524032,"packedResultBytes":65536},"scenarios":[{"scenarioId":"minimum-evaluated","shape":{"rawRelations":1,"rawAdjacentEntries":1,"opaqueUtf8Bytes":2,"canonicalRelations":1,"applicableRelations":1,"applicableEdges":1},"samples":[{"sampleIndex":0,"elapsedNs":"6797003","requestBytes":222,"requestSha256":"21ff2cc1a9b3da86e633f999a5c8417f0be059c499fc9114490e274b319d2ee7","outcomeBytes":4823,"outcomeSha256":"a6196a5aaec38b74029c0d6f5016b9249008d7cd9544c3d5cf5de9932770e090","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"lsb0PackOracleMatches":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":55968,"processMaxRssKiBAfter":57260,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1245184,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":19},{"sampleIndex":1,"elapsedNs":"4074156","requestBytes":222,"requestSha256":"21ff2cc1a9b3da86e633f999a5c8417f0be059c499fc9114490e274b319d2ee7","outcomeBytes":4823,"outcomeSha256":"a6196a5aaec38b74029c0d6f5016b9249008d7cd9544c3d5cf5de9932770e090","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"lsb0PackOracleMatches":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":56060,"processMaxRssKiBAfter":57212,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1245184,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":19},{"sampleIndex":2,"elapsedNs":"9780233","requestBytes":222,"requestSha256":"21ff2cc1a9b3da86e633f999a5c8417f0be059c499fc9114490e274b319d2ee7","outcomeBytes":4823,"outcomeSha256":"a6196a5aaec38b74029c0d6f5016b9249008d7cd9544c3d5cf5de9932770e090","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"lsb0PackOracleMatches":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":56108,"processMaxRssKiBAfter":57440,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1245184,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":19},{"sampleIndex":3,"elapsedNs":"22370955","requestBytes":222,"requestSha256":"21ff2cc1a9b3da86e633f999a5c8417f0be059c499fc9114490e274b319d2ee7","outcomeBytes":4823,"outcomeSha256":"a6196a5aaec38b74029c0d6f5016b9249008d7cd9544c3d5cf5de9932770e090","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"lsb0PackOracleMatches":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54260,"processMaxRssKiBAfter":55464,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1245184,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":19},{"sampleIndex":4,"elapsedNs":"9011452","requestBytes":222,"requestSha256":"21ff2cc1a9b3da86e633f999a5c8417f0be059c499fc9114490e274b319d2ee7","outcomeBytes":4823,"outcomeSha256":"a6196a5aaec38b74029c0d6f5016b9249008d7cd9544c3d5cf5de9932770e090","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"lsb0PackOracleMatches":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54220,"processMaxRssKiBAfter":55372,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1245184,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":19}]},{"scenarioId":"maximum-canonical-applicable-relations","shape":{"rawRelations":2047,"rawAdjacentEntries":2047,"opaqueUtf8Bytes":20470,"canonicalRelations":2047,"applicableRelations":2047,"applicableEdges":2047},"samples":[{"sampleIndex":0,"elapsedNs":"284403596","requestBytes":264164,"requestSha256":"83955d22987a29e4df0c91647cecadcb3a7e7f5bc06010e0b05519fac4b938d2","outcomeBytes":527090,"outcomeSha256":"dedc563b2a3f7bee7a0f37c5976af853814ac4e9ce12f27efaa3ec8661a79b1c","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"2047","applicableRelations":"2047","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":56624,"processMaxRssKiBAfter":68348,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4259840,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":65},{"sampleIndex":1,"elapsedNs":"211332656","requestBytes":264164,"requestSha256":"83955d22987a29e4df0c91647cecadcb3a7e7f5bc06010e0b05519fac4b938d2","outcomeBytes":527090,"outcomeSha256":"dedc563b2a3f7bee7a0f37c5976af853814ac4e9ce12f27efaa3ec8661a79b1c","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"2047","applicableRelations":"2047","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":58256,"processMaxRssKiBAfter":72704,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4259840,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":65},{"sampleIndex":2,"elapsedNs":"328038886","requestBytes":264164,"requestSha256":"83955d22987a29e4df0c91647cecadcb3a7e7f5bc06010e0b05519fac4b938d2","outcomeBytes":527090,"outcomeSha256":"dedc563b2a3f7bee7a0f37c5976af853814ac4e9ce12f27efaa3ec8661a79b1c","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"2047","applicableRelations":"2047","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":58300,"processMaxRssKiBAfter":72040,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4259840,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":65},{"sampleIndex":3,"elapsedNs":"377642199","requestBytes":264164,"requestSha256":"83955d22987a29e4df0c91647cecadcb3a7e7f5bc06010e0b05519fac4b938d2","outcomeBytes":527090,"outcomeSha256":"dedc563b2a3f7bee7a0f37c5976af853814ac4e9ce12f27efaa3ec8661a79b1c","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"2047","applicableRelations":"2047","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":56868,"processMaxRssKiBAfter":67596,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4259840,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":65},{"sampleIndex":4,"elapsedNs":"150545765","requestBytes":264164,"requestSha256":"83955d22987a29e4df0c91647cecadcb3a7e7f5bc06010e0b05519fac4b938d2","outcomeBytes":527090,"outcomeSha256":"dedc563b2a3f7bee7a0f37c5976af853814ac4e9ce12f27efaa3ec8661a79b1c","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"2047","applicableRelations":"2047","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":58376,"processMaxRssKiBAfter":71784,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4259840,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":65}]},{"scenarioId":"maximum-applicable-edges","shape":{"rawRelations":1,"rawAdjacentEntries":2047,"opaqueUtf8Bytes":19,"canonicalRelations":1,"applicableRelations":1,"applicableEdges":2047},"samples":[{"sampleIndex":0,"elapsedNs":"231849342","requestBytes":23909,"requestSha256":"9a60a035743121bc4b79660f3a76f75ff1d3da2c5b3a243449442f972e8af9ae","outcomeBytes":269795,"outcomeSha256":"7f08a3d65184a40adc8de04edc352511130fe6fc8e223729996a11cd18bc622c","summary":{"outcome":"success","terminal":"infeasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":0,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":56256,"processMaxRssKiBAfter":66104,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":2228224,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":34},{"sampleIndex":1,"elapsedNs":"285102118","requestBytes":23909,"requestSha256":"9a60a035743121bc4b79660f3a76f75ff1d3da2c5b3a243449442f972e8af9ae","outcomeBytes":269795,"outcomeSha256":"7f08a3d65184a40adc8de04edc352511130fe6fc8e223729996a11cd18bc622c","summary":{"outcome":"success","terminal":"infeasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":0,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54440,"processMaxRssKiBAfter":65656,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":2228224,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":34},{"sampleIndex":2,"elapsedNs":"101696689","requestBytes":23909,"requestSha256":"9a60a035743121bc4b79660f3a76f75ff1d3da2c5b3a243449442f972e8af9ae","outcomeBytes":269795,"outcomeSha256":"7f08a3d65184a40adc8de04edc352511130fe6fc8e223729996a11cd18bc622c","summary":{"outcome":"success","terminal":"infeasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":0,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54232,"processMaxRssKiBAfter":64752,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":2228224,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":34},{"sampleIndex":3,"elapsedNs":"157785234","requestBytes":23909,"requestSha256":"9a60a035743121bc4b79660f3a76f75ff1d3da2c5b3a243449442f972e8af9ae","outcomeBytes":269795,"outcomeSha256":"7f08a3d65184a40adc8de04edc352511130fe6fc8e223729996a11cd18bc622c","summary":{"outcome":"success","terminal":"infeasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":0,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54224,"processMaxRssKiBAfter":63764,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":2228224,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":34},{"sampleIndex":4,"elapsedNs":"162747300","requestBytes":23909,"requestSha256":"9a60a035743121bc4b79660f3a76f75ff1d3da2c5b3a243449442f972e8af9ae","outcomeBytes":269795,"outcomeSha256":"7f08a3d65184a40adc8de04edc352511130fe6fc8e223729996a11cd18bc622c","summary":{"outcome":"success","terminal":"infeasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":0,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54332,"processMaxRssKiBAfter":64136,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":2228224,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":34}]},{"scenarioId":"maximum-opaque-utf8-bytes","shape":{"rawRelations":1,"rawAdjacentEntries":0,"opaqueUtf8Bytes":65536,"canonicalRelations":1,"applicableRelations":0,"applicableEdges":0},"samples":[{"sampleIndex":0,"elapsedNs":"33663889","requestBytes":65710,"requestSha256":"4c2106974c30967456ac4d92bced32418cb0cb9b8f9ba97e53f19e0f629cf1f2","outcomeBytes":66050,"outcomeSha256":"4651c1ed3d6d43ef8d0eeaf78bb745d4b58ba7d160877df4a23433b233869f18","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"1","applicableRelations":"0","notApplicableRelations":"1","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":56232,"processMaxRssKiBAfter":61504,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1572864,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":24},{"sampleIndex":1,"elapsedNs":"15353309","requestBytes":65710,"requestSha256":"4c2106974c30967456ac4d92bced32418cb0cb9b8f9ba97e53f19e0f629cf1f2","outcomeBytes":66050,"outcomeSha256":"4651c1ed3d6d43ef8d0eeaf78bb745d4b58ba7d160877df4a23433b233869f18","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"1","applicableRelations":"0","notApplicableRelations":"1","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":56236,"processMaxRssKiBAfter":61640,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1572864,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":24},{"sampleIndex":2,"elapsedNs":"7861688","requestBytes":65710,"requestSha256":"4c2106974c30967456ac4d92bced32418cb0cb9b8f9ba97e53f19e0f629cf1f2","outcomeBytes":66050,"outcomeSha256":"4651c1ed3d6d43ef8d0eeaf78bb745d4b58ba7d160877df4a23433b233869f18","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"1","applicableRelations":"0","notApplicableRelations":"1","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54404,"processMaxRssKiBAfter":59456,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1572864,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":24},{"sampleIndex":3,"elapsedNs":"10565477","requestBytes":65710,"requestSha256":"4c2106974c30967456ac4d92bced32418cb0cb9b8f9ba97e53f19e0f629cf1f2","outcomeBytes":66050,"outcomeSha256":"4651c1ed3d6d43ef8d0eeaf78bb745d4b58ba7d160877df4a23433b233869f18","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"1","applicableRelations":"0","notApplicableRelations":"1","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54196,"processMaxRssKiBAfter":59564,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1572864,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":24},{"sampleIndex":4,"elapsedNs":"7964923","requestBytes":65710,"requestSha256":"4c2106974c30967456ac4d92bced32418cb0cb9b8f9ba97e53f19e0f629cf1f2","outcomeBytes":66050,"outcomeSha256":"4651c1ed3d6d43ef8d0eeaf78bb745d4b58ba7d160877df4a23433b233869f18","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"1","applicableRelations":"0","notApplicableRelations":"1","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54288,"processMaxRssKiBAfter":59788,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1572864,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":24}]},{"scenarioId":"maximum-canonical-not-applicable-relations","shape":{"rawRelations":2047,"rawAdjacentEntries":0,"opaqueUtf8Bytes":36846,"canonicalRelations":2047,"applicableRelations":0,"applicableEdges":0},"samples":[{"sampleIndex":0,"elapsedNs":"19423868","requestBytes":186378,"requestSha256":"b92c8700a4f6f6979e9476d56e57166618971aaecdbf4445ccd6aac124f821b7","outcomeBytes":186719,"outcomeSha256":"bf21a25ad92cfefad9c7116c16f918d97dab5e18e610a21482b26fdf5a948d69","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"2047","applicableRelations":"0","notApplicableRelations":"2047","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":59116,"processMaxRssKiBAfter":70660,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":2162688,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":33},{"sampleIndex":1,"elapsedNs":"26496648","requestBytes":186378,"requestSha256":"b92c8700a4f6f6979e9476d56e57166618971aaecdbf4445ccd6aac124f821b7","outcomeBytes":186719,"outcomeSha256":"bf21a25ad92cfefad9c7116c16f918d97dab5e18e610a21482b26fdf5a948d69","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"2047","applicableRelations":"0","notApplicableRelations":"2047","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":59272,"processMaxRssKiBAfter":68364,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":2162688,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":33},{"sampleIndex":2,"elapsedNs":"27000578","requestBytes":186378,"requestSha256":"b92c8700a4f6f6979e9476d56e57166618971aaecdbf4445ccd6aac124f821b7","outcomeBytes":186719,"outcomeSha256":"bf21a25ad92cfefad9c7116c16f918d97dab5e18e610a21482b26fdf5a948d69","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"2047","applicableRelations":"0","notApplicableRelations":"2047","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":59240,"processMaxRssKiBAfter":70760,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":2162688,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":33},{"sampleIndex":3,"elapsedNs":"48798368","requestBytes":186378,"requestSha256":"b92c8700a4f6f6979e9476d56e57166618971aaecdbf4445ccd6aac124f821b7","outcomeBytes":186719,"outcomeSha256":"bf21a25ad92cfefad9c7116c16f918d97dab5e18e610a21482b26fdf5a948d69","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"2047","applicableRelations":"0","notApplicableRelations":"2047","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":57076,"processMaxRssKiBAfter":69924,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":2162688,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":33},{"sampleIndex":4,"elapsedNs":"37050528","requestBytes":186378,"requestSha256":"b92c8700a4f6f6979e9476d56e57166618971aaecdbf4445ccd6aac124f821b7","outcomeBytes":186719,"outcomeSha256":"bf21a25ad92cfefad9c7116c16f918d97dab5e18e610a21482b26fdf5a948d69","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"2047","applicableRelations":"0","notApplicableRelations":"2047","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":57544,"processMaxRssKiBAfter":68648,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":2162688,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":33}]},{"scenarioId":"maximum-combined-not-applicable-envelope","shape":{"rawRelations":2047,"rawAdjacentEntries":0,"opaqueUtf8Bytes":65536,"canonicalRelations":2047,"applicableRelations":0,"applicableEdges":0},"samples":[{"sampleIndex":0,"elapsedNs":"27843880","requestBytes":542748,"requestSha256":"25ecc80095ed218f461bfe5283fa19d3e440eb603d49e8b65b65f5ec57a92ece","outcomeBytes":543093,"outcomeSha256":"dab7eda461b67a099559b2419b077c541991f62263f5e393322f278164835c33","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"2047","applicableRelations":"0","notApplicableRelations":"2047","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":60836,"processMaxRssKiBAfter":73804,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4194304,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":64},{"sampleIndex":1,"elapsedNs":"59693443","requestBytes":542748,"requestSha256":"25ecc80095ed218f461bfe5283fa19d3e440eb603d49e8b65b65f5ec57a92ece","outcomeBytes":543093,"outcomeSha256":"dab7eda461b67a099559b2419b077c541991f62263f5e393322f278164835c33","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"2047","applicableRelations":"0","notApplicableRelations":"2047","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":59700,"processMaxRssKiBAfter":74976,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4194304,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":64},{"sampleIndex":2,"elapsedNs":"27117204","requestBytes":542748,"requestSha256":"25ecc80095ed218f461bfe5283fa19d3e440eb603d49e8b65b65f5ec57a92ece","outcomeBytes":543093,"outcomeSha256":"dab7eda461b67a099559b2419b077c541991f62263f5e393322f278164835c33","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"2047","applicableRelations":"0","notApplicableRelations":"2047","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":58028,"processMaxRssKiBAfter":69676,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4194304,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":64},{"sampleIndex":3,"elapsedNs":"98384906","requestBytes":542748,"requestSha256":"25ecc80095ed218f461bfe5283fa19d3e440eb603d49e8b65b65f5ec57a92ece","outcomeBytes":543093,"outcomeSha256":"dab7eda461b67a099559b2419b077c541991f62263f5e393322f278164835c33","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"2047","applicableRelations":"0","notApplicableRelations":"2047","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":57900,"processMaxRssKiBAfter":70220,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4194304,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":64},{"sampleIndex":4,"elapsedNs":"37439595","requestBytes":542748,"requestSha256":"25ecc80095ed218f461bfe5283fa19d3e440eb603d49e8b65b65f5ec57a92ece","outcomeBytes":543093,"outcomeSha256":"dab7eda461b67a099559b2419b077c541991f62263f5e393322f278164835c33","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"2047","applicableRelations":"0","notApplicableRelations":"2047","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":60040,"processMaxRssKiBAfter":74420,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4194304,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":64}]},{"scenarioId":"maximum-combined-applicable-envelope","shape":{"rawRelations":2047,"rawAdjacentEntries":2047,"opaqueUtf8Bytes":65536,"canonicalRelations":2047,"applicableRelations":2047,"applicableEdges":2047},"samples":[{"sampleIndex":0,"elapsedNs":"142330702","requestBytes":657380,"requestSha256":"53bbc4fa41ce062a2056151b8e5f7ae68a8d81b122e6178bc919aa749b9898c3","outcomeBytes":847668,"outcomeSha256":"885a51a2136c2a09189bd5c88c35b886d343f99917a3c55f7231b2b37203e270","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"2047","applicableRelations":"2047","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":149,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":59196,"processMaxRssKiBAfter":72624,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4718592,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":72},{"sampleIndex":1,"elapsedNs":"154085935","requestBytes":657380,"requestSha256":"53bbc4fa41ce062a2056151b8e5f7ae68a8d81b122e6178bc919aa749b9898c3","outcomeBytes":847668,"outcomeSha256":"885a51a2136c2a09189bd5c88c35b886d343f99917a3c55f7231b2b37203e270","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"2047","applicableRelations":"2047","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":149,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":58892,"processMaxRssKiBAfter":73784,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4718592,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":72},{"sampleIndex":2,"elapsedNs":"121344687","requestBytes":657380,"requestSha256":"53bbc4fa41ce062a2056151b8e5f7ae68a8d81b122e6178bc919aa749b9898c3","outcomeBytes":847668,"outcomeSha256":"885a51a2136c2a09189bd5c88c35b886d343f99917a3c55f7231b2b37203e270","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"2047","applicableRelations":"2047","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":149,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":59200,"processMaxRssKiBAfter":72356,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4718592,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":72},{"sampleIndex":3,"elapsedNs":"122747833","requestBytes":657380,"requestSha256":"53bbc4fa41ce062a2056151b8e5f7ae68a8d81b122e6178bc919aa749b9898c3","outcomeBytes":847668,"outcomeSha256":"885a51a2136c2a09189bd5c88c35b886d343f99917a3c55f7231b2b37203e270","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"2047","applicableRelations":"2047","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":149,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":57372,"processMaxRssKiBAfter":72616,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4718592,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":72},{"sampleIndex":4,"elapsedNs":"125001524","requestBytes":657380,"requestSha256":"53bbc4fa41ce062a2056151b8e5f7ae68a8d81b122e6178bc919aa749b9898c3","outcomeBytes":847668,"outcomeSha256":"885a51a2136c2a09189bd5c88c35b886d343f99917a3c55f7231b2b37203e270","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"2047","applicableRelations":"2047","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":149,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":57484,"processMaxRssKiBAfter":70344,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4718592,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":72}]},{"scenarioId":"maximum-raw-duplicate-relations","shape":{"rawRelations":2047,"rawAdjacentEntries":2047,"opaqueUtf8Bytes":26611,"canonicalRelations":1,"applicableRelations":1,"applicableEdges":1},"samples":[{"sampleIndex":0,"elapsedNs":"10587180","requestBytes":270305,"requestSha256":"fc5f72ce71f8f9d81164dbe7632ac3345fca31c2eb677db9709c45bd7a04077a","outcomeBytes":4837,"outcomeSha256":"ef8e4cf8ac7580773571fa354b6b2eed38e4ca60d32780b0ba86e2be8cf6bcf2","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":57572,"processMaxRssKiBAfter":67528,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1769472,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":27},{"sampleIndex":1,"elapsedNs":"10756735","requestBytes":270305,"requestSha256":"fc5f72ce71f8f9d81164dbe7632ac3345fca31c2eb677db9709c45bd7a04077a","outcomeBytes":4837,"outcomeSha256":"ef8e4cf8ac7580773571fa354b6b2eed38e4ca60d32780b0ba86e2be8cf6bcf2","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":58236,"processMaxRssKiBAfter":65408,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1769472,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":27},{"sampleIndex":2,"elapsedNs":"14821536","requestBytes":270305,"requestSha256":"fc5f72ce71f8f9d81164dbe7632ac3345fca31c2eb677db9709c45bd7a04077a","outcomeBytes":4837,"outcomeSha256":"ef8e4cf8ac7580773571fa354b6b2eed38e4ca60d32780b0ba86e2be8cf6bcf2","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":56228,"processMaxRssKiBAfter":65624,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1769472,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":27},{"sampleIndex":3,"elapsedNs":"13961971","requestBytes":270305,"requestSha256":"fc5f72ce71f8f9d81164dbe7632ac3345fca31c2eb677db9709c45bd7a04077a","outcomeBytes":4837,"outcomeSha256":"ef8e4cf8ac7580773571fa354b6b2eed38e4ca60d32780b0ba86e2be8cf6bcf2","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":56072,"processMaxRssKiBAfter":67248,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1769472,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":27},{"sampleIndex":4,"elapsedNs":"27038746","requestBytes":270305,"requestSha256":"fc5f72ce71f8f9d81164dbe7632ac3345fca31c2eb677db9709c45bd7a04077a","outcomeBytes":4837,"outcomeSha256":"ef8e4cf8ac7580773571fa354b6b2eed38e4ca60d32780b0ba86e2be8cf6bcf2","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":56276,"processMaxRssKiBAfter":68532,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1769472,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":27}]},{"scenarioId":"maximum-raw-adjacent-duplicates","shape":{"rawRelations":1,"rawAdjacentEntries":2047,"opaqueUtf8Bytes":2,"canonicalRelations":1,"applicableRelations":1,"applicableEdges":1},"samples":[{"sampleIndex":0,"elapsedNs":"8530819","requestBytes":28866,"requestSha256":"495db8cf72c85e1811f87180cefb4a90c8d64be1277ad4ed41562a2e97034979","outcomeBytes":4823,"outcomeSha256":"a6196a5aaec38b74029c0d6f5016b9249008d7cd9544c3d5cf5de9932770e090","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":56116,"processMaxRssKiBAfter":62108,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1441792,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":22},{"sampleIndex":1,"elapsedNs":"5619217","requestBytes":28866,"requestSha256":"495db8cf72c85e1811f87180cefb4a90c8d64be1277ad4ed41562a2e97034979","outcomeBytes":4823,"outcomeSha256":"a6196a5aaec38b74029c0d6f5016b9249008d7cd9544c3d5cf5de9932770e090","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54096,"processMaxRssKiBAfter":58404,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1441792,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":22},{"sampleIndex":2,"elapsedNs":"5719256","requestBytes":28866,"requestSha256":"495db8cf72c85e1811f87180cefb4a90c8d64be1277ad4ed41562a2e97034979","outcomeBytes":4823,"outcomeSha256":"a6196a5aaec38b74029c0d6f5016b9249008d7cd9544c3d5cf5de9932770e090","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54348,"processMaxRssKiBAfter":60628,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1441792,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":22},{"sampleIndex":3,"elapsedNs":"9152595","requestBytes":28866,"requestSha256":"495db8cf72c85e1811f87180cefb4a90c8d64be1277ad4ed41562a2e97034979","outcomeBytes":4823,"outcomeSha256":"a6196a5aaec38b74029c0d6f5016b9249008d7cd9544c3d5cf5de9932770e090","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54624,"processMaxRssKiBAfter":60128,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1441792,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":22},{"sampleIndex":4,"elapsedNs":"24554589","requestBytes":28866,"requestSha256":"495db8cf72c85e1811f87180cefb4a90c8d64be1277ad4ed41562a2e97034979","outcomeBytes":4823,"outcomeSha256":"a6196a5aaec38b74029c0d6f5016b9249008d7cd9544c3d5cf5de9932770e090","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":55940,"processMaxRssKiBAfter":61612,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1441792,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":22}]},{"scenarioId":"transport-limit-plus-one","shape":{"rawRelations":2047,"rawAdjacentEntries":2047,"opaqueUtf8Bytes":65536,"canonicalRelations":2047,"applicableRelations":2047,"applicableEdges":2047},"samples":[{"sampleIndex":0,"elapsedNs":"1115563","requestBytes":657381,"requestSha256":"8569362aca144e7f9b4f83af9f9b91ef6287768b91afef3a2feb4d9d2e27606d","outcomeBytes":154,"outcomeSha256":"1a074b69790ebf713dcd65a76ddc630a75ce996ae01404a552583a2211b5e125","summary":{"outcome":"failure","source":"transport","code":"envelopeTooLarge","requestedBytes":"657381","limitBytes":"657380","proportionalFieldsPresent":false},"processMaxRssKiBBefore":91932,"processMaxRssKiBAfter":92240,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1245184,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":19},{"sampleIndex":1,"elapsedNs":"1125767","requestBytes":657381,"requestSha256":"8569362aca144e7f9b4f83af9f9b91ef6287768b91afef3a2feb4d9d2e27606d","outcomeBytes":154,"outcomeSha256":"1a074b69790ebf713dcd65a76ddc630a75ce996ae01404a552583a2211b5e125","summary":{"outcome":"failure","source":"transport","code":"envelopeTooLarge","requestedBytes":"657381","limitBytes":"657380","proportionalFieldsPresent":false},"processMaxRssKiBBefore":93336,"processMaxRssKiBAfter":93644,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1245184,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":19},{"sampleIndex":2,"elapsedNs":"1025406","requestBytes":657381,"requestSha256":"8569362aca144e7f9b4f83af9f9b91ef6287768b91afef3a2feb4d9d2e27606d","outcomeBytes":154,"outcomeSha256":"1a074b69790ebf713dcd65a76ddc630a75ce996ae01404a552583a2211b5e125","summary":{"outcome":"failure","source":"transport","code":"envelopeTooLarge","requestedBytes":"657381","limitBytes":"657380","proportionalFieldsPresent":false},"processMaxRssKiBBefore":92264,"processMaxRssKiBAfter":92444,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1245184,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":19},{"sampleIndex":3,"elapsedNs":"1062113","requestBytes":657381,"requestSha256":"8569362aca144e7f9b4f83af9f9b91ef6287768b91afef3a2feb4d9d2e27606d","outcomeBytes":154,"outcomeSha256":"1a074b69790ebf713dcd65a76ddc630a75ce996ae01404a552583a2211b5e125","summary":{"outcome":"failure","source":"transport","code":"envelopeTooLarge","requestedBytes":"657381","limitBytes":"657380","proportionalFieldsPresent":false},"processMaxRssKiBBefore":91584,"processMaxRssKiBAfter":91892,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1245184,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":19},{"sampleIndex":4,"elapsedNs":"1122203","requestBytes":657381,"requestSha256":"8569362aca144e7f9b4f83af9f9b91ef6287768b91afef3a2feb4d9d2e27606d","outcomeBytes":154,"outcomeSha256":"1a074b69790ebf713dcd65a76ddc630a75ce996ae01404a552583a2211b5e125","summary":{"outcome":"failure","source":"transport","code":"envelopeTooLarge","requestedBytes":"657381","limitBytes":"657380","proportionalFieldsPresent":false},"processMaxRssKiBBefore":93108,"processMaxRssKiBAfter":93416,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1245184,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":19}]}]} +{"schemaVersion":1,"artifactId":"wcag22-feasibility-wasm-whole-call-v1","claimBoundary":"canonical-wasm-package-root-whole-call-observations-only","claims":{"admission":"canonical-linux-x64-exact-wasm-only","hardGates":["completion","request-and-outcome-bytes","sha256-binding","terminal-algebra","packed-shape","candidate-major-lsb0-pack-oracle","no-proportional-dto"],"timingThresholdNs":null,"latency":"observation-only-no-production-threshold","memory":"process maxRSS is total-process high-water including V8; post-call WASM pages are linear-memory high-water observations; neither is total operation memory"},"environment":{"execution":"fresh-node-child-process-per-sample","platform":"linux-x64","nodeVersion":"v24.14.0","sampleCount":5,"requestConstructionMeasured":false,"timer":"process.hrtime.bigint","packageRootApi":"packages/colors/index.js","canonicalCandidate":true,"rustToolchain":"1.96.0","wasmPack":"0.13.1","wasmBindgen":"0.2.126","target":"wasm32-unknown-unknown","cargoProfile":"release","wasmOpt":"-Oz"},"bindings":{"coreAdmission":{"path":"crates/labcolors-core/contracts/wcag22-feasibility-benchmark-v1.json","schemaVersion":1,"artifactId":"wcag22-feasibility-admission-raw-v1","profileId":"compile-v1","sha256":"7e9ffcbdd9d5d50fe681f511c34fc5c5dd270e9c475ce23ae56e9776922a3c5e"},"packOracle":{"path":"conformance/vectors/wcag22-feasibility.json","sha256":"ae2caec47a7b650e73b8d4029a69b4e401dfb7cc199db579c0f95106eebe8dc3","caseId":"text-default-seven","requestSha256":"8da46d4cb835c4e3c89b8f56ec4025457e0e4308ed28adcfa7beb134858e3d6f","outcomeSha256":"9dbe5499abfd07cd915da6da0ca1578278aebc33b0f24c41c6318858026ff442","manifestPath":"conformance/vectors/manifest.json","manifestSha256":"6b873967ba648b4ec160fd3e909a3359d6ffcb0f129bf38711aa74f3dff5a63d","packVersion":"5.0.0","packDigest":"48d00cd5"},"runtimeSources":{"harness":{"path":"packages/colors/bench/wcag22-feasibility-boundary.bench.mjs","sha256":"edad6ca3e5f5361c176831cbdb5b6caec0a8c552d21ac7937c8aa86480452f49"},"packageManifest":{"path":"packages/colors/package.json","sha256":"a3fa15d4ee232abb8ff392a47bd1b2768198255106e9435da2ab2ef0777c9fdf"},"packageRoot":{"path":"packages/colors/index.js","sha256":"5b414bfc3abca90996003d30a87d868b652dc298dd0deec3c1110a746fe3f833"},"wasmGlue":{"path":"packages/colors/pkg/labcolors.js","sha256":"b9933eff570f8120726341933dc765efa0c2cba7d37a83cef83f3bd1f13db9d7"},"adaptTheme":{"path":"packages/colors/adapt-theme.js","sha256":"5629566a0dce766c7101852a2b19b58066916427db11b7844b29b3b45133b06a"},"applyTheme":{"path":"packages/colors/apply-theme.js","sha256":"36a4c598042cd2be8579ef7a315ee598a25954c4e990aeec6382177bab53a76a"},"effectiveBackground":{"path":"packages/colors/effective-bg.js","sha256":"03cf758df0cdb34c840bedfab8a996eb44a8ad9b603bf241c3a8a9f88b359460"},"watchTheme":{"path":"packages/colors/watch-theme.js","sha256":"f0287092f1ed4d413886b2650e930afcb527ecb3ad6536dbe548347d12159ebd"}},"wasmToolchain":{"path":"packages/colors/bench/wasm-size-budget-v1.json","schemaVersion":2,"budgetId":"labcolors-wasm-raw-issue-284-v1","recipeSha256":"0ea74cb070e0a5facb7280f6124930a0bb673ee4dcee9c99fff110db6c9389d4"},"wasm":{"path":"packages/colors/pkg/labcolors_bg.wasm","bytes":521231,"sha256":"779379e914909ff1ddbb5afdd6554d026b586f3c71ef6b2cfeba3468bf93e029"}},"limits":{"maxRequestBytes":657380,"profileId":"compile-v1","rawRelations":2047,"rawAdjacentEntries":2047,"opaqueUtf8Bytes":65536,"canonicalRelations":2047,"applicableEdges":2047,"logicalAssessments":524032,"packedResultBytes":65536},"scenarios":[{"scenarioId":"minimum-evaluated","shape":{"rawRelations":1,"rawAdjacentEntries":1,"opaqueUtf8Bytes":2,"canonicalRelations":1,"applicableRelations":1,"applicableEdges":1},"samples":[{"sampleIndex":0,"elapsedNs":"3974236","requestBytes":222,"requestSha256":"21ff2cc1a9b3da86e633f999a5c8417f0be059c499fc9114490e274b319d2ee7","outcomeBytes":4823,"outcomeSha256":"a6196a5aaec38b74029c0d6f5016b9249008d7cd9544c3d5cf5de9932770e090","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"lsb0PackOracleMatches":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":53964,"processMaxRssKiBAfter":55260,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1245184,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":19},{"sampleIndex":1,"elapsedNs":"4804078","requestBytes":222,"requestSha256":"21ff2cc1a9b3da86e633f999a5c8417f0be059c499fc9114490e274b319d2ee7","outcomeBytes":4823,"outcomeSha256":"a6196a5aaec38b74029c0d6f5016b9249008d7cd9544c3d5cf5de9932770e090","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"lsb0PackOracleMatches":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":53972,"processMaxRssKiBAfter":55252,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1245184,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":19},{"sampleIndex":2,"elapsedNs":"4171112","requestBytes":222,"requestSha256":"21ff2cc1a9b3da86e633f999a5c8417f0be059c499fc9114490e274b319d2ee7","outcomeBytes":4823,"outcomeSha256":"a6196a5aaec38b74029c0d6f5016b9249008d7cd9544c3d5cf5de9932770e090","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"lsb0PackOracleMatches":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54148,"processMaxRssKiBAfter":55352,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1245184,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":19},{"sampleIndex":3,"elapsedNs":"4328940","requestBytes":222,"requestSha256":"21ff2cc1a9b3da86e633f999a5c8417f0be059c499fc9114490e274b319d2ee7","outcomeBytes":4823,"outcomeSha256":"a6196a5aaec38b74029c0d6f5016b9249008d7cd9544c3d5cf5de9932770e090","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"lsb0PackOracleMatches":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54132,"processMaxRssKiBAfter":55284,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1245184,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":19},{"sampleIndex":4,"elapsedNs":"4119654","requestBytes":222,"requestSha256":"21ff2cc1a9b3da86e633f999a5c8417f0be059c499fc9114490e274b319d2ee7","outcomeBytes":4823,"outcomeSha256":"a6196a5aaec38b74029c0d6f5016b9249008d7cd9544c3d5cf5de9932770e090","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"lsb0PackOracleMatches":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":53976,"processMaxRssKiBAfter":55180,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1245184,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":19}]},{"scenarioId":"maximum-canonical-applicable-relations","shape":{"rawRelations":2047,"rawAdjacentEntries":2047,"opaqueUtf8Bytes":20470,"canonicalRelations":2047,"applicableRelations":2047,"applicableEdges":2047},"samples":[{"sampleIndex":0,"elapsedNs":"109786904","requestBytes":264164,"requestSha256":"83955d22987a29e4df0c91647cecadcb3a7e7f5bc06010e0b05519fac4b938d2","outcomeBytes":527090,"outcomeSha256":"dedc563b2a3f7bee7a0f37c5976af853814ac4e9ce12f27efaa3ec8661a79b1c","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"2047","applicableRelations":"2047","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":56456,"processMaxRssKiBAfter":69472,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4259840,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":65},{"sampleIndex":1,"elapsedNs":"109448884","requestBytes":264164,"requestSha256":"83955d22987a29e4df0c91647cecadcb3a7e7f5bc06010e0b05519fac4b938d2","outcomeBytes":527090,"outcomeSha256":"dedc563b2a3f7bee7a0f37c5976af853814ac4e9ce12f27efaa3ec8661a79b1c","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"2047","applicableRelations":"2047","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":56284,"processMaxRssKiBAfter":68504,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4259840,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":65},{"sampleIndex":2,"elapsedNs":"112698857","requestBytes":264164,"requestSha256":"83955d22987a29e4df0c91647cecadcb3a7e7f5bc06010e0b05519fac4b938d2","outcomeBytes":527090,"outcomeSha256":"dedc563b2a3f7bee7a0f37c5976af853814ac4e9ce12f27efaa3ec8661a79b1c","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"2047","applicableRelations":"2047","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":56340,"processMaxRssKiBAfter":69288,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4259840,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":65},{"sampleIndex":3,"elapsedNs":"104241669","requestBytes":264164,"requestSha256":"83955d22987a29e4df0c91647cecadcb3a7e7f5bc06010e0b05519fac4b938d2","outcomeBytes":527090,"outcomeSha256":"dedc563b2a3f7bee7a0f37c5976af853814ac4e9ce12f27efaa3ec8661a79b1c","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"2047","applicableRelations":"2047","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":56184,"processMaxRssKiBAfter":68772,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4259840,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":65},{"sampleIndex":4,"elapsedNs":"102893273","requestBytes":264164,"requestSha256":"83955d22987a29e4df0c91647cecadcb3a7e7f5bc06010e0b05519fac4b938d2","outcomeBytes":527090,"outcomeSha256":"dedc563b2a3f7bee7a0f37c5976af853814ac4e9ce12f27efaa3ec8661a79b1c","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"2047","applicableRelations":"2047","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":56448,"processMaxRssKiBAfter":70108,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4259840,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":65}]},{"scenarioId":"maximum-applicable-edges","shape":{"rawRelations":1,"rawAdjacentEntries":2047,"opaqueUtf8Bytes":19,"canonicalRelations":1,"applicableRelations":1,"applicableEdges":2047},"samples":[{"sampleIndex":0,"elapsedNs":"99365844","requestBytes":23909,"requestSha256":"9a60a035743121bc4b79660f3a76f75ff1d3da2c5b3a243449442f972e8af9ae","outcomeBytes":269795,"outcomeSha256":"7f08a3d65184a40adc8de04edc352511130fe6fc8e223729996a11cd18bc622c","summary":{"outcome":"success","terminal":"infeasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":0,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54484,"processMaxRssKiBAfter":64228,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":2228224,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":34},{"sampleIndex":1,"elapsedNs":"112198523","requestBytes":23909,"requestSha256":"9a60a035743121bc4b79660f3a76f75ff1d3da2c5b3a243449442f972e8af9ae","outcomeBytes":269795,"outcomeSha256":"7f08a3d65184a40adc8de04edc352511130fe6fc8e223729996a11cd18bc622c","summary":{"outcome":"success","terminal":"infeasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":0,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54380,"processMaxRssKiBAfter":64320,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":2228224,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":34},{"sampleIndex":2,"elapsedNs":"97002688","requestBytes":23909,"requestSha256":"9a60a035743121bc4b79660f3a76f75ff1d3da2c5b3a243449442f972e8af9ae","outcomeBytes":269795,"outcomeSha256":"7f08a3d65184a40adc8de04edc352511130fe6fc8e223729996a11cd18bc622c","summary":{"outcome":"success","terminal":"infeasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":0,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54452,"processMaxRssKiBAfter":64116,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":2228224,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":34},{"sampleIndex":3,"elapsedNs":"97208399","requestBytes":23909,"requestSha256":"9a60a035743121bc4b79660f3a76f75ff1d3da2c5b3a243449442f972e8af9ae","outcomeBytes":269795,"outcomeSha256":"7f08a3d65184a40adc8de04edc352511130fe6fc8e223729996a11cd18bc622c","summary":{"outcome":"success","terminal":"infeasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":0,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54252,"processMaxRssKiBAfter":64008,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":2228224,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":34},{"sampleIndex":4,"elapsedNs":"119764356","requestBytes":23909,"requestSha256":"9a60a035743121bc4b79660f3a76f75ff1d3da2c5b3a243449442f972e8af9ae","outcomeBytes":269795,"outcomeSha256":"7f08a3d65184a40adc8de04edc352511130fe6fc8e223729996a11cd18bc622c","summary":{"outcome":"success","terminal":"infeasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":0,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54068,"processMaxRssKiBAfter":64672,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":2228224,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":34}]},{"scenarioId":"maximum-opaque-utf8-bytes","shape":{"rawRelations":1,"rawAdjacentEntries":0,"opaqueUtf8Bytes":65536,"canonicalRelations":1,"applicableRelations":0,"applicableEdges":0},"samples":[{"sampleIndex":0,"elapsedNs":"6466826","requestBytes":65710,"requestSha256":"4c2106974c30967456ac4d92bced32418cb0cb9b8f9ba97e53f19e0f629cf1f2","outcomeBytes":66050,"outcomeSha256":"4651c1ed3d6d43ef8d0eeaf78bb745d4b58ba7d160877df4a23433b233869f18","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"1","applicableRelations":"0","notApplicableRelations":"1","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54312,"processMaxRssKiBAfter":59484,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1572864,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":24},{"sampleIndex":1,"elapsedNs":"7306562","requestBytes":65710,"requestSha256":"4c2106974c30967456ac4d92bced32418cb0cb9b8f9ba97e53f19e0f629cf1f2","outcomeBytes":66050,"outcomeSha256":"4651c1ed3d6d43ef8d0eeaf78bb745d4b58ba7d160877df4a23433b233869f18","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"1","applicableRelations":"0","notApplicableRelations":"1","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54208,"processMaxRssKiBAfter":60200,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1572864,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":24},{"sampleIndex":2,"elapsedNs":"6160755","requestBytes":65710,"requestSha256":"4c2106974c30967456ac4d92bced32418cb0cb9b8f9ba97e53f19e0f629cf1f2","outcomeBytes":66050,"outcomeSha256":"4651c1ed3d6d43ef8d0eeaf78bb745d4b58ba7d160877df4a23433b233869f18","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"1","applicableRelations":"0","notApplicableRelations":"1","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54108,"processMaxRssKiBAfter":59476,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1572864,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":24},{"sampleIndex":3,"elapsedNs":"6657484","requestBytes":65710,"requestSha256":"4c2106974c30967456ac4d92bced32418cb0cb9b8f9ba97e53f19e0f629cf1f2","outcomeBytes":66050,"outcomeSha256":"4651c1ed3d6d43ef8d0eeaf78bb745d4b58ba7d160877df4a23433b233869f18","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"1","applicableRelations":"0","notApplicableRelations":"1","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54304,"processMaxRssKiBAfter":59556,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1572864,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":24},{"sampleIndex":4,"elapsedNs":"7697833","requestBytes":65710,"requestSha256":"4c2106974c30967456ac4d92bced32418cb0cb9b8f9ba97e53f19e0f629cf1f2","outcomeBytes":66050,"outcomeSha256":"4651c1ed3d6d43ef8d0eeaf78bb745d4b58ba7d160877df4a23433b233869f18","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"1","applicableRelations":"0","notApplicableRelations":"1","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54424,"processMaxRssKiBAfter":59608,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1572864,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":24}]},{"scenarioId":"maximum-canonical-not-applicable-relations","shape":{"rawRelations":2047,"rawAdjacentEntries":0,"opaqueUtf8Bytes":36846,"canonicalRelations":2047,"applicableRelations":0,"applicableEdges":0},"samples":[{"sampleIndex":0,"elapsedNs":"19630305","requestBytes":186378,"requestSha256":"b92c8700a4f6f6979e9476d56e57166618971aaecdbf4445ccd6aac124f821b7","outcomeBytes":186719,"outcomeSha256":"bf21a25ad92cfefad9c7116c16f918d97dab5e18e610a21482b26fdf5a948d69","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"2047","applicableRelations":"0","notApplicableRelations":"2047","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":57388,"processMaxRssKiBAfter":69816,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":2162688,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":33},{"sampleIndex":1,"elapsedNs":"16465449","requestBytes":186378,"requestSha256":"b92c8700a4f6f6979e9476d56e57166618971aaecdbf4445ccd6aac124f821b7","outcomeBytes":186719,"outcomeSha256":"bf21a25ad92cfefad9c7116c16f918d97dab5e18e610a21482b26fdf5a948d69","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"2047","applicableRelations":"0","notApplicableRelations":"2047","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":57240,"processMaxRssKiBAfter":69228,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":2162688,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":33},{"sampleIndex":2,"elapsedNs":"20561408","requestBytes":186378,"requestSha256":"b92c8700a4f6f6979e9476d56e57166618971aaecdbf4445ccd6aac124f821b7","outcomeBytes":186719,"outcomeSha256":"bf21a25ad92cfefad9c7116c16f918d97dab5e18e610a21482b26fdf5a948d69","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"2047","applicableRelations":"0","notApplicableRelations":"2047","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":57024,"processMaxRssKiBAfter":69316,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":2162688,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":33},{"sampleIndex":3,"elapsedNs":"19390302","requestBytes":186378,"requestSha256":"b92c8700a4f6f6979e9476d56e57166618971aaecdbf4445ccd6aac124f821b7","outcomeBytes":186719,"outcomeSha256":"bf21a25ad92cfefad9c7116c16f918d97dab5e18e610a21482b26fdf5a948d69","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"2047","applicableRelations":"0","notApplicableRelations":"2047","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":57312,"processMaxRssKiBAfter":70128,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":2162688,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":33},{"sampleIndex":4,"elapsedNs":"22630861","requestBytes":186378,"requestSha256":"b92c8700a4f6f6979e9476d56e57166618971aaecdbf4445ccd6aac124f821b7","outcomeBytes":186719,"outcomeSha256":"bf21a25ad92cfefad9c7116c16f918d97dab5e18e610a21482b26fdf5a948d69","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"2047","applicableRelations":"0","notApplicableRelations":"2047","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":57260,"processMaxRssKiBAfter":69364,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":2162688,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":33}]},{"scenarioId":"maximum-combined-not-applicable-envelope","shape":{"rawRelations":2047,"rawAdjacentEntries":0,"opaqueUtf8Bytes":65536,"canonicalRelations":2047,"applicableRelations":0,"applicableEdges":0},"samples":[{"sampleIndex":0,"elapsedNs":"43578153","requestBytes":542748,"requestSha256":"25ecc80095ed218f461bfe5283fa19d3e440eb603d49e8b65b65f5ec57a92ece","outcomeBytes":543093,"outcomeSha256":"dab7eda461b67a099559b2419b077c541991f62263f5e393322f278164835c33","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"2047","applicableRelations":"0","notApplicableRelations":"2047","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":57820,"processMaxRssKiBAfter":73028,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4194304,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":64},{"sampleIndex":1,"elapsedNs":"35495839","requestBytes":542748,"requestSha256":"25ecc80095ed218f461bfe5283fa19d3e440eb603d49e8b65b65f5ec57a92ece","outcomeBytes":543093,"outcomeSha256":"dab7eda461b67a099559b2419b077c541991f62263f5e393322f278164835c33","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"2047","applicableRelations":"0","notApplicableRelations":"2047","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":58168,"processMaxRssKiBAfter":72196,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4194304,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":64},{"sampleIndex":2,"elapsedNs":"24821665","requestBytes":542748,"requestSha256":"25ecc80095ed218f461bfe5283fa19d3e440eb603d49e8b65b65f5ec57a92ece","outcomeBytes":543093,"outcomeSha256":"dab7eda461b67a099559b2419b077c541991f62263f5e393322f278164835c33","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"2047","applicableRelations":"0","notApplicableRelations":"2047","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":57952,"processMaxRssKiBAfter":72404,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4194304,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":64},{"sampleIndex":3,"elapsedNs":"27304031","requestBytes":542748,"requestSha256":"25ecc80095ed218f461bfe5283fa19d3e440eb603d49e8b65b65f5ec57a92ece","outcomeBytes":543093,"outcomeSha256":"dab7eda461b67a099559b2419b077c541991f62263f5e393322f278164835c33","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"2047","applicableRelations":"0","notApplicableRelations":"2047","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":58264,"processMaxRssKiBAfter":71068,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4194304,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":64},{"sampleIndex":4,"elapsedNs":"22711272","requestBytes":542748,"requestSha256":"25ecc80095ed218f461bfe5283fa19d3e440eb603d49e8b65b65f5ec57a92ece","outcomeBytes":543093,"outcomeSha256":"dab7eda461b67a099559b2419b077c541991f62263f5e393322f278164835c33","summary":{"outcome":"success","terminal":"notEvaluated","canonicalRelations":"2047","applicableRelations":"0","notApplicableRelations":"2047","numericalEvidencePresent":false,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":57900,"processMaxRssKiBAfter":73164,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4194304,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":64}]},{"scenarioId":"maximum-combined-applicable-envelope","shape":{"rawRelations":2047,"rawAdjacentEntries":2047,"opaqueUtf8Bytes":65536,"canonicalRelations":2047,"applicableRelations":2047,"applicableEdges":2047},"samples":[{"sampleIndex":0,"elapsedNs":"118431576","requestBytes":657380,"requestSha256":"53bbc4fa41ce062a2056151b8e5f7ae68a8d81b122e6178bc919aa749b9898c3","outcomeBytes":847668,"outcomeSha256":"885a51a2136c2a09189bd5c88c35b886d343f99917a3c55f7231b2b37203e270","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"2047","applicableRelations":"2047","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":149,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":57364,"processMaxRssKiBAfter":71868,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4718592,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":72},{"sampleIndex":1,"elapsedNs":"120646527","requestBytes":657380,"requestSha256":"53bbc4fa41ce062a2056151b8e5f7ae68a8d81b122e6178bc919aa749b9898c3","outcomeBytes":847668,"outcomeSha256":"885a51a2136c2a09189bd5c88c35b886d343f99917a3c55f7231b2b37203e270","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"2047","applicableRelations":"2047","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":149,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":57368,"processMaxRssKiBAfter":70680,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4718592,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":72},{"sampleIndex":2,"elapsedNs":"111036380","requestBytes":657380,"requestSha256":"53bbc4fa41ce062a2056151b8e5f7ae68a8d81b122e6178bc919aa749b9898c3","outcomeBytes":847668,"outcomeSha256":"885a51a2136c2a09189bd5c88c35b886d343f99917a3c55f7231b2b37203e270","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"2047","applicableRelations":"2047","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":149,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":57312,"processMaxRssKiBAfter":75592,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4718592,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":72},{"sampleIndex":3,"elapsedNs":"111265285","requestBytes":657380,"requestSha256":"53bbc4fa41ce062a2056151b8e5f7ae68a8d81b122e6178bc919aa749b9898c3","outcomeBytes":847668,"outcomeSha256":"885a51a2136c2a09189bd5c88c35b886d343f99917a3c55f7231b2b37203e270","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"2047","applicableRelations":"2047","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":149,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":57236,"processMaxRssKiBAfter":75408,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4718592,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":72},{"sampleIndex":4,"elapsedNs":"110401442","requestBytes":657380,"requestSha256":"53bbc4fa41ce062a2056151b8e5f7ae68a8d81b122e6178bc919aa749b9898c3","outcomeBytes":847668,"outcomeSha256":"885a51a2136c2a09189bd5c88c35b886d343f99917a3c55f7231b2b37203e270","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"2047","applicableRelations":"2047","notApplicableRelations":"0","applicableEdges":"2047","logicalAssessments":"524032","failureMatrixBytes":65504,"partitionBytes":32,"feasibleCandidates":149,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":57120,"processMaxRssKiBAfter":75828,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":4718592,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":72}]},{"scenarioId":"maximum-raw-duplicate-relations","shape":{"rawRelations":2047,"rawAdjacentEntries":2047,"opaqueUtf8Bytes":26611,"canonicalRelations":1,"applicableRelations":1,"applicableEdges":1},"samples":[{"sampleIndex":0,"elapsedNs":"11337773","requestBytes":270305,"requestSha256":"fc5f72ce71f8f9d81164dbe7632ac3345fca31c2eb677db9709c45bd7a04077a","outcomeBytes":4837,"outcomeSha256":"ef8e4cf8ac7580773571fa354b6b2eed38e4ca60d32780b0ba86e2be8cf6bcf2","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":55696,"processMaxRssKiBAfter":66240,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1769472,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":27},{"sampleIndex":1,"elapsedNs":"14548387","requestBytes":270305,"requestSha256":"fc5f72ce71f8f9d81164dbe7632ac3345fca31c2eb677db9709c45bd7a04077a","outcomeBytes":4837,"outcomeSha256":"ef8e4cf8ac7580773571fa354b6b2eed38e4ca60d32780b0ba86e2be8cf6bcf2","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":55724,"processMaxRssKiBAfter":67228,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1769472,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":27},{"sampleIndex":2,"elapsedNs":"9878753","requestBytes":270305,"requestSha256":"fc5f72ce71f8f9d81164dbe7632ac3345fca31c2eb677db9709c45bd7a04077a","outcomeBytes":4837,"outcomeSha256":"ef8e4cf8ac7580773571fa354b6b2eed38e4ca60d32780b0ba86e2be8cf6bcf2","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":56044,"processMaxRssKiBAfter":67012,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1769472,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":27},{"sampleIndex":3,"elapsedNs":"14595007","requestBytes":270305,"requestSha256":"fc5f72ce71f8f9d81164dbe7632ac3345fca31c2eb677db9709c45bd7a04077a","outcomeBytes":4837,"outcomeSha256":"ef8e4cf8ac7580773571fa354b6b2eed38e4ca60d32780b0ba86e2be8cf6bcf2","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":56024,"processMaxRssKiBAfter":66448,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1769472,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":27},{"sampleIndex":4,"elapsedNs":"15706765","requestBytes":270305,"requestSha256":"fc5f72ce71f8f9d81164dbe7632ac3345fca31c2eb677db9709c45bd7a04077a","outcomeBytes":4837,"outcomeSha256":"ef8e4cf8ac7580773571fa354b6b2eed38e4ca60d32780b0ba86e2be8cf6bcf2","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":55764,"processMaxRssKiBAfter":66792,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1769472,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":27}]},{"scenarioId":"maximum-raw-adjacent-duplicates","shape":{"rawRelations":1,"rawAdjacentEntries":2047,"opaqueUtf8Bytes":2,"canonicalRelations":1,"applicableRelations":1,"applicableEdges":1},"samples":[{"sampleIndex":0,"elapsedNs":"5272693","requestBytes":28866,"requestSha256":"495db8cf72c85e1811f87180cefb4a90c8d64be1277ad4ed41562a2e97034979","outcomeBytes":4823,"outcomeSha256":"a6196a5aaec38b74029c0d6f5016b9249008d7cd9544c3d5cf5de9932770e090","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54100,"processMaxRssKiBAfter":60016,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1441792,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":22},{"sampleIndex":1,"elapsedNs":"7825707","requestBytes":28866,"requestSha256":"495db8cf72c85e1811f87180cefb4a90c8d64be1277ad4ed41562a2e97034979","outcomeBytes":4823,"outcomeSha256":"a6196a5aaec38b74029c0d6f5016b9249008d7cd9544c3d5cf5de9932770e090","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":53856,"processMaxRssKiBAfter":60468,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1441792,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":22},{"sampleIndex":2,"elapsedNs":"5252975","requestBytes":28866,"requestSha256":"495db8cf72c85e1811f87180cefb4a90c8d64be1277ad4ed41562a2e97034979","outcomeBytes":4823,"outcomeSha256":"a6196a5aaec38b74029c0d6f5016b9249008d7cd9544c3d5cf5de9932770e090","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54268,"processMaxRssKiBAfter":60460,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1441792,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":22},{"sampleIndex":3,"elapsedNs":"6113653","requestBytes":28866,"requestSha256":"495db8cf72c85e1811f87180cefb4a90c8d64be1277ad4ed41562a2e97034979","outcomeBytes":4823,"outcomeSha256":"a6196a5aaec38b74029c0d6f5016b9249008d7cd9544c3d5cf5de9932770e090","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54064,"processMaxRssKiBAfter":60116,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1441792,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":22},{"sampleIndex":4,"elapsedNs":"5927413","requestBytes":28866,"requestSha256":"495db8cf72c85e1811f87180cefb4a90c8d64be1277ad4ed41562a2e97034979","outcomeBytes":4823,"outcomeSha256":"a6196a5aaec38b74029c0d6f5016b9249008d7cd9544c3d5cf5de9932770e090","summary":{"outcome":"success","terminal":"feasible","domainCount":"256","canonicalRelations":"1","applicableRelations":"1","notApplicableRelations":"0","applicableEdges":"1","logicalAssessments":"256","failureMatrixBytes":32,"partitionBytes":32,"feasibleCandidates":7,"lsb0PartitionMatchesMatrix":true,"proportionalFieldsPresent":false},"processMaxRssKiBBefore":54116,"processMaxRssKiBAfter":60196,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1441792,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":22}]},{"scenarioId":"transport-limit-plus-one","shape":{"rawRelations":2047,"rawAdjacentEntries":2047,"opaqueUtf8Bytes":65536,"canonicalRelations":2047,"applicableRelations":2047,"applicableEdges":2047},"samples":[{"sampleIndex":0,"elapsedNs":"1583330","requestBytes":657381,"requestSha256":"8569362aca144e7f9b4f83af9f9b91ef6287768b91afef3a2feb4d9d2e27606d","outcomeBytes":154,"outcomeSha256":"1a074b69790ebf713dcd65a76ddc630a75ce996ae01404a552583a2211b5e125","summary":{"outcome":"failure","source":"transport","code":"envelopeTooLarge","requestedBytes":"657381","limitBytes":"657380","proportionalFieldsPresent":false},"processMaxRssKiBBefore":92044,"processMaxRssKiBAfter":92224,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1245184,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":19},{"sampleIndex":1,"elapsedNs":"1289926","requestBytes":657381,"requestSha256":"8569362aca144e7f9b4f83af9f9b91ef6287768b91afef3a2feb4d9d2e27606d","outcomeBytes":154,"outcomeSha256":"1a074b69790ebf713dcd65a76ddc630a75ce996ae01404a552583a2211b5e125","summary":{"outcome":"failure","source":"transport","code":"envelopeTooLarge","requestedBytes":"657381","limitBytes":"657380","proportionalFieldsPresent":false},"processMaxRssKiBBefore":92024,"processMaxRssKiBAfter":92332,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1245184,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":19},{"sampleIndex":2,"elapsedNs":"1223507","requestBytes":657381,"requestSha256":"8569362aca144e7f9b4f83af9f9b91ef6287768b91afef3a2feb4d9d2e27606d","outcomeBytes":154,"outcomeSha256":"1a074b69790ebf713dcd65a76ddc630a75ce996ae01404a552583a2211b5e125","summary":{"outcome":"failure","source":"transport","code":"envelopeTooLarge","requestedBytes":"657381","limitBytes":"657380","proportionalFieldsPresent":false},"processMaxRssKiBBefore":91784,"processMaxRssKiBAfter":92092,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1245184,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":19},{"sampleIndex":3,"elapsedNs":"1855049","requestBytes":657381,"requestSha256":"8569362aca144e7f9b4f83af9f9b91ef6287768b91afef3a2feb4d9d2e27606d","outcomeBytes":154,"outcomeSha256":"1a074b69790ebf713dcd65a76ddc630a75ce996ae01404a552583a2211b5e125","summary":{"outcome":"failure","source":"transport","code":"envelopeTooLarge","requestedBytes":"657381","limitBytes":"657380","proportionalFieldsPresent":false},"processMaxRssKiBBefore":91896,"processMaxRssKiBAfter":92204,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1245184,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":19},{"sampleIndex":4,"elapsedNs":"1257968","requestBytes":657381,"requestSha256":"8569362aca144e7f9b4f83af9f9b91ef6287768b91afef3a2feb4d9d2e27606d","outcomeBytes":154,"outcomeSha256":"1a074b69790ebf713dcd65a76ddc630a75ce996ae01404a552583a2211b5e125","summary":{"outcome":"failure","source":"transport","code":"envelopeTooLarge","requestedBytes":"657381","limitBytes":"657380","proportionalFieldsPresent":false},"processMaxRssKiBBefore":92164,"processMaxRssKiBAfter":92344,"wasmMemoryBytesBefore":1179648,"wasmMemoryBytesAfter":1245184,"wasmMemoryPagesBefore":18,"wasmMemoryPagesAfter":19}]}]} diff --git a/packages/colors/test/release-contract.test.mjs b/packages/colors/test/release-contract.test.mjs index 2c5e07a1..384206ba 100644 --- a/packages/colors/test/release-contract.test.mjs +++ b/packages/colors/test/release-contract.test.mjs @@ -121,7 +121,7 @@ test("every workspace package inherits the declared MSRV", () => { } }); -test("WCAG22 feasibility has one protocol feature owner shared by every boundary consumer", () => { +test("WCAG22 feasibility projects only the registered-domain capability through transports", () => { const isolatedCoreEdge = /labcolors-core = \{ path = "\.\.\/labcolors-core", default-features = false \}/u; const protocolEdge = /labcolors-protocol = \{ path = "\.\.\/labcolors-protocol" \}/u; @@ -158,6 +158,10 @@ test("WCAG22 feasibility has one protocol feature owner shared by every boundary 1, "the dependency and feature-tree checks must share one consumer loop", ); + assert.match( + projection, + /core\["features"\]\.get\("default"\) != \[\n\s+"wcag22-feasibility",\n\s+"wcag22-explicit-feasibility",\n\s*\]:/u, + ); assert.match(projection, /protocol_core\["features"\] != \["wcag22-feasibility"\]/u); assert.match(projection, /core_dependency\["features"\]/u); assert.match(projection, /dependency\["name"\] == "labcolors-protocol"/u); @@ -165,6 +169,10 @@ test("WCAG22 feasibility has one protocol feature owner shared by every boundary projection, /\["cargo", "tree", "-p", consumer, "--edges", "normal", "-e", "features"\]/u, ); + assert.match( + projection, + /'labcolors-core feature "wcag22-explicit-feasibility"' in feature_tree/u, + ); assert.doesNotMatch(projection, /for consumer in labcolors-/u); }); @@ -980,9 +988,10 @@ test("release evidence carries the versioned WCAG22 feasibility operation", () = assert.match(verifier, /case "incompatibleCoreContract"/u); }); -test("WCAG22 WASM budget v2 is exact, immutable, and acyclic", async () => { +test("WCAG22 WASM budget history is exact, append-only, and acyclic", async () => { const v1Path = join(root, "packages", "colors", "bench", "wasm-size-budget-v1.json"); const v2Path = join(root, "packages", "colors", "bench", "wasm-size-budget-v2.json"); + const v3Path = join(root, "packages", "colors", "bench", "wasm-size-budget-v3.json"); const checkerPath = join(root, "scripts", "check-wasm-size-budget.mjs"); const sha256 = (value) => createHash("sha256").update(value).digest("hex"); const canonicalJson = (value) => `${JSON.stringify(value, null, 2)}\n`; @@ -1060,13 +1069,42 @@ test("WCAG22 WASM budget v2 is exact, immutable, and acyclic", async () => { gzip: "diagnostic-only", }); + const v3Bytes = readFileSync(v3Path); + const v3 = JSON.parse(v3Bytes); + assert.equal(v3Bytes.toString("utf8"), canonicalJson(v3)); + assert.equal( + sha256(v3Bytes), + "d7937612e4c33574a8af28845bb1dd30cca86fc39fc0206cac4c377de77fec15", + "the admitted v3 document must be byte-immutable", + ); + assert.deepEqual(Object.keys(v3), Object.keys(v2)); + assert.deepEqual(Object.keys(v3.buildRecipe), Object.keys(v2.buildRecipe)); + assert.deepEqual(Object.keys(v3.measurement), Object.keys(v2.measurement)); + assert.deepEqual(Object.keys(v3.policy), Object.keys(v2.policy)); + assert.equal(v3.schemaVersion, 3); + assert.equal(v3.budgetId, "labcolors-wasm-raw-issue-296-v3"); + assert.equal(v3.artifact, "packages/colors/pkg/labcolors_bg.wasm"); + assert.deepEqual(v3.buildRecipe, v2.buildRecipe); + assert.deepEqual(v3.measurement, { + issue: 296, + measurementPlatform: "linux-x64", + rawBytes: 521231, + sha256: "779379e914909ff1ddbb5afdd6554d026b586f3c71ef6b2cfeba3468bf93e029", + }); + assert.deepEqual(v3.policy, { + maxRawBytes: 521231, + derivation: "exact-accepted-issue-296-slice-a-measurement", + gzip: "diagnostic-only", + }); + const checker = await import( new URL("../../../scripts/check-wasm-size-budget.mjs", import.meta.url) ); - assert.equal(checker.DEFAULT_BUDGET, v2Path); - assert.equal(checker.V1_FILE_SHA256, v2.buildRecipe.fileSha256); - assert.equal(checker.V1_RECIPE_SHA256, v2.buildRecipe.recipeSha256); + assert.equal(checker.DEFAULT_BUDGET, v3Path); + assert.equal(checker.V1_FILE_SHA256, v3.buildRecipe.fileSha256); + assert.equal(checker.V1_RECIPE_SHA256, v3.buildRecipe.recipeSha256); assert.equal(checker.V2_FILE_SHA256, sha256(v2Bytes)); + assert.equal(checker.V3_FILE_SHA256, sha256(v3Bytes)); const wholeCallSource = read( "packages", @@ -1075,7 +1113,7 @@ test("WCAG22 WASM budget v2 is exact, immutable, and acyclic", async () => { "wcag22-feasibility-boundary.bench.mjs", ); assert.match(wholeCallSource, /wasmToolchainPath = resolve\(here, "wasm-size-budget-v1\.json"\)/u); - assert.doesNotMatch(wholeCallSource, /wasm-size-budget-v2\.json/u); + assert.doesNotMatch(wholeCallSource, /wasm-size-budget-v[23]\.json/u); assert.doesNotMatch( read("scripts", "check-wasm-size-budget.mjs"), /wcag22-feasibility-wasm-boundary-v1\.json/u, @@ -1103,13 +1141,13 @@ test("WCAG22 WASM budget v2 is exact, immutable, and acyclic", async () => { /\/opt\/actions-runner\/[^\0]*?\/cargo-wasm\/registry\/src\//u, ); - const temporary = mkdtempSync(join(tmpdir(), "labcolors-wasm-budget-v2-")); + const temporary = mkdtempSync(join(tmpdir(), "labcolors-wasm-budget-v3-")); try { const wasmPath = join(temporary, "fixture.wasm"); const fixtureBudgetPath = join(temporary, "budget.json"); const bytes = Buffer.alloc(16); bytes.set([0x00, 0x61, 0x73, 0x6d]); - const fixture = structuredClone(v2); + const fixture = structuredClone(v3); fixture.measurement.rawBytes = bytes.length; fixture.measurement.sha256 = sha256(bytes); fixture.policy.maxRawBytes = bytes.length; @@ -1136,7 +1174,7 @@ test("WCAG22 WASM budget v2 is exact, immutable, and acyclic", async () => { ["v1 file drift", (value) => { value.buildRecipe.fileSha256 = "0".repeat(64); }], ["recipe drift", (value) => { value.buildRecipe.recipeSha256 = "0".repeat(64); }], ["missing recipe field", (value) => { delete value.buildRecipe.recipeSha256; }], - ["measurement issue drift", (value) => { value.measurement.issue = 294; }], + ["measurement issue drift", (value) => { value.measurement.issue = 295; }], ["measurement platform drift", (value) => { value.measurement.measurementPlatform = "darwin-arm64"; }], @@ -1163,7 +1201,7 @@ test("WCAG22 WASM budget v2 is exact, immutable, and acyclic", async () => { policy: value.policy, })], ]; - assert.equal(schemaMutations.length, 24, "v2 schema mutation set changed"); + assert.equal(schemaMutations.length, 24, "v3 schema mutation set changed"); for (const [name, mutate] of schemaMutations) { const invalid = structuredClone(fixture); const result = mutate(invalid) ?? invalid; @@ -1231,8 +1269,8 @@ test("WCAG22 WASM budget v2 is exact, immutable, and acyclic", async () => { coordinatedMutation.measurement.sha256 = sha256(sameSizeMutation); const coordinatedBytes = Buffer.from(canonicalJson(coordinatedMutation)); assert.throws( - () => checker.parseBudgetDocument(coordinatedBytes, v2Path), - /immutable v2 file SHA-256 mismatch/u, + () => checker.parseBudgetDocument(coordinatedBytes, v3Path), + /immutable v3 file SHA-256 mismatch/u, "coordinated artifact and document drift must still fail the default identity", ); } finally { @@ -1240,7 +1278,7 @@ test("WCAG22 WASM budget v2 is exact, immutable, and acyclic", async () => { } }); -test("feasibility benchmark applicability permits only source-less non-Core lock drift", () => { +test("feasibility benchmark keeps V1 history and admits only exact V2 Core subjects", () => { const checker = join( root, "scripts", @@ -1254,10 +1292,10 @@ test("feasibility benchmark applicability permits only source-less non-Core lock "crates", "labcolors-core", "contracts", - "wcag22-feasibility-benchmark-v1.json", + "wcag22-feasibility-benchmark-v2.json", ), "--artifact-sha256", - "7e9ffcbdd9d5d50fe681f511c34fc5c5dd270e9c475ce23ae56e9776922a3c5e", + "d8d5c7f3eda834bca9912d835fe3ada13d9dcd5a11cb47a131736716b0b51202", "--self-test", ], { cwd: root, @@ -1269,17 +1307,23 @@ test("feasibility benchmark applicability permits only source-less non-Core lock const ci = read(".github", "workflows", "ci.yml"); assert.match( ci, - /historical_snapshot=6001cf41e0a8364f25543e7955ceaf64d50129b4[\s\S]*?git worktree add --detach "\$historical_root" "\$historical_snapshot"[\s\S]*?\(\n\s+cd "\$historical_root"\n\s+python3 scripts\/check_wcag22_feasibility_benchmark\.py[\s\S]*?--verify-current-subjects[\s\S]*?--artifact-sha256 7e9ffcbdd9d5d50fe681f511c34fc5c5dd270e9c475ce23ae56e9776922a3c5e[\s\S]*?--self-test/u, + /historical_checker_snapshot=6001cf41e0a8364f25543e7955ceaf64d50129b4[\s\S]*?git worktree add --detach "\$historical_root" "\$historical_checker_snapshot"[\s\S]*?\(\n\s+cd "\$historical_root"\n\s+python3 scripts\/check_wcag22_feasibility_benchmark\.py[\s\S]*?--verify-current-subjects[\s\S]*?--artifact-sha256 7e9ffcbdd9d5d50fe681f511c34fc5c5dd270e9c475ce23ae56e9776922a3c5e[\s\S]*?--self-test/u, "the unchanged checker must replay in its exact clean Slice-A snapshot", ); - assert.equal( - ci.match(/python3 scripts\/check_wcag22_feasibility_benchmark\.py/gu)?.length, - 1, - "the historical checker must never be applied to the additive current workspace", + assert.match( + ci, + /historical_applicability_snapshot=94efeeeb1811f5515558ab2d79014a5e4c3a570a[\s\S]*?git worktree add --detach "\$historical_root" "\$historical_applicability_snapshot"[\s\S]*?python3 scripts\/check_wcag22_feasibility_applicability\.py[\s\S]*?--artifact-sha256 7e9ffcbdd9d5d50fe681f511c34fc5c5dd270e9c475ce23ae56e9776922a3c5e[\s\S]*?--self-test/u, + "the V1 applicability law must replay where its verifier first existed", ); assert.match( ci, - /python3 scripts\/check_wcag22_feasibility_applicability\.py[\s\S]*?--artifact-sha256 7e9ffcbdd9d5d50fe681f511c34fc5c5dd270e9c475ce23ae56e9776922a3c5e[\s\S]*?--self-test/u, + /current_artifact="crates\/labcolors-core\/contracts\/wcag22-feasibility-benchmark-v2\.json"[\s\S]*?--admit-revision 965eb42642beb1c072a74886be1d016027afeae5[\s\S]*?python3 scripts\/check_wcag22_feasibility_benchmark\.py[\s\S]*?--verify-current-subjects[\s\S]*?--artifact-sha256 d8d5c7f3eda834bca9912d835fe3ada13d9dcd5a11cb47a131736716b0b51202[\s\S]*?python3 scripts\/check_wcag22_feasibility_applicability\.py[\s\S]*?--artifact-sha256 d8d5c7f3eda834bca9912d835fe3ada13d9dcd5a11cb47a131736716b0b51202[\s\S]*?--self-test/u, + "V2 must bind the current generic kernel to one exact measured source commit", + ); + assert.equal( + ci.match(/python3 scripts\/check_wcag22_feasibility_benchmark\.py/gu)?.length, + 2, + "CI must validate exactly one historical and one current benchmark artifact", ); }); diff --git a/packages/colors/test/wcag22-feasibility-boundary.test.mjs b/packages/colors/test/wcag22-feasibility-boundary.test.mjs index 0d00a55c..7255654e 100644 --- a/packages/colors/test/wcag22-feasibility-boundary.test.mjs +++ b/packages/colors/test/wcag22-feasibility-boundary.test.mjs @@ -331,10 +331,10 @@ test("final CI verifies and uploads committed Linux evidence before the size gat const fingerprintIndex = ci.indexOf(fingerprint, verifyIndex); const uploadIndex = ci.indexOf(upload, verifyIndex); const oldSizeGateIndex = ci.indexOf(oldSizeGate); - assert.match(ci, /verify committed #295 canonical whole-call WASM boundary evidence/u); + assert.match(ci, /verify committed #296-A canonical whole-call WASM boundary evidence/u); assert.match( ci, - /name: "upload exact #295 verified whole-call evidence"/u, + /name: "upload exact #296-A verified whole-call evidence"/u, ); assert.ok(harnessIndex >= 0, "the package-root harness must run in CI"); assert.ok(verifyIndex > harnessIndex, "CI must rerun the committed evidence verifier"); diff --git a/scripts/check-wasm-size-budget.mjs b/scripts/check-wasm-size-budget.mjs index 2a8910a8..907e2332 100644 --- a/scripts/check-wasm-size-budget.mjs +++ b/scripts/check-wasm-size-budget.mjs @@ -11,10 +11,11 @@ const SCRIPT_DIR = dirname(SCRIPT_PATH); const REPO_ROOT = resolve(SCRIPT_DIR, ".."); const DEFAULT_WASM = resolve(REPO_ROOT, "packages/colors/pkg/labcolors_bg.wasm"); const V1_PATH = resolve(REPO_ROOT, "packages/colors/bench/wasm-size-budget-v1.json"); +const V2_PATH = resolve(REPO_ROOT, "packages/colors/bench/wasm-size-budget-v2.json"); export const DEFAULT_BUDGET = resolve( REPO_ROOT, - "packages/colors/bench/wasm-size-budget-v2.json", + "packages/colors/bench/wasm-size-budget-v3.json", ); export const V1_FILE_SHA256 = "4f7340fc8cfd0ccb97377c385f2f8d8e7a9ef2c5ba96177f518c5d07de2825e1"; @@ -22,9 +23,11 @@ export const V1_RECIPE_SHA256 = "0ea74cb070e0a5facb7280f6124930a0bb673ee4dcee9c99fff110db6c9389d4"; export const V2_FILE_SHA256 = "713ccc314b3e6f638d87a54716d665d52f77c86f34a2b6edefe0a354a499d8b1"; +export const V3_FILE_SHA256 = + "d7937612e4c33574a8af28845bb1dd30cca86fc39fc0206cac4c377de77fec15"; const V1_REPOSITORY_PATH = "packages/colors/bench/wasm-size-budget-v1.json"; -const V2_BUDGET_ID = "labcolors-wasm-raw-issue-295-v2"; +const V3_BUDGET_ID = "labcolors-wasm-raw-issue-296-v3"; const WASM_REPOSITORY_PATH = "packages/colors/pkg/labcolors_bg.wasm"; function fail(message) { @@ -107,6 +110,18 @@ function verifyImmutableBuildRecipe() { `expected=${V1_RECIPE_SHA256} actual=${actualRecipeSha256}`, ); } + + const v2Bytes = readFileSync(V2_PATH); + if (sha256(v2Bytes) !== V2_FILE_SHA256) { + fail( + `immutable v2 file SHA-256 mismatch: ` + + `expected=${V2_FILE_SHA256} actual=${sha256(v2Bytes)}`, + ); + } + const v2 = JSON.parse(v2Bytes.toString("utf8")); + if (v2?.schemaVersion !== 3 || v2?.budgetId !== "labcolors-wasm-raw-issue-295-v2") { + fail("immutable v2 budget identity drifted"); + } } function validateBudgetValue(budget) { @@ -116,7 +131,7 @@ function validateBudgetValue(budget) { "budget", ); if (budget.schemaVersion !== 3) fail("supported schemaVersion is exactly 3"); - if (budget.budgetId !== V2_BUDGET_ID) fail(`budgetId must be ${V2_BUDGET_ID}`); + if (budget.budgetId !== V3_BUDGET_ID) fail(`budgetId must be ${V3_BUDGET_ID}`); if (budget.artifact !== WASM_REPOSITORY_PATH) { fail(`artifact must be ${WASM_REPOSITORY_PATH}`); } @@ -141,7 +156,7 @@ function validateBudgetValue(budget) { ["issue", "measurementPlatform", "rawBytes", "sha256"], "measurement", ); - if (budget.measurement.issue !== 295) fail("measurement must cite Issue #295"); + if (budget.measurement.issue !== 296) fail("measurement must cite Issue #296"); if (budget.measurement.measurementPlatform !== "linux-x64") { fail("measurement.measurementPlatform must be canonical linux-x64"); } @@ -151,10 +166,10 @@ function validateBudgetValue(budget) { exactKeys(budget.policy, ["maxRawBytes", "derivation", "gzip"], "policy"); positiveSafeInteger(budget.policy.maxRawBytes, "policy.maxRawBytes"); if (budget.policy.maxRawBytes !== budget.measurement.rawBytes) { - fail("v2 ceiling must equal the exact accepted measurement (zero arbitrary headroom)"); + fail("current ceiling must equal the exact accepted measurement (zero arbitrary headroom)"); } - if (budget.policy.derivation !== "exact-accepted-issue-295-slice-b-measurement") { - fail("policy.derivation must cite the exact accepted Issue #295 Slice B measurement"); + if (budget.policy.derivation !== "exact-accepted-issue-296-slice-a-measurement") { + fail("policy.derivation must cite the exact accepted Issue #296 Slice A measurement"); } if (budget.policy.gzip !== "diagnostic-only") { fail("gzip must remain diagnostic-only across implementations"); @@ -178,10 +193,10 @@ export function parseBudgetDocument(bytes, budgetPath) { validateBudgetValue(budget); if (resolve(budgetPath) === DEFAULT_BUDGET) { const actualFileSha256 = sha256(document); - if (actualFileSha256 !== V2_FILE_SHA256) { + if (actualFileSha256 !== V3_FILE_SHA256) { fail( - `immutable v2 file SHA-256 mismatch: ` + - `expected=${V2_FILE_SHA256} actual=${actualFileSha256}`, + `immutable v3 file SHA-256 mismatch: ` + + `expected=${V3_FILE_SHA256} actual=${actualFileSha256}`, ); } } diff --git a/scripts/check_wcag22_feasibility_applicability.py b/scripts/check_wcag22_feasibility_applicability.py index 6e56705f..c9959fda 100644 --- a/scripts/check_wcag22_feasibility_applicability.py +++ b/scripts/check_wcag22_feasibility_applicability.py @@ -1,9 +1,10 @@ #!/usr/bin/env python3 """Verify current applicability of the immutable feasibility benchmark. -The admitted Slice-A artifact keeps the exact measured ``Cargo.lock`` blob as -historical provenance. This verifier deliberately does not reinterpret that -blob as a byte-for-byte constraint on unrelated workspace packages. Instead +The admitted neutral-path V2 artifact keeps the exact measured ``Cargo.lock`` +blob as historical provenance. This verifier deliberately does not +reinterpret that blob as a byte-for-byte constraint on unrelated workspace +packages. Instead it proves that every current Core/benchmark subject is unchanged and that the current lock preserves the exact ``labcolors-core`` record plus every sourced registry/git package from the measured lock. Only source-less non-Core @@ -25,7 +26,7 @@ ROOT = Path(__file__).resolve().parents[1] DEFAULT_ARTIFACT = ROOT / ( "crates/labcolors-core/contracts/" - "wcag22-feasibility-benchmark-v1.json" + "wcag22-feasibility-benchmark-v2.json" ) HISTORICAL_ONLY_PATH = "Cargo.lock" CORE_SOURCE_TREE_PATH = "crates/labcolors-core/src" @@ -33,6 +34,7 @@ "Cargo.toml", "crates/labcolors-core/src/lib.rs", "crates/labcolors-core/src/wcag22_feasibility.rs", + "crates/labcolors-core/src/wcag22_feasibility/explicit.rs", "crates/labcolors-core/src/srgb8.rs", "crates/labcolors-core/src/sha256.rs", "crates/labcolors-core/src/wcag22.rs", @@ -310,7 +312,7 @@ def check_current_applicability( payload = decode_artifact(raw) require(payload.get("schemaVersion") == 1, "unsupported benchmark artifact schema") - require(payload.get("artifactId") == "wcag22-feasibility-admission-raw-v1", + require(payload.get("artifactId") == "wcag22-feasibility-admission-raw-v2", "unexpected benchmark artifact identity") manifest = subject_manifest(payload) diff --git a/scripts/check_wcag22_feasibility_benchmark.py b/scripts/check_wcag22_feasibility_benchmark.py index 061cb228..d1b9eaa2 100644 --- a/scripts/check_wcag22_feasibility_benchmark.py +++ b/scripts/check_wcag22_feasibility_benchmark.py @@ -24,7 +24,7 @@ DEFAULT_ARTIFACT = Path( - "/private/tmp/labcolors-wcag22-feasibility-admission-raw-v1.json" + "/private/tmp/labcolors-wcag22-feasibility-admission-raw-v2.json" ) HEX_256 = re.compile(r"[0-9a-f]{64}") GIT_OBJECT = re.compile(r"[0-9a-f]{40}") @@ -40,6 +40,7 @@ "Cargo.toml", "crates/labcolors-core/src/lib.rs", "crates/labcolors-core/src/wcag22_feasibility.rs", + "crates/labcolors-core/src/wcag22_feasibility/explicit.rs", "crates/labcolors-core/src/srgb8.rs", "crates/labcolors-core/src/sha256.rs", "crates/labcolors-core/src/wcag22.rs", @@ -535,7 +536,7 @@ def check( ) -> None: require(isinstance(payload, dict), "artifact root must be an object") require(payload.get("schemaVersion") == 1, "unsupported benchmark schemaVersion") - require(payload.get("artifactId") == "wcag22-feasibility-admission-raw-v1", + require(payload.get("artifactId") == "wcag22-feasibility-admission-raw-v2", "unexpected benchmark artifactId") require( payload.get("claimBoundary") diff --git a/scripts/verify_wcag22_explicit_feasibility_identity.py b/scripts/verify_wcag22_explicit_feasibility_identity.py new file mode 100755 index 00000000..502352cf --- /dev/null +++ b/scripts/verify_wcag22_explicit_feasibility_identity.py @@ -0,0 +1,484 @@ +#!/usr/bin/env python3 +"""Independent byte oracle for explicit WCAG22 feasibility identities. + +This verifier imports no Rust code and performs no colour evaluation. It fixes +only the versioned cross-language byte grammar used by #296-A: exact UTF-8 +candidate ordering, the inherited relation grammar, explicit-domain identity +and explicit evaluation identity. +""" + +from __future__ import annotations + +import argparse +import copy +import hashlib +import json +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[1] +FIXTURE = ROOT / ( + "crates/labcolors-core/contracts/" + "wcag22-explicit-feasibility-identity-v1.json" +) + +DOMAIN_SEPARATOR = ( + b"labcolors/wcag22-feasibility/domain/explicit-srgb8-set/v1\0" +) +RELATION_SEPARATOR = b"labcolors/wcag22-feasibility/relations/v1\0" +EVALUATION_SEPARATOR = ( + b"labcolors/wcag22-feasibility/evaluation/explicit-srgb8-set/v1\0" +) +DOMAIN_KIND = b"explicit-srgb8-set-v1" + +PROFILE_KEY = b"wcag22-srgb8-contrast-v1" +ARTIFACT_KEY = b"wcag22-srgb8-luminance-q55-v1" +BOUND_KEY = b"wcag22-srgb8-outward-q55-v1" +PROOF_KEY = b"wcag22-srgb8-full-domain-q55-v1" +PROOF_SHA256 = bytes.fromhex( + "d269e9de689009bb955788bf8762fce56680bf616fc0459b6526a367875a6a08" +) + +LAYOUT_FIELDS = ( + "canonicalRelations", + "applicableRelations", + "notApplicableRelations", + "applicableEdges", + "candidateCount", + "logicalAssessments", + "failureMatrixBytes", + "partitionBytes", + "packedResultBytes", +) + + +def require(condition: bool, message: str) -> None: + if not condition: + raise ValueError(message) + + +def u64(value: int) -> bytes: + require(type(value) is int and 0 <= value < 1 << 64, + f"not an unsigned 64-bit value: {value!r}") + return value.to_bytes(8, "big") + + +def field(value: bytes) -> bytes: + return u64(len(value)) + value + + +def sha256(value: bytes) -> bytes: + return hashlib.sha256(value).digest() + + +def rgb(value: Any, label: str) -> tuple[int, int, int]: + require( + isinstance(value, (list, tuple)) + and len(value) == 3 + and all(type(channel) is int and 0 <= channel <= 255 for channel in value), + f"{label} must be exactly three sRGB8 octets", + ) + return (value[0], value[1], value[2]) + + +def candidate_id_bytes(candidate: dict[str, Any]) -> bytes: + value = candidate.get("candidateId") + require(isinstance(value, str) and value != "", + "candidateId must be a non-empty UTF-8 string") + return value.encode("utf-8") + + +def canonical_candidates( + candidates: list[dict[str, Any]], +) -> list[dict[str, Any]]: + require(candidates != [], "explicit candidate set must be non-empty") + canonical = sorted(copy.deepcopy(candidates), key=candidate_id_bytes) + previous: bytes | None = None + for index, candidate in enumerate(canonical): + identity = candidate_id_bytes(candidate) + require(identity != previous, "duplicate exact candidate ID bytes") + candidate["emitted"] = list(rgb(candidate.get("emitted"), f"candidate[{index}]")) + previous = identity + return canonical + + +def domain_preimage_from_canonical( + candidates: list[dict[str, Any]], +) -> bytes: + preimage = bytearray(DOMAIN_SEPARATOR) + preimage += field(DOMAIN_KIND) + preimage += u64(len(candidates)) + for candidate in candidates: + preimage += field(candidate_id_bytes(candidate)) + preimage += bytes(rgb(candidate["emitted"], "canonical candidate emitted")) + return bytes(preimage) + + +def canonical_relation(relation: dict[str, Any]) -> dict[str, Any]: + result = copy.deepcopy(relation) + relation_id = result.get("relationId") + occurrence_id = result.get("occurrenceId") + require(isinstance(relation_id, str) and relation_id != "", + "relationId must be non-empty") + require(isinstance(occurrence_id, str) and occurrence_id != "", + "occurrenceId must be non-empty") + kind = result.get("kind") + if kind == "applicable": + criterion = result.get("criterion") + require(isinstance(criterion, str) and criterion != "", + "applicable criterion key must be non-empty") + adjacent = result.get("adjacent") + require(isinstance(adjacent, list) and adjacent != [], + "applicable relation needs adjacency") + result["adjacent"] = [ + list(value) + for value in sorted({rgb(value, "adjacent") for value in adjacent}) + ] + result.pop("reasonId", None) + elif kind == "notApplicable": + reason = result.get("reasonId") + require(isinstance(reason, str) and reason != "", + "NotApplicable reasonId must be non-empty") + result.pop("criterion", None) + result.pop("adjacent", None) + else: + raise ValueError(f"unknown relation kind: {kind!r}") + return result + + +def canonical_relations( + relations: list[dict[str, Any]], +) -> list[dict[str, Any]]: + require(relations != [], "relation set must be non-empty") + by_id: dict[bytes, dict[str, Any]] = {} + for relation in relations: + canonical = canonical_relation(relation) + identity = canonical["relationId"].encode("utf-8") + previous = by_id.get(identity) + require(previous is None or previous == canonical, + "same relation ID has conflicting canonical declarations") + by_id[identity] = canonical + return [by_id[identity] for identity in sorted(by_id)] + + +def relation_preimage(relations: list[dict[str, Any]]) -> bytes: + preimage = bytearray(RELATION_SEPARATOR) + preimage += u64(len(relations)) + for relation in relations: + kind = relation["kind"] + if kind == "applicable": + preimage += b"\x01" + preimage += field(relation["relationId"].encode("utf-8")) + preimage += field(relation["occurrenceId"].encode("utf-8")) + preimage += field(relation["criterion"].encode("utf-8")) + preimage += u64(len(relation["adjacent"])) + for adjacent in relation["adjacent"]: + preimage += bytes(rgb(adjacent, "canonical adjacent")) + else: + require(kind == "notApplicable", "canonical relation kind drifted") + preimage += b"\x02" + preimage += field(relation["relationId"].encode("utf-8")) + preimage += field(relation["occurrenceId"].encode("utf-8")) + preimage += field(relation["reasonId"].encode("utf-8")) + return bytes(preimage) + + +def evaluation_preimage( + domain_digest: bytes, + relation_digest: bytes, + layout: dict[str, int], + matrix: bytes, + partition: bytes, +) -> bytes: + require(len(domain_digest) == 32 and len(relation_digest) == 32, + "content digests must be SHA-256 values") + preimage = bytearray(EVALUATION_SEPARATOR) + preimage += domain_digest + preimage += relation_digest + for key in (PROFILE_KEY, ARTIFACT_KEY, BOUND_KEY, PROOF_KEY): + preimage += field(key) + preimage += PROOF_SHA256 + for name in LAYOUT_FIELDS: + require(name in layout, f"missing layout field {name}") + preimage += u64(layout[name]) + preimage += sha256(matrix) + preimage += field(partition) + return bytes(preimage) + + +def bit(value: bytes, index: int) -> bool: + return value[index // 8] & (1 << (index % 8)) != 0 + + +def validate_complete_fixture( + candidates: list[dict[str, Any]], + relations: list[dict[str, Any]], + layout: dict[str, int], + matrix: bytes, + partition: bytes, +) -> None: + candidate_count = len(candidates) + applicable = [value for value in relations if value["kind"] == "applicable"] + not_applicable = [ + value for value in relations if value["kind"] == "notApplicable" + ] + edges = sum(len(value["adjacent"]) for value in applicable) + work = candidate_count * edges + matrix_bytes = (work + 7) // 8 + partition_bytes = (candidate_count + 7) // 8 + expected = { + "canonicalRelations": len(relations), + "applicableRelations": len(applicable), + "notApplicableRelations": len(not_applicable), + "applicableEdges": edges, + "candidateCount": candidate_count, + "logicalAssessments": work, + "failureMatrixBytes": matrix_bytes, + "partitionBytes": partition_bytes, + "packedResultBytes": matrix_bytes + partition_bytes, + } + require(layout == expected, "fixture layout drifted from exact C x E laws") + require(len(matrix) == matrix_bytes, "matrix byte length is not ceil(C*E/8)") + require(len(partition) == partition_bytes, + "partition byte length is not ceil(C/8)") + if work % 8: + require(matrix[-1] >> (work % 8) == 0, "matrix tail bits must be zero") + if candidate_count % 8: + require(partition[-1] >> (candidate_count % 8) == 0, + "partition tail bits must be zero") + for candidate in range(candidate_count): + row_passes = all(not bit(matrix, candidate * edges + edge) + for edge in range(edges)) + require(bit(partition, candidate) == row_passes, + "partition must be the exact row-wise matrix reduction") + + +def fixture_model() -> dict[str, Any]: + # Deliberately shuffled. U+0065 U+0301 and U+00E9 look alike but are two + # exact, non-normalized client IDs and even carry the same physical bytes. + candidates = [ + {"candidateId": "🎨", "emitted": [255, 128, 1]}, + {"candidateId": "é", "emitted": [18, 52, 86]}, + {"candidateId": "海", "emitted": [0, 0, 0]}, + {"candidateId": "e\u0301", "emitted": [18, 52, 86]}, + ] + relations = [ + { + "relationId": "zeta", + "occurrenceId": "ornament", + "kind": "notApplicable", + "reasonId": "client/не-применимо", + }, + { + "relationId": "alpha", + "occurrenceId": "hover/🎨", + "kind": "applicable", + "criterion": "sc-1.4.3-text-default", + "adjacent": [[255, 255, 255], [0, 0, 0], [118, 118, 118], [0, 0, 0]], + }, + ] + return { + "candidates": candidates, + "relations": relations, + "layout": { + "canonicalRelations": 2, + "applicableRelations": 1, + "notApplicableRelations": 1, + "applicableEdges": 3, + "candidateCount": 4, + "logicalAssessments": 12, + "failureMatrixBytes": 2, + "partitionBytes": 1, + "packedResultBytes": 3, + }, + # Actual public WCAG path, candidate-major LSB0 rows: + # F,F,P | F,F,P | F,P,P | P,F,F. No candidate passes every edge. + "matrix": bytes.fromhex("5b0c"), + "partition": bytes.fromhex("00"), + } + + +def identities(model: dict[str, Any]) -> tuple[bytes, bytes, bytes]: + candidates = canonical_candidates(model["candidates"]) + relations = canonical_relations(model["relations"]) + domain = sha256(domain_preimage_from_canonical(candidates)) + relation = sha256(relation_preimage(relations)) + evaluation = sha256( + evaluation_preimage( + domain, + relation, + model["layout"], + model["matrix"], + model["partition"], + ) + ) + return domain, relation, evaluation + + +def build_fixture() -> dict[str, Any]: + model = fixture_model() + candidates = canonical_candidates(model["candidates"]) + relations = canonical_relations(model["relations"]) + validate_complete_fixture( + candidates, + relations, + model["layout"], + model["matrix"], + model["partition"], + ) + domain, relation, evaluation = identities(model) + return { + "schemaVersion": 1, + "artifactId": "wcag22-explicit-feasibility-identity-v1", + "encoding": { + "integer": "u64-big-endian", + "byteString": "u64-length-then-exact-bytes", + "candidateOrder": "lexicographic-exact-utf8-bytes-no-normalization", + "candidateRecord": "length-prefixed-id-then-three-srgb8-octets", + "relationGrammar": "wcag22-feasibility-relations-v1", + "matrixBitOrder": "candidate-major-contiguous-lsb0", + "partitionBitOrder": "canonical-candidate-index-lsb0", + "evaluationLayoutOrder": list(LAYOUT_FIELDS), + "partitionInEvaluation": "u64-length-then-exact-bytes", + }, + "fixture": { + "domainKind": DOMAIN_KIND.decode(), + "declaredCandidates": model["candidates"], + "canonicalCandidates": [ + { + **candidate, + "candidateIdUtf8Hex": candidate_id_bytes(candidate).hex(), + } + for candidate in candidates + ], + "canonicalRelations": relations, + "layout": model["layout"], + "failureMatrixHex": model["matrix"].hex(), + "matrixSha256": sha256(model["matrix"]).hex(), + "partitionHex": model["partition"].hex(), + }, + "expected": { + "domainDigestSha256": domain.hex(), + "relationSetDigestSha256": relation.hex(), + "evaluationIdSha256": evaluation.hex(), + }, + } + + +def canonical_bytes(value: dict[str, Any]) -> bytes: + return ( + json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + + "\n" + ).encode("utf-8") + + +def mutation_self_tests() -> tuple[int, int]: + baseline_model = fixture_model() + baseline = identities(baseline_model) + mutation_checks = 0 + invariance_checks = 0 + + def changed(label: str, mutate: Any, expected_indices: tuple[int, ...]) -> None: + nonlocal mutation_checks + candidate = copy.deepcopy(baseline_model) + mutate(candidate) + observed = identities(candidate) + require( + all(observed[index] != baseline[index] for index in expected_indices), + f"mutation survived identity oracle: {label}", + ) + mutation_checks += 1 + + changed( + "candidate ID bytes", + lambda value: value["candidates"][1].__setitem__("candidateId", "É"), + (0, 2), + ) + changed( + "candidate emitted RGB", + lambda value: value["candidates"][1].__setitem__("emitted", [19, 52, 86]), + (0, 2), + ) + changed( + "layout count", + lambda value: value["layout"].__setitem__("logicalAssessments", 13), + (2,), + ) + changed( + "matrix byte", + lambda value: value.__setitem__( + "matrix", bytes([value["matrix"][0] ^ 1, value["matrix"][1]]) + ), + (2,), + ) + changed( + "partition byte", + lambda value: value.__setitem__("partition", bytes([value["partition"][0] ^ 1])), + (2,), + ) + changed( + "relation occurrence", + lambda value: value["relations"][1].__setitem__("occurrenceId", "hover/other"), + (1, 2), + ) + + # A caller permutation is intentionally invariant because Core sorts exact + # UTF-8 bytes. Hashing the reversed canonical records directly, however, + # is a wrong preimage and must not match the canonical domain identity. + permuted = copy.deepcopy(baseline_model) + permuted["candidates"].reverse() + require(identities(permuted) == baseline, + "declared candidate permutation changed canonical identity") + canonical = canonical_candidates(baseline_model["candidates"]) + wrong_order = sha256(domain_preimage_from_canonical(list(reversed(canonical)))) + require(wrong_order != baseline[0], + "non-canonical candidate record order matched canonical digest") + invariance_checks += 1 + + decomposed = "e\u0301".encode("utf-8") + composed = "é".encode("utf-8") + require(decomposed != composed, "normalization witness collapsed exact UTF-8 bytes") + require( + [candidate_id_bytes(value) for value in canonical] + == sorted([candidate_id_bytes(value) for value in canonical]), + "canonical candidate order is not exact byte order", + ) + invariance_checks += 1 + return mutation_checks, invariance_checks + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--print", action="store_true", dest="print_fixture") + parser.add_argument("--self-test", action="store_true") + args = parser.parse_args() + + expected = canonical_bytes(build_fixture()) + if args.print_fixture: + print(expected.decode("utf-8"), end="") + return 0 + actual = FIXTURE.read_bytes() + require(actual == expected, + "explicit feasibility identity fixture drift; run --print and review exact bytes") + payload = json.loads(actual) + mutation_checks, invariance_checks = ( + mutation_self_tests() if args.self_test else (0, 0) + ) + print( + "WCAG22 explicit feasibility identity oracle: PASS; " + f"domain={payload['expected']['domainDigestSha256']}; " + f"relations={payload['expected']['relationSetDigestSha256']}; " + f"evaluation={payload['expected']['evaluationIdSha256']}; " + f"fixture_sha256={sha256(actual).hex()}; " + f"mutation_checks={mutation_checks}; " + f"invariance_checks={invariance_checks}" + ) + return 0 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except (OSError, ValueError) as error: + raise SystemExit(f"WCAG22 explicit feasibility identity oracle: {error}") from error