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 791dd29d..08055b1a 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 @@ -104,11 +104,11 @@ "sha256": "83279cfa6d46b15f2a41e8849422a934613fc80f4554b73a156be5a4b9deeffc" }, { - "bytes": 227119, + "bytes": 235762, "license": "MIT", "path": "crates/labcolors-core/src/program_session.rs", "role": "program_source", - "sha256": "ba097479052d32a71b5d167d63c04b35928c7e888945eddbab6b6acf49c0ea5c" + "sha256": "7ae542be3b4f10978c643906360eb3ea61710752b13736d9f07ff384575cdb8e" }, { "bytes": 21872, @@ -125,11 +125,11 @@ "sha256": "aa6aa7c0b630437f1c1ba8c2ceafb0dadf6551c42331559504076a6cd44e6331" }, { - "bytes": 27055, + "bytes": 27141, "license": "MIT", "path": "crates/labcolors-core/src/session.rs", "role": "session_runtime_source", - "sha256": "38c65b46a3e392b399092e8c4834d7f31acb2b91eff626d6faa1b1aaa90b52f3" + "sha256": "67245bc8b35f32edbd159ad5ffed4a26c5e276a6ef44d5e4ab319a5ab17b4d4a" }, { "bytes": 34105, 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 e209c889..4b743754 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 @@ -ac397fa06ead9b3c981653271e8a019f8909306e8125f07f35eb5ab249d346eb receipt-v1.json +3d333ace96cbc66fa3e29f6321e2093a723eb8aa338bfe441bba994f558848e0 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 676141cb..7b9e9e47 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":"d28c25723d1adab50f2ce2b206fcbfa8852a213edf0d86a023f390d2c33ca931","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":"719de715c1324b6112342fbc7bdeec4f8a4c96576e5e77b46c655aa8ba151186","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":"81a6e6e2a9d3f382a5cb094585584a9dc1be2ebaa38c214de271d65850f08e5f"},{"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":"2f52bd1abb4042f020a533496dff8053242d99b243d4f109b1b9a802aefb5dce"} +{"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":"cff8ea394a8e3ed587db40e7e08e7a5e2e472b832b029c0d557628efefe2278c","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":"eed5e08f605e6d82f3b8409e14d44a47b41247a37ed056c779faf324f488bbdb","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":"81a6e6e2a9d3f382a5cb094585584a9dc1be2ebaa38c214de271d65850f08e5f"},{"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":"f8c12113a1487cc33dc289621d16e54ef84ba99a65bc370dead1da185228f17e"},{"kind":"rust-source","path":"crates/labcolors-core/src/session.rs","sha256":"67245bc8b35f32edbd159ad5ffed4a26c5e276a6ef44d5e4ab319a5ab17b4d4a"},{"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":"60ffa4fb329ebb85e380c58aa18177e5b2241a9d7b1f761984103c8b24ae9c00"} diff --git a/crates/labcolors-core/src/generic_boundary_tests.rs b/crates/labcolors-core/src/generic_boundary_tests.rs index cad3d3a1..669d7627 100644 --- a/crates/labcolors-core/src/generic_boundary_tests.rs +++ b/crates/labcolors-core/src/generic_boundary_tests.rs @@ -1323,9 +1323,11 @@ fn shared_observation_ssot_has_one_backing_without_lifecycle_or_adapter_facades( "compiled schema and observations must share the same Rc-backed schema", ); assert!( - OBSERVATION_SOURCE.contains( - "#[derive(Debug, PartialEq, Eq)]\n#[cfg_attr(test, derive(Clone))]\npub(crate) struct CanonicalObservationSchemaV1", - ), + OBSERVATION_SOURCE.contains(concat!( + "#[derive(Debug, PartialEq, Eq)]\n", + "#[cfg_attr(test, derive(Clone))]\n", + "pub(crate) struct CanonicalObservationSchemaV1(Rc<[SurfaceInputPortId]>);", + )), "production schema ownership must not expose a general Clone capability", ); assert_eq!( @@ -1891,7 +1893,7 @@ fn program_session_keeps_physical_evidence_separate_from_lazy_lcs_capability() { let lcs_adapter = source_scope( CONSTRAINTS_SOURCE, "pub(crate) struct ProgramLcsPointAdapterV1 {", - "#[cfg(test)]\nimpl Evaluator", + "impl Evaluator", ); assert!( !lcs_adapter.contains("Option"), diff --git a/crates/labcolors-core/src/point_support.rs b/crates/labcolors-core/src/point_support.rs index a61baa55..7e7f659b 100644 --- a/crates/labcolors-core/src/point_support.rs +++ b/crates/labcolors-core/src/point_support.rs @@ -342,6 +342,7 @@ impl SessionPlanV1 for CompiledPointSupportRecheckV1 { &mut self, _owner: &Self::OwnerLease, observation: RevisionBoundObservationV1, + _previous: Option<&Self::Verified>, _permit: SessionObservationBindingPermitV1, ) -> Result, Self::Error> { let assessment = evaluate_bound_point_support(self, &observation)?; diff --git a/crates/labcolors-core/src/private_fixture.rs b/crates/labcolors-core/src/private_fixture.rs index 949ff951..658dd167 100644 --- a/crates/labcolors-core/src/private_fixture.rs +++ b/crates/labcolors-core/src/private_fixture.rs @@ -1355,6 +1355,8 @@ mod tests { use core::cell::{Cell, RefCell, UnsafeCell}; use std::rc::Rc; + use crate::program_session::CORE_PROGRAM_ASSESSMENT_CALLS; + use super::*; #[derive(Debug, Default)] @@ -2202,4 +2204,235 @@ mod tests { Srgb8::new([64, 64, 64]) ); } + + // Opaque Paint makes distinct backdrops distinct physical cases without + // changing the certified visible render, isolating reuse from semantics. + fn two_case_opaque_authored() -> AuthoredPrivateFixtureV1 { + AuthoredPrivateFixtureV1 { + source: Srgb8::new([64, 64, 64]), + opacity: 1.0, + appearance: AuthoredAppearanceV1 { + adapting_luminance_cd_m2: 64.0, + background_luminance_ratio_yb_yw: 0.2, + surround: AuthoredSurroundV1::Dim, + }, + expected_final_visible: Srgb8::new([64, 64, 64]), + sink_output: 501, + stream: 31, + revision: 1, + scenarios: ScenarioWireSetV2 { + len: 2, + values: [ + ScenarioWireV2 { + id: 1, + backdrop: Srgb8::new([128, 128, 128]), + }, + ScenarioWireV2 { + id: 2, + backdrop: Srgb8::new([192, 192, 192]), + }, + ], + }, + } + } + + fn two_case_observed_update( + revision: u64, + first: Srgb8, + second: Srgb8, + ) -> ObservationUpdateWireV2 { + ObservationUpdateWireV2::Observed { + stream: 31, + revision, + scenarios: ScenarioWireSetV2 { + len: 2, + values: [ + ScenarioWireV2 { + id: 1, + backdrop: first, + }, + ScenarioWireV2 { + id: 2, + backdrop: second, + }, + ], + }, + } + } + + fn two_case_active_instance() -> ( + PrivateFixtureInstanceV1, + Rc>, + CertifiedPrivateFixtureResultV1, + ) { + let mut instance = PrivateFixtureInstanceV1::new(); + let generation = instance.begin_run().unwrap(); + let (host, oracle) = native_host(generation); + let executed = execute_private_fixture_v1(two_case_opaque_authored(), host).unwrap(); + let initial = instance + .complete_run(generation, executed) + .unwrap() + .unwrap(); + (instance, oracle, initial) + } + + #[test] + fn unchanged_cases_skip_evaluator_work_on_revision_only_update() { + let (mut instance, _oracle, initial) = two_case_active_instance(); + CORE_PROGRAM_ASSESSMENT_CALLS.with(|calls| calls.set(0)); + + let updated = instance + .update(two_case_observed_update( + 2, + Srgb8::new([128, 128, 128]), + Srgb8::new([192, 192, 192]), + )) + .unwrap(); + + assert_eq!(updated.state, PrivateFixtureStateV2::Ready); + assert_eq!(updated.revision, 2); + assert_eq!(updated.content_identity, initial.content_identity); + assert_eq!(updated.paint_source, initial.paint_source); + assert_eq!(updated.paint_opacity_bits, initial.paint_opacity_bits); + assert_eq!( + CORE_PROGRAM_ASSESSMENT_CALLS.with(Cell::get), + 0, + "an unchanged observation must not re-run the evaluator" + ); + } + + #[test] + fn single_changed_case_recomputes_only_the_affected_case() { + let (mut instance, _oracle, _initial) = two_case_active_instance(); + CORE_PROGRAM_ASSESSMENT_CALLS.with(|calls| calls.set(0)); + + let updated = instance + .update(two_case_observed_update( + 2, + Srgb8::new([129, 129, 129]), + Srgb8::new([192, 192, 192]), + )) + .unwrap(); + + assert_eq!(updated.state, PrivateFixtureStateV2::Ready); + assert_eq!(updated.revision, 2); + assert_eq!( + CORE_PROGRAM_ASSESSMENT_CALLS.with(Cell::get), + 1, + "a single changed case must recompute exactly that case" + ); + } + + #[test] + fn incremental_revision_update_parities_fresh_full_resolve() { + let mut authored = two_case_opaque_authored(); + authored.revision = 2; + let fresh = run_details(authored); + assert_eq!(fresh.status, 0); + + let (mut instance, _oracle, _initial) = two_case_active_instance(); + let updated = instance + .update(two_case_observed_update( + 2, + Srgb8::new([128, 128, 128]), + Srgb8::new([192, 192, 192]), + )) + .unwrap(); + + assert_eq!(encode_result_v1(updated), fresh.result); + } + + #[test] + fn changed_every_case_full_resolve_parities_and_stays_certified() { + let mut authored = two_case_opaque_authored(); + authored.revision = 2; + authored.scenarios.values = [ + ScenarioWireV2 { + id: 1, + backdrop: Srgb8::new([1, 2, 3]), + }, + ScenarioWireV2 { + id: 2, + backdrop: Srgb8::new([250, 251, 252]), + }, + ]; + let fresh = run_details(authored); + assert_eq!(fresh.status, 0); + + let (mut instance, _oracle, _initial) = two_case_active_instance(); + CORE_PROGRAM_ASSESSMENT_CALLS.with(|calls| calls.set(0)); + let updated = instance + .update(two_case_observed_update( + 2, + Srgb8::new([1, 2, 3]), + Srgb8::new([250, 251, 252]), + )) + .unwrap(); + + assert_eq!(updated.state, PrivateFixtureStateV2::Ready); + assert_eq!(updated.revision, 2); + assert_eq!(encode_result_v1(updated), fresh.result); + assert_eq!(CORE_PROGRAM_ASSESSMENT_CALLS.with(Cell::get), 2); + } + + #[test] + fn conflicting_revisions_are_rejected_and_the_baseline_is_preserved() { + let (mut instance, _oracle, initial) = two_case_active_instance(); + + assert_eq!( + instance.update(two_case_observed_update( + 0, + Srgb8::new([128, 128, 128]), + Srgb8::new([192, 192, 192]), + )), + Err(PrivateFixtureErrorV1::UpdateRejected) + ); + assert_eq!( + instance.update(two_case_observed_update( + 1, + Srgb8::new([1, 2, 3]), + Srgb8::new([192, 192, 192]), + )), + Err(PrivateFixtureErrorV1::UpdateRejected) + ); + + let replay = instance + .update(two_case_observed_update( + 2, + Srgb8::new([128, 128, 128]), + Srgb8::new([192, 192, 192]), + )) + .unwrap(); + assert_eq!(replay.revision, 2); + assert_eq!(replay.content_identity, initial.content_identity); + } + + #[test] + fn unknown_transition_preserves_the_ready_baseline_for_later_reuse() { + let (mut instance, _oracle, initial) = two_case_active_instance(); + + let unknown = instance + .update(ObservationUpdateWireV2::Unknown { + stream: 31, + revision: 2, + reason: 7, + }) + .unwrap(); + assert_eq!(unknown.state, PrivateFixtureStateV2::Stale); + assert_eq!(unknown.revision, 2); + assert_eq!(unknown.output, 0); + + CORE_PROGRAM_ASSESSMENT_CALLS.with(|calls| calls.set(0)); + let recovered = instance + .update(two_case_observed_update( + 3, + Srgb8::new([128, 128, 128]), + Srgb8::new([192, 192, 192]), + )) + .unwrap(); + assert_eq!(recovered.state, PrivateFixtureStateV2::Ready); + assert_eq!(recovered.revision, 3); + assert_eq!(recovered.content_identity, initial.content_identity); + assert_eq!(CORE_PROGRAM_ASSESSMENT_CALLS.with(Cell::get), 0); + } } diff --git a/crates/labcolors-core/src/program_session.rs b/crates/labcolors-core/src/program_session.rs index b932e3b0..9cbf117f 100644 --- a/crates/labcolors-core/src/program_session.rs +++ b/crates/labcolors-core/src/program_session.rs @@ -751,6 +751,20 @@ pub(crate) trait ProgramConstraintEvaluatorSetV1: Sized { fn violation_binding(evidence: &Self::ViolationEvidence) -> ProgramVisiblePointBindingV1; + fn copy_pass_evidence(_evidence: &Self::PassEvidence) -> Option { + None + } + + fn copy_violation_evidence( + _evidence: &Self::ViolationEvidence, + ) -> Option { + None + } + + fn supports_evidence_copy() -> bool { + false + } + fn constraint_content(&self, invocation: Self::Invocation) -> ProgramConstraintContentV1; } @@ -882,6 +896,28 @@ macro_rules! define_core_program_evaluators_v1 { } } + fn copy_pass_evidence(evidence: &Self::PassEvidence) -> Option { + Some(match evidence { + $(CoreProgramPassEvidenceV1::$variant(evidence) => { + CoreProgramPassEvidenceV1::$variant(evidence.clone()) + }),+ + }) + } + + fn copy_violation_evidence( + evidence: &Self::ViolationEvidence, + ) -> Option { + Some(match evidence { + $(CoreProgramViolationEvidenceV1::$variant(evidence) => { + CoreProgramViolationEvidenceV1::$variant(evidence.clone()) + }),+ + }) + } + + fn supports_evidence_copy() -> bool { + true + } + fn constraint_content(&self, invocation: Self::Invocation) -> ProgramConstraintContentV1 { match invocation { $(CoreProgramConstraintInvocationV1::$variant(invocation) => { @@ -2507,6 +2543,22 @@ where DeclaredSrgb8CleanSet(DeclaredSrgb8CleanSetPassV1), } +impl ProgramConstraintPassEvidenceV1 +where + Evaluation: ProgramConstraintEvaluatorSetV1, +{ + fn try_copy(&self) -> Option { + Some(match self { + Self::VisibleUnary(evidence) => { + Self::VisibleUnary(Evaluation::copy_pass_evidence(evidence)?) + } + Self::IntrinsicUnary(evidence) => Self::IntrinsicUnary(*evidence), + Self::Relation(span) => Self::Relation(*span), + Self::DeclaredSrgb8CleanSet(evidence) => Self::DeclaredSrgb8CleanSet(*evidence), + }) + } +} + pub(crate) enum ProgramConstraintViolationEvidenceV1 where Evaluation: ProgramConstraintEvaluatorSetV1, @@ -2517,6 +2569,22 @@ where DeclaredSrgb8CleanSet(DeclaredSrgb8CleanSetViolationV1), } +impl ProgramConstraintViolationEvidenceV1 +where + Evaluation: ProgramConstraintEvaluatorSetV1, +{ + fn try_copy(&self) -> Option { + Some(match self { + Self::VisibleUnary(evidence) => { + Self::VisibleUnary(Evaluation::copy_violation_evidence(evidence)?) + } + Self::IntrinsicUnary(evidence) => Self::IntrinsicUnary(*evidence), + Self::Relation(span) => Self::Relation(*span), + Self::DeclaredSrgb8CleanSet(evidence) => Self::DeclaredSrgb8CleanSet(*evidence), + }) + } +} + /// One evaluator classification retained in the complete Program report. pub enum ProgramConstraintResultV1 where @@ -2533,6 +2601,13 @@ where pub const fn is_violation(&self) -> bool { matches!(self, Self::Violation(_)) } + + fn try_copy(&self) -> Option { + match self { + Self::Pass(evidence) => Some(Self::Pass(evidence.try_copy()?)), + Self::Violation(evidence) => Some(Self::Violation(evidence.try_copy()?)), + } + } } /// One canonical `physical case × constraint` report cell. @@ -2552,6 +2627,16 @@ impl ProgramConstraintCellV1 where Evaluation: ProgramConstraintEvaluatorSetV1, { + fn try_copy(&self) -> Option { + Some(Self { + candidate_state_index: self.candidate_state_index, + case_index: self.case_index, + constraint: self.constraint, + subject: self.subject, + mode: self.mode, + result: self.result.try_copy()?, + }) + } pub const fn candidate_state_index(&self) -> usize { self.candidate_state_index } @@ -3815,9 +3900,10 @@ where &mut self, owner: &Self::OwnerLease, observation: RevisionBoundObservationV1, + previous: Option<&Self::Verified>, _permit: SessionObservationBindingPermitV1, ) -> Result, Self::Error> { - evaluate_program_session(self, &owner.0, observation) + evaluate_program_session(self, &owner.0, observation, previous) } fn retire_verified(&mut self, evidence: Self::Verified) { @@ -3838,6 +3924,7 @@ fn evaluate_program_session( plan: &mut ProgramSessionPlan, epoch: &ProgramEpochV1, observation: RevisionBoundObservationV1, + previous: Option<&ProgramVerifiedV1>, ) -> ProgramSessionEvaluationResult where Evaluation: ProgramConstraintEvaluatorSetV1, @@ -3870,6 +3957,7 @@ where scenario_set, arena.storage_mut(), counts, + previous, )?; if matches!(&outcome, ProgramEvaluationOutcomeV1::Conflict { .. }) { // Conflict не имеет output-authority: сохраняется только capacity для @@ -3904,6 +3992,7 @@ fn evaluate_program_session_into( scenario_set: NonEmptyScenarioSetV1<'_>, arena: &mut ProgramEvaluationArenaV1, counts: ProgramEvaluationCardinalityV1, + previous: Option<&ProgramVerifiedV1>, ) -> Result< ProgramEvaluationOutcomeV1, ProgramSessionEvaluationError>, @@ -3922,6 +4011,7 @@ where 1, arena, counts, + previous, ); } CompiledTargetSelectionV1::Finite { space, .. } => space, @@ -3938,6 +4028,7 @@ where state_index, ProgramEvaluationPhaseV1::Hard, ProgramCandidateCollectionV1::none(), + None, )? { // A selected tuple is never certified from its allocation-free // search pass. Re-apply and collect fresh terminal evidence. @@ -3950,6 +4041,7 @@ where state_index + 1, arena, counts, + None, )? { ProgramEvaluationOutcomeV1::Verified { .. } => { return Ok(ProgramEvaluationOutcomeV1::Verified { @@ -4013,6 +4105,7 @@ where steps: &mut arena.point_causal_steps, }), }, + None, )? { return Err(ProgramSessionEvaluationError::InternalInvariant); } @@ -4038,6 +4131,7 @@ where outputs: None, point_causal: None, }, + None, )? { return Err(ProgramSessionEvaluationError::InternalInvariant); } @@ -4083,6 +4177,10 @@ where Ok(()) } +#[allow( + clippy::too_many_arguments, + reason = "the terminal candidate transaction carries one typed coordinate per bounded arena and baseline" +)] fn collect_program_candidate_into( runtime: &mut ProgramEvaluationRuntimeV1<'_>, epoch: &ProgramEpochV1, @@ -4091,6 +4189,7 @@ fn collect_program_candidate_into( considered_state_count: usize, arena: &mut ProgramEvaluationArenaV1, counts: ProgramEvaluationCardinalityV1, + previous: Option<&ProgramVerifiedV1>, ) -> Result< ProgramEvaluationOutcomeV1, ProgramSessionEvaluationError>, @@ -4154,6 +4253,7 @@ where steps: &mut arena.point_causal_steps, }), }, + previous, )? } else { false @@ -4200,6 +4300,7 @@ where outputs: (!has_hard_constraints).then_some(&mut arena.outputs), point_causal, }, + None, )? { return Err(ProgramSessionEvaluationError::InternalInvariant); } @@ -4279,6 +4380,7 @@ fn scan_program_candidate( candidate_state_index: usize, phase: ProgramEvaluationPhaseV1, collection: ProgramCandidateCollectionV1<'_, Evaluation>, + previous: Option<&ProgramVerifiedV1>, ) -> Result>> where Evaluation: ProgramConstraintEvaluatorSetV1, @@ -4299,6 +4401,47 @@ where } let case_count = scenario_set.len().get(); + let previous_storage = previous + .filter(|verified| { + Evaluation::supports_evidence_copy() + && verified.selected_state_index().is_none() + && !epoch + .constraint_phases + .contains(ProgramEvaluationPhaseV1::ReportOnly) + && verified.report().content_identity() == epoch.content_identity + && verified.report().selection_release_identity() + == epoch.target_selection.release_identity() + && verified + .report() + .observation() + .shares_schema_backing_with(schema) + && verified.report().observation().physical_case_count() == case_count + }) + .map(|verified| { + ( + &verified.report().observation, + &verified.report().arena.storage, + ) + }); + let previous_strides = previous_storage + .map(|(_, storage)| { + let lengths = [ + storage.cells.len(), + storage.relation_members.len(), + storage.point_causal_records.len(), + storage.point_causal_steps.len(), + ]; + if lengths.into_iter().any(|length| length % case_count != 0) { + return Err(ProgramSessionEvaluationError::InternalInvariant); + } + Ok(lengths.map(|length| length / case_count)) + }) + .transpose()?; + if let (Some(outputs), Some((_, storage))) = (outputs.as_deref_mut(), previous_storage) { + if outputs.is_empty() { + outputs.extend_from_slice(&storage.outputs); + } + } let mut has_hard_violation = false; let mut output_mismatch = None; for case_index in 0..case_count { @@ -4316,6 +4459,73 @@ where ), )); } + if let (Some((previous_observation, storage)), Some(strides)) = + (previous_storage, previous_strides) + { + let previous_values = previous_observation + .physical_values(case_index) + .ok_or(ProgramSessionEvaluationError::InternalInvariant)?; + if previous_values == values { + if let ProgramConstraintEvidenceCaptureV1::Report { + cells, + relation_members, + } = &mut evidence + { + let cell_start = case_index * strides[0]; + let relation_start = case_index * strides[1]; + let previous_cells = storage + .cells + .get(cell_start..cell_start + strides[0]) + .ok_or(ProgramSessionEvaluationError::InternalInvariant)?; + let previous_relations = storage + .relation_members + .get(relation_start..relation_start + strides[1]) + .ok_or(ProgramSessionEvaluationError::InternalInvariant)?; + if !storage_has_spare_capacity( + [cells.len(), relation_members.len()], + [cells.capacity(), relation_members.capacity()], + [previous_cells.len(), previous_relations.len()], + ) { + return Err(ProgramSessionEvaluationError::InternalInvariant); + } + has_hard_violation |= previous_cells + .iter() + .any(|cell| cell.result().is_violation()); + for cell in previous_cells { + cells.push( + cell.try_copy() + .ok_or(ProgramSessionEvaluationError::InternalInvariant)?, + ); + } + relation_members.extend_from_slice(previous_relations); + } + if let Some(point_causal) = point_causal.as_mut() { + let record_start = case_index * strides[2]; + let step_start = case_index * strides[3]; + let previous_records = storage + .point_causal_records + .get(record_start..record_start + strides[2]) + .ok_or(ProgramSessionEvaluationError::InternalInvariant)?; + let previous_steps = storage + .point_causal_steps + .get(step_start..step_start + strides[3]) + .ok_or(ProgramSessionEvaluationError::InternalInvariant)?; + if !storage_has_spare_capacity( + [point_causal.records.len(), point_causal.steps.len()], + [ + point_causal.records.capacity(), + point_causal.steps.capacity(), + ], + [previous_records.len(), previous_steps.len()], + ) { + return Err(ProgramSessionEvaluationError::InternalInvariant); + } + point_causal.records.extend_from_slice(previous_records); + point_causal.steps.extend_from_slice(previous_steps); + } + continue; + } + } runtime .bindings .overwrite_surface_inputs_canonical(schema.as_slice().iter().copied(), |index| { @@ -4710,7 +4920,7 @@ where output: output.output, paint, }; - if case_index == 0 { + if outputs.len() == output_index { outputs.push(routed); } else if outputs.get(output_index).copied() != Some(routed) && output_mismatch.is_none() diff --git a/crates/labcolors-core/src/session.rs b/crates/labcolors-core/src/session.rs index 2f6235a1..58e19838 100644 --- a/crates/labcolors-core/src/session.rs +++ b/crates/labcolors-core/src/session.rs @@ -85,6 +85,7 @@ pub(crate) trait SessionPlanV1: private::PlanSealed { &mut self, owner: &Self::OwnerLease, observation: RevisionBoundObservationV1, + previous: Option<&Self::Verified>, permit: SessionObservationBindingPermitV1, ) -> Result, Self::Error>; @@ -613,6 +614,7 @@ fn prepare_session_transition<'session, Plan: SessionPlanV1>( .evaluate( &owner, observation, + state.last_verified(), SessionObservationBindingPermitV1::mint(), ) .map_err(SessionUpdateError::Plan)?; diff --git a/crates/labcolors-core/src/session_tests.rs b/crates/labcolors-core/src/session_tests.rs index ac3ed164..f5b1cc3b 100644 --- a/crates/labcolors-core/src/session_tests.rs +++ b/crates/labcolors-core/src/session_tests.rs @@ -139,6 +139,7 @@ impl SessionPlanV1 for SentinelPlan { &mut self, _owner: &Self::OwnerLease, observation: RevisionBoundObservationV1, + _previous: Option<&Self::Verified>, _permit: SessionObservationBindingPermitV1, ) -> Result, Self::Error> { self.control @@ -207,6 +208,7 @@ impl SessionPlanV1 for ReplacingOwnerPlan { &mut self, owner: &Self::OwnerLease, observation: RevisionBoundObservationV1, + _previous: Option<&Self::Verified>, _permit: SessionObservationBindingPermitV1, ) -> Result, Self::Error> { self.evaluations.set(self.evaluations.get() + 1); @@ -324,6 +326,7 @@ impl SessionPlanV1 for RetirementPlan { &mut self, _owner: &Self::OwnerLease, observation: RevisionBoundObservationV1, + _previous: Option<&Self::Verified>, _permit: SessionObservationBindingPermitV1, ) -> Result, Self::Error> { if !observation.shares_schema_backing_with(&self.schema) { @@ -377,6 +380,7 @@ impl SessionPlanV1 for DropOrderPlan { &mut self, owner: &Self::OwnerLease, observation: RevisionBoundObservationV1, + _previous: Option<&Self::Verified>, _permit: SessionObservationBindingPermitV1, ) -> Result, Self::Error> { let installed = self diff --git a/fixtures/private-program-browser/proof.mjs b/fixtures/private-program-browser/proof.mjs index d470149a..19d4ae03 100644 --- a/fixtures/private-program-browser/proof.mjs +++ b/fixtures/private-program-browser/proof.mjs @@ -9,6 +9,8 @@ const EXPECTED_COMPUTED_CSS = "rgba(64, 64, 64, 0.5)"; const REQUEST_HEX = "4c43465102004600404040000000000000e03f00000000000050409a9999999999c93f01606060f50100001f0000000100000000000000010100000080808000000000000000"; const UPDATE_HEX = "4c434655020028001f00000002000000000000000101020000008080800000000000000000000000"; +const CHANGED_UPDATE_HEX = + "4c434655020028001f0000000300000000000000010103000000ffffff0000000000000000000000"; const EXPECTED_CONTENT_IDENTITY = "4daf4731d823ba228f4189d08c76183dd8a81e8b593ff709828b7329e075a914"; @@ -19,6 +21,7 @@ const EXPECTED_CHECKS = Object.freeze([ "exact-computed-css", "exact-certified-receipt", "explicit-observation-update", + "changed-observation-invalidates-certified-result", "dispose", "post-run-dispose-idempotence", ]); @@ -136,6 +139,22 @@ async function runProof() { equal(updated.revision, 2n, "updated state advances revision"); equal(updated.contentIdentity, EXPECTED_CONTENT_IDENTITY, "update reuses compiled identity"); checks.push("explicit-observation-update"); + + const changedUpdateBytes = exactWireBytes(CHANGED_UPDATE_HEX); + equal(changedUpdateBytes.length, EXPECTED_UPDATE_LENGTH, "changed update has the exact ABI length"); + const changed = await consumer.update(changedUpdateBytes); + equal(changed.state, 4, "changed observation state is Failed"); + equal(changed.stream, 31, "failed state retains stream identity"); + equal(changed.revision, 3n, "failed state advances revision"); + equal(changed.output, 0, "failed state has no certified output"); + equal(changed.contentIdentity, "0".repeat(64), "failed state has no certified identity"); + probe.getBoundingClientRect(); + equal( + getComputedStyle(probe).backgroundColor, + "rgba(0, 0, 0, 0)", + "changed observation revokes the previous certified output", + ); + checks.push("changed-observation-invalidates-certified-result"); } finally { disposeResult = await consumer.dispose(); } diff --git a/packages/colors/bench/private-program-wasm.json b/packages/colors/bench/private-program-wasm.json index 25978494..622cf787 100644 --- a/packages/colors/bench/private-program-wasm.json +++ b/packages/colors/bench/private-program-wasm.json @@ -21,12 +21,12 @@ "wasmOptFlags": "-Oz --enable-bulk-memory --enable-nontrapping-float-to-int" }, "measurement": { - "source": "github-actions-run-31583435451", + "source": "github-actions-run-31637406587", "platform": "linux-x64", - "rawBytes": 318134 + "rawBytes": 321335 }, "policy": { - "maxRawBytes": 318134, + "maxRawBytes": 321335, "basis": "exact optimized private Program artifact from GitHub Actions Linux", "gzip": "diagnostic-only" } diff --git a/packages/colors/test/private-program-mutation-contract.test.mjs b/packages/colors/test/private-program-mutation-contract.test.mjs index 92b4f7c1..02976f5e 100644 --- a/packages/colors/test/private-program-mutation-contract.test.mjs +++ b/packages/colors/test/private-program-mutation-contract.test.mjs @@ -51,6 +51,14 @@ const EXPECTED_MUTATIONS = Object.freeze([ "82242d45a0a98d8912bb70424a6661c8090f1233ac7f7693ccca40a79a3e53e2", "Error: private Program browser fixture: updated state is Ready; expected 2, got 3", ], + [ + "incremental-invalidation-skip", + "semantic-source-bypass", + "rust-wasm", + "crates/labcolors-core/src/program_session.rs", + "92eb588560200891c4655325c3d26e919f9a9c76fa8f4499f235c32f98721454", + "Error: private Program browser fixture: changed observation state is Failed; expected 4, got 2", + ], [ "external-attachment-handoff-binding-bypass", "semantic-source-bypass", @@ -88,7 +96,7 @@ async function assertTreeContainsNoLinks(root) { } } -test("private Program mutation IDs bind six exact source transformations", () => { +test("private Program mutation IDs bind seven exact source transformations", () => { assert.deepEqual( PRIVATE_PROGRAM_MUTATION_CASES.map( ({ id, proof, artifact, sourcePath, search, replacement, expectedBrowserAssertion }) => [ @@ -369,7 +377,7 @@ test("a mutation kill requires its real-browser assertion and semantic marker", () => assertMutationSpecificBrowserFailure(definition, { ...killed, - stdout: "LAB_COLORS_PRIVATE_PROGRAM_BROWSER_PASS v1 checks=8", + stdout: "LAB_COLORS_PRIVATE_PROGRAM_BROWSER_PASS v1 checks=9", }), /emitted the browser PASS receipt/u, ); diff --git a/scripts/test-private-program-browser.mjs b/scripts/test-private-program-browser.mjs index e1b87933..efb0da58 100644 --- a/scripts/test-private-program-browser.mjs +++ b/scripts/test-private-program-browser.mjs @@ -42,6 +42,7 @@ const EXPECTED_CHECKS = Object.freeze([ "exact-computed-css", "exact-certified-receipt", "explicit-observation-update", + "changed-observation-invalidates-certified-result", "dispose", "post-run-dispose-idempotence", ]); diff --git a/scripts/test-private-program-mutations.mjs b/scripts/test-private-program-mutations.mjs index f66c93de..b1b0e525 100644 --- a/scripts/test-private-program-mutations.mjs +++ b/scripts/test-private-program-mutations.mjs @@ -68,7 +68,7 @@ const BROWSER_ASSERTION_PREFIX = "private Program browser proof: browser assertion failed:"; const BROWSER_CLEANUP_FAILURE = "private Program browser proof or cleanup failed"; const MUTATION_RECEIPT = "LAB_COLORS_PRIVATE_PROGRAM_MUTATIONS_PASS v1"; -const EXPECTED_SEMANTIC_MUTATION_COUNT = 6; +const EXPECTED_SEMANTIC_MUTATION_COUNT = 7; const EXPECTED_BINARY_DIFFERENTIAL_COUNT = 1; const MUTATION_TIMEOUT_ENV = "LAB_COLORS_PRIVATE_MUTATION_TIMEOUT_MS"; const CHILD_TIMEOUT_ENV = "LAB_COLORS_PRIVATE_MUTATION_CHILD_TIMEOUT_MS"; @@ -137,6 +137,16 @@ export const PRIVATE_PROGRAM_MUTATION_CASES = Object.freeze([ expectedBrowserAssertion: "Error: private Program browser fixture: updated state is Ready; expected 2, got 3", }), + mutation({ + id: "incremental-invalidation-skip", + proof: "semantic-source-bypass", + artifact: "rust-wasm", + sourcePath: "crates/labcolors-core/src/program_session.rs", + search: "if previous_values == values {", + replacement: "if true {", + expectedBrowserAssertion: + "Error: private Program browser fixture: changed observation state is Failed; expected 4, got 2", + }), mutation({ id: "external-attachment-handoff-binding-bypass", proof: "semantic-source-bypass", diff --git a/scripts/verify_point_support_surplus.py b/scripts/verify_point_support_surplus.py index 71770806..7265f551 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 = ( - "719de715c1324b6112342fbc7bdeec4f8a4c96576e5e77b46c655aa8ba151186" + "eed5e08f605e6d82f3b8409e14d44a47b41247a37ed056c779faf324f488bbdb" ) EXPECTED_Q55_PROOF_SHA256 = ( "ac59cf89503170c789223b91d775213a19d4e571ef930f2ea609fcd51b14defd"