From a2e6de1764ba2772af9e7f8823779c621eec3e15 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Wed, 5 Aug 2026 11:15:46 +0300 Subject: [PATCH 1/5] RED: hostile contract for the authored selection release (V5c-1) --- crates/labcolors-core/src/lib.rs | 12 ++ .../src/selection_release_tests.rs | 187 ++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 crates/labcolors-core/src/selection_release_tests.rs diff --git a/crates/labcolors-core/src/lib.rs b/crates/labcolors-core/src/lib.rs index 2fe730e8..710427c3 100644 --- a/crates/labcolors-core/src/lib.rs +++ b/crates/labcolors-core/src/lib.rs @@ -176,6 +176,18 @@ mod session_tests; pub(crate) mod joint; +#[cfg_attr( + not(test), + expect( + dead_code, + reason = "the authored selection release materialises the joint order from V5c-2" + ) +)] +pub(crate) mod selection_release; + +#[cfg(test)] +mod selection_release_tests; + #[cfg(test)] mod constraint_tests; diff --git a/crates/labcolors-core/src/selection_release_tests.rs b/crates/labcolors-core/src/selection_release_tests.rs new file mode 100644 index 00000000..5a11d01c --- /dev/null +++ b/crates/labcolors-core/src/selection_release_tests.rs @@ -0,0 +1,187 @@ +//! Hostile contract for the sole authored selection release (V5c-1). +//! +//! One versioned `SelectionReleaseV1` is the only authored selection input: +//! it declares a total preorder over opaque canonical candidate keys as an +//! ordered sequence of tie groups, and the single common tie-break inside a +//! group is the canonical key bytes themselves. Declaration index, `usize` +//! positions, RGB bytes, distances and weights never participate in the +//! order. Admission seals the release once and content-addresses it; any +//! release shape that cannot form a total preorder is a typed rejection, +//! and selection materialisation never sorts by anything but the admitted +//! rank and the canonical key. + +use crate::selection_release::{ + admit_selection_release_v1, SelectionCandidateKeyV1, SelectionReleaseErrorV1, + SelectionReleaseV1, +}; + +fn key(bytes: &[u8]) -> SelectionCandidateKeyV1 { + SelectionCandidateKeyV1::new(bytes.to_vec().into_boxed_slice()) +} + +fn release( + revision: u64, + groups: &[&[&[u8]]], +) -> SelectionReleaseV1 { + SelectionReleaseV1::new( + revision, + groups + .iter() + .map(|group| { + group + .iter() + .map(|bytes| key(bytes)) + .collect::>() + .into_boxed_slice() + }) + .collect::>() + .into_boxed_slice(), + ) +} + +#[test] +fn admission_rejects_every_foreign_release_shape() { + // no rank groups at all + let rejected = admit_selection_release_v1(release(1, &[])); + assert!(matches!( + rejected, + Err(SelectionReleaseErrorV1::EmptyRelease) + )); + // one empty tie group + let rejected = admit_selection_release_v1(release(1, &[&[]])); + assert!(matches!( + rejected, + Err(SelectionReleaseErrorV1::EmptyRankGroup) + )); + // an empty canonical key + let rejected = admit_selection_release_v1(release(1, &[&[b""] ])); + assert!(matches!( + rejected, + Err(SelectionReleaseErrorV1::EmptyCandidateKey) + )); + // duplicate key inside one tie group + let rejected = admit_selection_release_v1(release(1, &[&[b"a", b"a"]])); + assert!(matches!( + rejected, + Err(SelectionReleaseErrorV1::DuplicateCandidateKey) + )); + // duplicate key across two tie groups + let rejected = admit_selection_release_v1(release(1, &[&[b"a"], &[b"a"]])); + assert!(matches!( + rejected, + Err(SelectionReleaseErrorV1::DuplicateCandidateKey) + )); +} + +#[test] +fn identity_is_content_addressed_and_revision_bound() { + let first = admit_selection_release_v1(release(7, &[&[b"a", b"b"], &[b"c"]])) + .expect("authored release must admit"); + let second = admit_selection_release_v1(release(7, &[&[b"a", b"b"], &[b"c"]])) + .expect("the same authoring must admit identically"); + assert_eq!(first.identity(), second.identity()); + let renumbered = admit_selection_release_v1(release(8, &[&[b"a", b"b"], &[b"c"]])) + .expect("a renumbered release must still admit"); + assert_ne!(first.identity(), renumbered.identity()); +} + +#[test] +fn key_permutation_inside_a_tie_group_is_not_policy() { + let canonical = + admit_selection_release_v1(release(1, &[&[b"zeta", b"alpha"], &[b"beta"]])) + .expect("authored release must admit"); + let permuted = + admit_selection_release_v1(release(1, &[&[b"alpha", b"zeta"], &[b"beta"]])) + .expect("permuted tie group must admit"); + assert_eq!(canonical.identity(), permuted.identity()); + let candidates = [ + (1u32, key(b"zeta")), + (2, key(b"alpha")), + (3, key(b"beta")), + ]; + assert_eq!( + canonical.select_order_v1(&candidates).unwrap(), + permuted.select_order_v1(&candidates).unwrap() + ); +} + +#[test] +fn total_preorder_ranks_follow_the_authored_groups() { + let admitted = admit_selection_release_v1(release(1, &[&[b"x", b"y"], &[b"z"]])) + .expect("authored release must admit"); + assert_eq!(admitted.rank_of(&key(b"x")), Some(0)); + assert_eq!(admitted.rank_of(&key(b"y")), Some(0)); + assert_eq!(admitted.rank_of(&key(b"z")), Some(1)); + assert_eq!(admitted.rank_of(&key(b"foreign")), None); +} + +#[test] +fn selection_orders_by_rank_then_canonical_key_bytes_only() { + let admitted = + admit_selection_release_v1(release(1, &[&[b"zz", b"aa"], &[b"mm"], &[b"bb"]])) + .expect("authored release must admit"); + let candidates = [ + ("last-declared", key(b"mm")), + ("first-declared", key(b"zz")), + ("middle-declared", key(b"aa")), + ("tail-declared", key(b"bb")), + ]; + let selected = admitted.select_order_v1(&candidates).unwrap(); + // rank dominates, and the tie inside rank 0 breaks on key bytes, never on + // declaration position + assert_eq!( + selected.as_ref(), + ["middle-declared", "first-declared", "last-declared", "tail-declared"] + ); + // permuting the candidate input order never changes the selection order + let shuffled = [ + ("tail-declared", key(b"bb")), + ("middle-declared", key(b"aa")), + ("last-declared", key(b"mm")), + ("first-declared", key(b"zz")), + ]; + assert_eq!(admitted.select_order_v1(&shuffled).unwrap(), selected); +} + +#[test] +fn selection_rejects_every_foreign_binding() { + let admitted = admit_selection_release_v1(release(1, &[&[b"a"], &[b"b"]])) + .expect("authored release must admit"); + // unknown key is not silently ranked + let rejected = admitted.select_order_v1(&[(1u32, key(b"foreign"))]); + assert!(matches!( + rejected, + Err(SelectionReleaseErrorV1::UnknownCandidateKey) + )); + // two candidates bound to one canonical key receive no hidden merge + let rejected = admitted.select_order_v1(&[(1u32, key(b"a")), (2, key(b"a"))]); + assert!(matches!( + rejected, + Err(SelectionReleaseErrorV1::DuplicateCandidateBinding) + )); + // an empty candidate set never produces a selection + let rejected = admitted.select_order_v1::(&[]); + assert!(matches!( + rejected, + Err(SelectionReleaseErrorV1::EmptyCandidateSet) + )); +} + +#[test] +fn bijective_payload_relabeling_preserves_the_order_structure() { + let admitted = + admit_selection_release_v1(release(1, &[&[b"k1", b"k2"], &[b"k3"]])) + .expect("authored release must admit"); + let candidates = [(10u32, key(b"k2")), (20, key(b"k3")), (30, key(b"k1"))]; + let original = admitted.select_order_v1(&candidates).unwrap(); + let relabeled_candidates = candidates + .iter() + .map(|(payload, key)| (payload.wrapping_mul(31).wrapping_add(7), key.clone())) + .collect::>(); + let relabeled = admitted.select_order_v1(&relabeled_candidates).unwrap(); + let expected = original + .iter() + .map(|payload| payload.wrapping_mul(31).wrapping_add(7)) + .collect::>(); + assert_eq!(relabeled.as_ref(), expected.as_slice()); +} From c17952bd406fd40e1e401a103b7c6aed41c5114a Mon Sep 17 00:00:00 2001 From: Claude Code Date: Wed, 5 Aug 2026 11:18:56 +0300 Subject: [PATCH 2/5] GREEN: authored selection release with a proven total preorder (V5c-1) --- .../labcolors-core/src/selection_release.rs | 177 ++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 crates/labcolors-core/src/selection_release.rs diff --git a/crates/labcolors-core/src/selection_release.rs b/crates/labcolors-core/src/selection_release.rs new file mode 100644 index 00000000..f631ba3b --- /dev/null +++ b/crates/labcolors-core/src/selection_release.rs @@ -0,0 +1,177 @@ +//! The sole authored selection release over the hard-feasible set (V5c-1). +//! +//! One versioned [`SelectionReleaseV1`] is the only authored selection input: +//! it declares a total preorder over opaque canonical candidate keys as an +//! ordered sequence of tie groups, and the single common tie-break inside a +//! group is the canonical key bytes themselves. Declaration index, `usize` +//! positions, RGB bytes, distances and weights never participate in the +//! order. The module admits the release once, seals it, and content-addresses +//! it; materialisation sorts exclusively by the admitted rank and the +//! canonical key, so no evaluator ever ranks or selects. + +use std::collections::BTreeMap; + +use crate::sha256; + +const IDENTITY_DOMAIN_V1: &[u8] = b"labcolors.selection-release.v1\0"; + +/// Typed admission and materialisation failures of the selection release. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum SelectionReleaseErrorV1 { + /// The release declares no tie groups, so no preorder exists. + EmptyRelease, + /// A tie group carries no candidate keys. + EmptyRankGroup, + /// A candidate key has no bytes and cannot identify a candidate. + EmptyCandidateKey, + /// One canonical key appears in more than one place of the release. + DuplicateCandidateKey, + /// A candidate binds a key that the release does not rank. + UnknownCandidateKey, + /// Two candidates bind the same canonical key. + DuplicateCandidateBinding, + /// Selection was asked to order an empty candidate set. + EmptyCandidateSet, +} + +/// One opaque canonical candidate key. +/// +/// The key is authored identity: its bytes are the only property the release +/// may ever inspect. No RGB bytes, declaration index, distance or weight +/// semantics are read from it anywhere in this module. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub(crate) struct SelectionCandidateKeyV1(Box<[u8]>); + +impl SelectionCandidateKeyV1 { + pub(crate) fn new(bytes: Box<[u8]>) -> Self { + Self(bytes) + } + + fn as_bytes(&self) -> &[u8] { + &self.0 + } +} + +/// The authored release shape before admission. +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct SelectionReleaseV1 { + revision: u64, + rank_groups: Box<[Box<[SelectionCandidateKeyV1]>]>, +} + +impl SelectionReleaseV1 { + pub(crate) fn new(revision: u64, rank_groups: Box<[Box<[SelectionCandidateKeyV1]>]>) -> Self { + Self { + revision, + rank_groups, + } + } +} + +/// A sealed selection release: the total preorder is admitted, canonical and +/// content-addressed. +#[derive(Debug, Clone)] +pub(crate) struct AdmittedSelectionReleaseV1 { + revision: u64, + identity: [u8; 32], + ranks: BTreeMap, usize>, +} + +/// Admit the exact authored release into its sealed canonical form. +/// +/// Key order inside one tie group is not policy: groups are canonicalised by +/// sorting their keys byte-wise before the identity is computed, so authored +/// permutations inside a group seal identically. Any shape that cannot form a +/// total preorder is a typed rejection, never a panic. +pub(crate) fn admit_selection_release_v1( + release: SelectionReleaseV1, +) -> Result { + if release.rank_groups.is_empty() { + return Err(SelectionReleaseErrorV1::EmptyRelease); + } + let mut ranks: BTreeMap, usize> = BTreeMap::new(); + let mut hasher = sha256::Hasher::new(); + hasher.update(IDENTITY_DOMAIN_V1); + hasher.update(&release.revision.to_be_bytes()); + hasher.update( + &u32::try_from(release.rank_groups.len()) + .unwrap() + .to_be_bytes(), + ); + for (rank, group) in release.rank_groups.iter().enumerate() { + if group.is_empty() { + return Err(SelectionReleaseErrorV1::EmptyRankGroup); + } + let mut keys = group + .iter() + .map(|key| key.as_bytes().to_vec()) + .collect::>(); + keys.sort(); + hasher.update(&u32::try_from(keys.len()).unwrap().to_be_bytes()); + for key in keys { + if key.is_empty() { + return Err(SelectionReleaseErrorV1::EmptyCandidateKey); + } + if ranks.insert(key.clone(), rank).is_some() { + return Err(SelectionReleaseErrorV1::DuplicateCandidateKey); + } + hasher.update(&u32::try_from(key.len()).unwrap().to_be_bytes()); + hasher.update(&key); + } + } + Ok(AdmittedSelectionReleaseV1 { + revision: release.revision, + identity: hasher.finalize().as_bytes().to_owned(), + ranks, + }) +} + +impl AdmittedSelectionReleaseV1 { + /// The content-addressed identity of the sealed release. + pub(crate) fn identity(&self) -> [u8; 32] { + self.identity + } + + /// The release revision the identity is bound to. + #[allow(dead_code)] + pub(crate) fn revision(&self) -> u64 { + self.revision + } + + /// The preorder rank of one canonical key, when the release ranks it. + pub(crate) fn rank_of(&self, key: &SelectionCandidateKeyV1) -> Option { + self.ranks.get(key.as_bytes()).copied() + } + + /// Materialise the total order of one candidate set from the release. + /// + /// Candidates are ordered by the admitted rank first and by the canonical + /// key bytes second; nothing else is inspected. Every key must be ranked + /// by the release and bound by exactly one candidate. + pub(crate) fn select_order_v1( + &self, + candidates: &[(C, SelectionCandidateKeyV1)], + ) -> Result, SelectionReleaseErrorV1> { + if candidates.is_empty() { + return Err(SelectionReleaseErrorV1::EmptyCandidateSet); + } + let mut ranked = Vec::with_capacity(candidates.len()); + let mut bound: BTreeMap<&[u8], ()> = BTreeMap::new(); + for (payload, key) in candidates { + let rank = self + .ranks + .get(key.as_bytes()) + .copied() + .ok_or(SelectionReleaseErrorV1::UnknownCandidateKey)?; + if bound.insert(key.as_bytes(), ()).is_some() { + return Err(SelectionReleaseErrorV1::DuplicateCandidateBinding); + } + ranked.push((rank, key.as_bytes(), payload)); + } + ranked.sort_by(|left, right| left.0.cmp(&right.0).then_with(|| left.1.cmp(right.1))); + Ok(ranked + .into_iter() + .map(|(_, _, payload)| payload.clone()) + .collect()) + } +} From 232e5e69e6e2d101d1239df7831abdaad39b041b Mon Sep 17 00:00:00 2001 From: Claude Code Date: Wed, 5 Aug 2026 11:39:15 +0300 Subject: [PATCH 3/5] Style: format the selection release hostile contract --- .../src/selection_release_tests.rs | 44 ++++++++----------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/crates/labcolors-core/src/selection_release_tests.rs b/crates/labcolors-core/src/selection_release_tests.rs index 5a11d01c..60c34395 100644 --- a/crates/labcolors-core/src/selection_release_tests.rs +++ b/crates/labcolors-core/src/selection_release_tests.rs @@ -11,18 +11,15 @@ //! rank and the canonical key. use crate::selection_release::{ - admit_selection_release_v1, SelectionCandidateKeyV1, SelectionReleaseErrorV1, - SelectionReleaseV1, + SelectionCandidateKeyV1, SelectionReleaseErrorV1, SelectionReleaseV1, + admit_selection_release_v1, }; fn key(bytes: &[u8]) -> SelectionCandidateKeyV1 { SelectionCandidateKeyV1::new(bytes.to_vec().into_boxed_slice()) } -fn release( - revision: u64, - groups: &[&[&[u8]]], -) -> SelectionReleaseV1 { +fn release(revision: u64, groups: &[&[&[u8]]]) -> SelectionReleaseV1 { SelectionReleaseV1::new( revision, groups @@ -54,7 +51,7 @@ fn admission_rejects_every_foreign_release_shape() { Err(SelectionReleaseErrorV1::EmptyRankGroup) )); // an empty canonical key - let rejected = admit_selection_release_v1(release(1, &[&[b""] ])); + let rejected = admit_selection_release_v1(release(1, &[&[b""]])); assert!(matches!( rejected, Err(SelectionReleaseErrorV1::EmptyCandidateKey) @@ -87,18 +84,12 @@ fn identity_is_content_addressed_and_revision_bound() { #[test] fn key_permutation_inside_a_tie_group_is_not_policy() { - let canonical = - admit_selection_release_v1(release(1, &[&[b"zeta", b"alpha"], &[b"beta"]])) - .expect("authored release must admit"); - let permuted = - admit_selection_release_v1(release(1, &[&[b"alpha", b"zeta"], &[b"beta"]])) - .expect("permuted tie group must admit"); + let canonical = admit_selection_release_v1(release(1, &[&[b"zeta", b"alpha"], &[b"beta"]])) + .expect("authored release must admit"); + let permuted = admit_selection_release_v1(release(1, &[&[b"alpha", b"zeta"], &[b"beta"]])) + .expect("permuted tie group must admit"); assert_eq!(canonical.identity(), permuted.identity()); - let candidates = [ - (1u32, key(b"zeta")), - (2, key(b"alpha")), - (3, key(b"beta")), - ]; + let candidates = [(1u32, key(b"zeta")), (2, key(b"alpha")), (3, key(b"beta"))]; assert_eq!( canonical.select_order_v1(&candidates).unwrap(), permuted.select_order_v1(&candidates).unwrap() @@ -117,9 +108,8 @@ fn total_preorder_ranks_follow_the_authored_groups() { #[test] fn selection_orders_by_rank_then_canonical_key_bytes_only() { - let admitted = - admit_selection_release_v1(release(1, &[&[b"zz", b"aa"], &[b"mm"], &[b"bb"]])) - .expect("authored release must admit"); + let admitted = admit_selection_release_v1(release(1, &[&[b"zz", b"aa"], &[b"mm"], &[b"bb"]])) + .expect("authored release must admit"); let candidates = [ ("last-declared", key(b"mm")), ("first-declared", key(b"zz")), @@ -131,7 +121,12 @@ fn selection_orders_by_rank_then_canonical_key_bytes_only() { // declaration position assert_eq!( selected.as_ref(), - ["middle-declared", "first-declared", "last-declared", "tail-declared"] + [ + "middle-declared", + "first-declared", + "last-declared", + "tail-declared" + ] ); // permuting the candidate input order never changes the selection order let shuffled = [ @@ -169,9 +164,8 @@ fn selection_rejects_every_foreign_binding() { #[test] fn bijective_payload_relabeling_preserves_the_order_structure() { - let admitted = - admit_selection_release_v1(release(1, &[&[b"k1", b"k2"], &[b"k3"]])) - .expect("authored release must admit"); + let admitted = admit_selection_release_v1(release(1, &[&[b"k1", b"k2"], &[b"k3"]])) + .expect("authored release must admit"); let candidates = [(10u32, key(b"k2")), (20, key(b"k3")), (30, key(b"k1"))]; let original = admitted.select_order_v1(&candidates).unwrap(); let relabeled_candidates = candidates From d4181233e539b5bf254f65974804b97efc95527a Mon Sep 17 00:00:00 2001 From: Claude Code Date: Wed, 5 Aug 2026 12:04:54 +0300 Subject: [PATCH 4/5] Rebind the pinned inventories to the selection release module registration lib.rs now registers selection_release, so the point-support source capsule and the clean-set module-registration artifact are rebound to the new byte digests without any semantic change. --- .../contracts/clean-set-srgb8-v1/receipt-v1.json | 4 ++-- .../contracts/clean-set-srgb8-v1/receipt-v1.sha256 | 2 +- .../point-support-reference-surplus-q55-bps-proof-v1.json | 2 +- scripts/verify_point_support_surplus.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/labcolors-core/contracts/clean-set-srgb8-v1/receipt-v1.json b/crates/labcolors-core/contracts/clean-set-srgb8-v1/receipt-v1.json index 4d1432f2..f2fa749d 100644 --- a/crates/labcolors-core/contracts/clean-set-srgb8-v1/receipt-v1.json +++ b/crates/labcolors-core/contracts/clean-set-srgb8-v1/receipt-v1.json @@ -48,11 +48,11 @@ "sha256": "8bf5d5a1c0f00ce245a1ecb18b923aa1631483345962d72b633e466c242a8a1d" }, { - "bytes": 14163, + "bytes": 14398, "license": "MIT", "path": "crates/labcolors-core/src/lib.rs", "role": "module_registration_source", - "sha256": "9c5493cc2d4dcca04dc989f61431e126d3f4ae973f284af0bac232059192dfed" + "sha256": "1f8cf074c2ebde44839c5bfe362fbfbd95634ff1b02039271434855c704ea697" }, { "bytes": 39439, diff --git a/crates/labcolors-core/contracts/clean-set-srgb8-v1/receipt-v1.sha256 b/crates/labcolors-core/contracts/clean-set-srgb8-v1/receipt-v1.sha256 index 59f64392..cabd9658 100644 --- a/crates/labcolors-core/contracts/clean-set-srgb8-v1/receipt-v1.sha256 +++ b/crates/labcolors-core/contracts/clean-set-srgb8-v1/receipt-v1.sha256 @@ -1 +1 @@ -203ad6b59400ddbfbb18269d56447913e2e802809dd1ce61b4e4dbafadbdb8c8 receipt-v1.json +000c8849218bc760a7d14c8f0bb0af52aeab9b0753dcdcdea9f1dd83dee580a4 receipt-v1.json diff --git a/crates/labcolors-core/contracts/point-support-reference-surplus-q55-bps-proof-v1.json b/crates/labcolors-core/contracts/point-support-reference-surplus-q55-bps-proof-v1.json index dd7ff9c2..60a3bb33 100644 --- a/crates/labcolors-core/contracts/point-support-reference-surplus-q55-bps-proof-v1.json +++ b/crates/labcolors-core/contracts/point-support-reference-surplus-q55-bps-proof-v1.json @@ -1 +1 @@ -{"artifact_id":"wcag22-srgb8-luminance-q55-v1","basis_point_proof":{"checks":30,"drop_all_semantics":"zero required surplus; current must still meet the anchor","drop_domain_inclusive":[0,10000],"nonpositive_baseline_semantics":"zero required surplus; current must meet the anchor"},"bound_id":"point-support-reference-surplus-q55-bps-v1","certified_claim":"for every successfully evaluated enabled stability cell, decision is Retained iff current_lower_surplus >= (10000-drop_bps)/10000 * max(baseline_lower_surplus,0); the declared anchor remains a separate hard floor","comparator_proof":{"algorithm":"euclidean-continued-fraction-ordering-v1","dense_denominator_inclusive":[1,31],"dense_numerator_inclusive":[0,31],"dense_small_cases":984064,"invariant":"equal integer parts; reciprocal proper fractions reverse order","largest_fibonacci_index":186,"oracle":"unbounded-integer-cross-product","random_cases":250000,"random_corpus_sha256":"97c4af7b452b31a4ab92645f70c17acb38bf57ca55484e32ad9d7d79d97a333d","random_seed":210583930,"termination":"each nonterminal denominator becomes a strictly smaller remainder","u128_adversarial_cases":190},"declared_operation_law":"q55-lower-reference-distance-explicit-anchor-bps-retention-v1","excluded_claim":"does not certify retention against the unknown exact baseline surplus, renderer equivalence outside encoded-sRGB8 source-over, or a successful result when evaluation fails","integer_replay_envelope":{"assumption":"every Q55 luminance upper <= scale + 3","i128_max":170141183460469231731687303715884105727,"offset_cleared_denominator_max":756604737398243388,"positive_baseline_numerator_max":1188950301625811064,"rational_denominator_max":1513209474796486776,"required_denominator_max":15132094747964867760000,"required_numerator_max":11889503016258110640000,"signed_anchor_abs_coarse_max":5296233161787703716,"u128_max":340282366920938463463374607431768211455,"u64_max":18446744073709551615},"profile_id":"srgb8-q55-retained-reference-surplus-bps-v1","proof_id":"point-support-reference-surplus-integer-v1","proof_payload_sha256":"c39f7525bcfec599a884dcb80e492442b08fbf31435032e0af7d3ce3f5146133","q55_dependency":{"artifact_id":"wcag22-srgb8-luminance-q55-v1","artifact_sha256":"7ff239d9052b346f3c50da01ca65ca2330892ed1a3ff30e190797fcef6f03604","maximum_luminance_upper":36028797018963971,"outward_interval_width_bound":3,"proof_id":"wcag22-srgb8-full-domain-q55-v1","proof_payload_sha256":"3c639a7c875046c46b56b51ecdd67d5ecaf14a1134490c88a222e7037b63c0f2","proof_sha256":"ac59cf89503170c789223b91d775213a19d4e571ef930f2ea609fcd51b14defd","q55_scale":36028797018963968},"reference_and_anchor_proof":{"anchor_identity_checks":75,"orientation_law":"distance-magnitude-symmetric-orientation-reported-separately","overlap_lower_distance":"0/1","separated_endpoint_checks":504},"schema_version":2,"site_id":"point-support-retained-reference-surplus-v1","source_binding_exclusions":["whole-crate compilation or compiler/toolchain attestation","binary, package, FFI, renderer, or browser transport attestation","unrelated Lab Colors modules outside the declared point-support semantic cone"],"source_binding_law":"point-support-rust-whole-file-semantic-cone-v2","source_binding_schema_version":2,"source_binding_scope":"exact bytes of the private point-support Rust semantic cone and its two WCAG include_str inputs; comments and cfg(test) text are intentionally significant","source_closure_sha256":"06b26e431934f311199313d703939aa19bdbd398e41adf712eac282895378c10","source_files":[{"kind":"compile-time-input","path":"crates/labcolors-core/contracts/wcag22-srgb8-q55-proof-v1.json","sha256":"ac59cf89503170c789223b91d775213a19d4e571ef930f2ea609fcd51b14defd"},{"kind":"compile-time-input","path":"crates/labcolors-core/contracts/wcag22-srgb8-v1.json","sha256":"b4bb7e5f17a99f2c911fdbe3da23a48b049277b796291094950f14680cc3cc7b"},{"kind":"rust-source","path":"crates/labcolors-core/src/appearance.rs","sha256":"fecab7250ec96171daf3d6fe75c663e45b2356a2b0a8bb6f5ee418c9d62654a1"},{"kind":"rust-source","path":"crates/labcolors-core/src/composition.rs","sha256":"0afc3232dd0c3b5b94596813d0a5e8aef4a2dea74b09dbae05f41e64d4c8ca1b"},{"kind":"rust-source","path":"crates/labcolors-core/src/constraints/exact.rs","sha256":"892576a8621185352583e63dc0a1aacac32e32a8063b6fe24ae16d4ff9dce7cb"},{"kind":"rust-source","path":"crates/labcolors-core/src/constraints/mod.rs","sha256":"f18bcafd7f911e84049cf9dae9673d6d6e0ed1fd50a9a7635a0ff597953b7727"},{"kind":"rust-source","path":"crates/labcolors-core/src/constraints/wcag22.rs","sha256":"856093c91159d8b3faab001f2d6524d33d7b16458a5a4e98ea65f8c62ab2694c"},{"kind":"rust-source","path":"crates/labcolors-core/src/hash.rs","sha256":"f97a0fd7d6ad3162f0f1dfb326fccfb7ed40da9a8fa67a5b8a239a1ae2ae49c3"},{"kind":"rust-source","path":"crates/labcolors-core/src/lcs_occurrence.rs","sha256":"78d37406e9bdc37f126b72987c9c92b452c13b3233c0aeb0a75ed25dadb83a68"},{"kind":"rust-source","path":"crates/labcolors-core/src/lib.rs","sha256":"9c5493cc2d4dcca04dc989f61431e126d3f4ae973f284af0bac232059192dfed"},{"kind":"rust-source","path":"crates/labcolors-core/src/numerics.rs","sha256":"e73a12136494f2ef9aca4e943ab38302c1439f054cecab36a552d35252c164f9"},{"kind":"rust-source","path":"crates/labcolors-core/src/observation.rs","sha256":"b0cfec5c9fe798abd5492260aac3caf87685f7e27f4f9628b01f386ef3f6ac7d"},{"kind":"rust-source","path":"crates/labcolors-core/src/point_support.rs","sha256":"6f6a376ff036d3d65960c004e6566e1bca580f19f5bd3cd333a80b0da5b5c242"},{"kind":"rust-source","path":"crates/labcolors-core/src/session.rs","sha256":"38c65b46a3e392b399092e8c4834d7f31acb2b91eff626d6faa1b1aaa90b52f3"},{"kind":"rust-source","path":"crates/labcolors-core/src/srgb8.rs","sha256":"6c95324eb05476f35f75375a9af0b2b4a41b8b2978c46e67d2ce1aea5adde342"},{"kind":"rust-source","path":"crates/labcolors-core/src/wcag22.rs","sha256":"7ba7864eb7e73789bad6c63c64a4dc2dcc08c2da6921375fb9564fca230c2780"},{"kind":"rust-source","path":"crates/labcolors-core/src/wcag22/kernel.rs","sha256":"c97980c1ca2c7ea9cabff9c8d2fb7282773cca180ae15948391c29c9d6196040"},{"kind":"rust-source","path":"crates/labcolors-core/src/wcag22/q55_data.rs","sha256":"af4d23d6b70c45ce6efa839e7dda4bb0a61f6aae43cb805af6fa9b29e6c3bae2"},{"kind":"rust-source","path":"crates/labcolors-core/src/wcag22_evidence.rs","sha256":"3c5a75b07254c6071a64700af208a64987d0f0ea9698eadc54a9e74585ce1f72"}],"source_negative_controls":43,"universal_algebraic_certificate":{"basis_point_scale_instantiation":10000,"domain":"integers; Q55 scale Q>0; anchor L>=D>=0; lighter monotonicity L2>=L1>D>=0; darker monotonicity L>D2>=D1>=0; current/baseline denominators b,q>0; basis-point scale B>0 instantiated as 10000; p>0; a>=0; 0<=drop_bps<=B","identities":["three explicit anchor-surplus formulas after denominator clearing","reference distance is monotone increasing in lighter L","reference distance is monotone decreasing in darker D","positive-baseline retained threshold is p*(B-drop)/(q*B)","a/b >= p*(B-drop)/(q*B) iff a*q*B >= p*(B-drop)*b"],"method":"exact-sparse-integer-polynomial-identities-plus-positive-denominator-order-lemma-v1","nonpositive_baseline_case":"max(baseline,0)=0; retained threshold is exactly zero","symbolic_mutation_controls":{"anchor_coefficients_and_denominator":6,"retained_cross_product":5},"wolfram_language_cross_check":{"query":"FullSimplify[{20 g/d - 0 == 20 g/d, 20 g/d - 2 == (20 g - 2 d)/d, 20 g/d - 7/2 == (40 g - 7 d)/(2 d), Equivalent[a/b >= p (s-x)/(q s), a q s >= p (s-x) b], Max[p/q, 0] (s-x)/s == Piecewise[{{0, p <= 0}}, p (s-x)/(q s)]}, Assumptions -> Element[{a,b,p,q,s,x,g,d}, Integers] && a >= 0 && b > 0 && q > 0 && s > 0 && 0 <= x <= s && d > 0 && g >= 0]","query_sha256":"8cdbb9964583030c8b92498961896cb2a98613f1cb31eb7c54acdf8e16beff10","result":"{True, True, True, True, True}","result_sha256":"13a8f2ee8d0fde335a638e46d7cc8a8427b9a1437c77d22cfcf925bb87fa6303"}},"verifier_sha256":"1c2362cd754725a4c21f6c3d6762aa869a9cfceff37c16bd10cab980c6d99c60"} +{"artifact_id":"wcag22-srgb8-luminance-q55-v1","basis_point_proof":{"checks":30,"drop_all_semantics":"zero required surplus; current must still meet the anchor","drop_domain_inclusive":[0,10000],"nonpositive_baseline_semantics":"zero required surplus; current must meet the anchor"},"bound_id":"point-support-reference-surplus-q55-bps-v1","certified_claim":"for every successfully evaluated enabled stability cell, decision is Retained iff current_lower_surplus >= (10000-drop_bps)/10000 * max(baseline_lower_surplus,0); the declared anchor remains a separate hard floor","comparator_proof":{"algorithm":"euclidean-continued-fraction-ordering-v1","dense_denominator_inclusive":[1,31],"dense_numerator_inclusive":[0,31],"dense_small_cases":984064,"invariant":"equal integer parts; reciprocal proper fractions reverse order","largest_fibonacci_index":186,"oracle":"unbounded-integer-cross-product","random_cases":250000,"random_corpus_sha256":"97c4af7b452b31a4ab92645f70c17acb38bf57ca55484e32ad9d7d79d97a333d","random_seed":210583930,"termination":"each nonterminal denominator becomes a strictly smaller remainder","u128_adversarial_cases":190},"declared_operation_law":"q55-lower-reference-distance-explicit-anchor-bps-retention-v1","excluded_claim":"does not certify retention against the unknown exact baseline surplus, renderer equivalence outside encoded-sRGB8 source-over, or a successful result when evaluation fails","integer_replay_envelope":{"assumption":"every Q55 luminance upper <= scale + 3","i128_max":170141183460469231731687303715884105727,"offset_cleared_denominator_max":756604737398243388,"positive_baseline_numerator_max":1188950301625811064,"rational_denominator_max":1513209474796486776,"required_denominator_max":15132094747964867760000,"required_numerator_max":11889503016258110640000,"signed_anchor_abs_coarse_max":5296233161787703716,"u128_max":340282366920938463463374607431768211455,"u64_max":18446744073709551615},"profile_id":"srgb8-q55-retained-reference-surplus-bps-v1","proof_id":"point-support-reference-surplus-integer-v1","proof_payload_sha256":"7f9e7079ecea24eb67b87e13e5ac817640e10b5e38e6c1f348bd5a4c534c5606","q55_dependency":{"artifact_id":"wcag22-srgb8-luminance-q55-v1","artifact_sha256":"7ff239d9052b346f3c50da01ca65ca2330892ed1a3ff30e190797fcef6f03604","maximum_luminance_upper":36028797018963971,"outward_interval_width_bound":3,"proof_id":"wcag22-srgb8-full-domain-q55-v1","proof_payload_sha256":"3c639a7c875046c46b56b51ecdd67d5ecaf14a1134490c88a222e7037b63c0f2","proof_sha256":"ac59cf89503170c789223b91d775213a19d4e571ef930f2ea609fcd51b14defd","q55_scale":36028797018963968},"reference_and_anchor_proof":{"anchor_identity_checks":75,"orientation_law":"distance-magnitude-symmetric-orientation-reported-separately","overlap_lower_distance":"0/1","separated_endpoint_checks":504},"schema_version":2,"site_id":"point-support-retained-reference-surplus-v1","source_binding_exclusions":["whole-crate compilation or compiler/toolchain attestation","binary, package, FFI, renderer, or browser transport attestation","unrelated Lab Colors modules outside the declared point-support semantic cone"],"source_binding_law":"point-support-rust-whole-file-semantic-cone-v2","source_binding_schema_version":2,"source_binding_scope":"exact bytes of the private point-support Rust semantic cone and its two WCAG include_str inputs; comments and cfg(test) text are intentionally significant","source_closure_sha256":"aad05b43983f2c3a45e43473be5fad61c4a535d628e7228c5842295864681885","source_files":[{"kind":"compile-time-input","path":"crates/labcolors-core/contracts/wcag22-srgb8-q55-proof-v1.json","sha256":"ac59cf89503170c789223b91d775213a19d4e571ef930f2ea609fcd51b14defd"},{"kind":"compile-time-input","path":"crates/labcolors-core/contracts/wcag22-srgb8-v1.json","sha256":"b4bb7e5f17a99f2c911fdbe3da23a48b049277b796291094950f14680cc3cc7b"},{"kind":"rust-source","path":"crates/labcolors-core/src/appearance.rs","sha256":"fecab7250ec96171daf3d6fe75c663e45b2356a2b0a8bb6f5ee418c9d62654a1"},{"kind":"rust-source","path":"crates/labcolors-core/src/composition.rs","sha256":"0afc3232dd0c3b5b94596813d0a5e8aef4a2dea74b09dbae05f41e64d4c8ca1b"},{"kind":"rust-source","path":"crates/labcolors-core/src/constraints/exact.rs","sha256":"892576a8621185352583e63dc0a1aacac32e32a8063b6fe24ae16d4ff9dce7cb"},{"kind":"rust-source","path":"crates/labcolors-core/src/constraints/mod.rs","sha256":"f18bcafd7f911e84049cf9dae9673d6d6e0ed1fd50a9a7635a0ff597953b7727"},{"kind":"rust-source","path":"crates/labcolors-core/src/constraints/wcag22.rs","sha256":"856093c91159d8b3faab001f2d6524d33d7b16458a5a4e98ea65f8c62ab2694c"},{"kind":"rust-source","path":"crates/labcolors-core/src/hash.rs","sha256":"f97a0fd7d6ad3162f0f1dfb326fccfb7ed40da9a8fa67a5b8a239a1ae2ae49c3"},{"kind":"rust-source","path":"crates/labcolors-core/src/lcs_occurrence.rs","sha256":"78d37406e9bdc37f126b72987c9c92b452c13b3233c0aeb0a75ed25dadb83a68"},{"kind":"rust-source","path":"crates/labcolors-core/src/lib.rs","sha256":"1f8cf074c2ebde44839c5bfe362fbfbd95634ff1b02039271434855c704ea697"},{"kind":"rust-source","path":"crates/labcolors-core/src/numerics.rs","sha256":"e73a12136494f2ef9aca4e943ab38302c1439f054cecab36a552d35252c164f9"},{"kind":"rust-source","path":"crates/labcolors-core/src/observation.rs","sha256":"b0cfec5c9fe798abd5492260aac3caf87685f7e27f4f9628b01f386ef3f6ac7d"},{"kind":"rust-source","path":"crates/labcolors-core/src/point_support.rs","sha256":"6f6a376ff036d3d65960c004e6566e1bca580f19f5bd3cd333a80b0da5b5c242"},{"kind":"rust-source","path":"crates/labcolors-core/src/session.rs","sha256":"38c65b46a3e392b399092e8c4834d7f31acb2b91eff626d6faa1b1aaa90b52f3"},{"kind":"rust-source","path":"crates/labcolors-core/src/srgb8.rs","sha256":"6c95324eb05476f35f75375a9af0b2b4a41b8b2978c46e67d2ce1aea5adde342"},{"kind":"rust-source","path":"crates/labcolors-core/src/wcag22.rs","sha256":"7ba7864eb7e73789bad6c63c64a4dc2dcc08c2da6921375fb9564fca230c2780"},{"kind":"rust-source","path":"crates/labcolors-core/src/wcag22/kernel.rs","sha256":"c97980c1ca2c7ea9cabff9c8d2fb7282773cca180ae15948391c29c9d6196040"},{"kind":"rust-source","path":"crates/labcolors-core/src/wcag22/q55_data.rs","sha256":"af4d23d6b70c45ce6efa839e7dda4bb0a61f6aae43cb805af6fa9b29e6c3bae2"},{"kind":"rust-source","path":"crates/labcolors-core/src/wcag22_evidence.rs","sha256":"3c5a75b07254c6071a64700af208a64987d0f0ea9698eadc54a9e74585ce1f72"}],"source_negative_controls":43,"universal_algebraic_certificate":{"basis_point_scale_instantiation":10000,"domain":"integers; Q55 scale Q>0; anchor L>=D>=0; lighter monotonicity L2>=L1>D>=0; darker monotonicity L>D2>=D1>=0; current/baseline denominators b,q>0; basis-point scale B>0 instantiated as 10000; p>0; a>=0; 0<=drop_bps<=B","identities":["three explicit anchor-surplus formulas after denominator clearing","reference distance is monotone increasing in lighter L","reference distance is monotone decreasing in darker D","positive-baseline retained threshold is p*(B-drop)/(q*B)","a/b >= p*(B-drop)/(q*B) iff a*q*B >= p*(B-drop)*b"],"method":"exact-sparse-integer-polynomial-identities-plus-positive-denominator-order-lemma-v1","nonpositive_baseline_case":"max(baseline,0)=0; retained threshold is exactly zero","symbolic_mutation_controls":{"anchor_coefficients_and_denominator":6,"retained_cross_product":5},"wolfram_language_cross_check":{"query":"FullSimplify[{20 g/d - 0 == 20 g/d, 20 g/d - 2 == (20 g - 2 d)/d, 20 g/d - 7/2 == (40 g - 7 d)/(2 d), Equivalent[a/b >= p (s-x)/(q s), a q s >= p (s-x) b], Max[p/q, 0] (s-x)/s == Piecewise[{{0, p <= 0}}, p (s-x)/(q s)]}, Assumptions -> Element[{a,b,p,q,s,x,g,d}, Integers] && a >= 0 && b > 0 && q > 0 && s > 0 && 0 <= x <= s && d > 0 && g >= 0]","query_sha256":"8cdbb9964583030c8b92498961896cb2a98613f1cb31eb7c54acdf8e16beff10","result":"{True, True, True, True, True}","result_sha256":"13a8f2ee8d0fde335a638e46d7cc8a8427b9a1437c77d22cfcf925bb87fa6303"}},"verifier_sha256":"3eed0848565097e9fac539f92c0051b2c272cdfe16d11556c5676a6601148cd0"} diff --git a/scripts/verify_point_support_surplus.py b/scripts/verify_point_support_surplus.py index 09ef450e..727a999b 100755 --- a/scripts/verify_point_support_surplus.py +++ b/scripts/verify_point_support_surplus.py @@ -58,7 +58,7 @@ SOURCE_BINDING_LAW = "point-support-rust-whole-file-semantic-cone-v2" SOURCE_BINDING_DOMAIN = b"labcolors.point-support.rust-whole-file-semantic-cone.v2" EXPECTED_SOURCE_CAPSULE_SHA256 = ( - "06b26e431934f311199313d703939aa19bdbd398e41adf712eac282895378c10" + "aad05b43983f2c3a45e43473be5fad61c4a535d628e7228c5842295864681885" ) EXPECTED_Q55_PROOF_SHA256 = ( "ac59cf89503170c789223b91d775213a19d4e571ef930f2ea609fcd51b14defd" From 30971331dc5f629ed981f0fd7c60faa48132f0bd Mon Sep 17 00:00:00 2001 From: Claude Code Date: Wed, 5 Aug 2026 12:20:40 +0300 Subject: [PATCH 5/5] Address review: typed length-field rejection and identity boundary test Admission now rejects oversized length fields through a typed ReleaseShapeOverflow verdict instead of panicking, and a hostile test pins that the length-prefixed identity grammar separates key and group boundaries for releases with identical joined key bytes. --- .../labcolors-core/src/selection_release.rs | 23 +++++++++++++------ .../src/selection_release_tests.rs | 14 +++++++++++ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/crates/labcolors-core/src/selection_release.rs b/crates/labcolors-core/src/selection_release.rs index f631ba3b..bcc00617 100644 --- a/crates/labcolors-core/src/selection_release.rs +++ b/crates/labcolors-core/src/selection_release.rs @@ -32,6 +32,9 @@ pub(crate) enum SelectionReleaseErrorV1 { DuplicateCandidateBinding, /// Selection was asked to order an empty candidate set. EmptyCandidateSet, + /// A release length field cannot be encoded, so the identity grammar is + /// unrepresentable. + ReleaseShapeOverflow, } /// One opaque canonical candidate key. @@ -77,6 +80,16 @@ pub(crate) struct AdmittedSelectionReleaseV1 { ranks: BTreeMap, usize>, } +/// One u32 length field of the identity grammar. +/// +/// The encoding is fail-closed: a length that does not fit u32 is a typed +/// rejection, never a panic. +fn length_field_v1(value: usize) -> Result<[u8; 4], SelectionReleaseErrorV1> { + Ok(u32::try_from(value) + .map_err(|_| SelectionReleaseErrorV1::ReleaseShapeOverflow)? + .to_be_bytes()) +} + /// Admit the exact authored release into its sealed canonical form. /// /// Key order inside one tie group is not policy: groups are canonicalised by @@ -93,11 +106,7 @@ pub(crate) fn admit_selection_release_v1( let mut hasher = sha256::Hasher::new(); hasher.update(IDENTITY_DOMAIN_V1); hasher.update(&release.revision.to_be_bytes()); - hasher.update( - &u32::try_from(release.rank_groups.len()) - .unwrap() - .to_be_bytes(), - ); + hasher.update(&length_field_v1(release.rank_groups.len())?); for (rank, group) in release.rank_groups.iter().enumerate() { if group.is_empty() { return Err(SelectionReleaseErrorV1::EmptyRankGroup); @@ -107,7 +116,7 @@ pub(crate) fn admit_selection_release_v1( .map(|key| key.as_bytes().to_vec()) .collect::>(); keys.sort(); - hasher.update(&u32::try_from(keys.len()).unwrap().to_be_bytes()); + hasher.update(&length_field_v1(keys.len())?); for key in keys { if key.is_empty() { return Err(SelectionReleaseErrorV1::EmptyCandidateKey); @@ -115,7 +124,7 @@ pub(crate) fn admit_selection_release_v1( if ranks.insert(key.clone(), rank).is_some() { return Err(SelectionReleaseErrorV1::DuplicateCandidateKey); } - hasher.update(&u32::try_from(key.len()).unwrap().to_be_bytes()); + hasher.update(&length_field_v1(key.len())?); hasher.update(&key); } } diff --git a/crates/labcolors-core/src/selection_release_tests.rs b/crates/labcolors-core/src/selection_release_tests.rs index 60c34395..235dc3a0 100644 --- a/crates/labcolors-core/src/selection_release_tests.rs +++ b/crates/labcolors-core/src/selection_release_tests.rs @@ -82,6 +82,20 @@ fn identity_is_content_addressed_and_revision_bound() { assert_ne!(first.identity(), renumbered.identity()); } +#[test] +fn identity_length_fields_separate_key_and_group_boundaries() { + let joined = + admit_selection_release_v1(release(1, &[&[b"ab"]])).expect("single joined key must admit"); + let split = admit_selection_release_v1(release(1, &[&[b"a", b"b"]])) + .expect("split keys with the same joined bytes must admit"); + assert_ne!(joined.identity(), split.identity()); + let left_split = admit_selection_release_v1(release(1, &[&[b"ab", b"c"]])) + .expect("left key split must admit"); + let right_split = admit_selection_release_v1(release(1, &[&[b"a", b"bc"]])) + .expect("right key split with the same joined bytes must admit"); + assert_ne!(left_split.identity(), right_split.identity()); +} + #[test] fn key_permutation_inside_a_tie_group_is_not_policy() { let canonical = admit_selection_release_v1(release(1, &[&[b"zeta", b"alpha"], &[b"beta"]]))