feat(numerics): атомарные исходы, per-invocation план и capability manifest (#292) - #311
Conversation
…292) package capability ≠ compiled invocation plan ≠ result evidence: - CompiledNumericalPlanV1 компилируется fail-closed из machine-readable registry-строки site (stable = [exact, refuse]; legacy только при объявленном compatibility profile); план не содержит результата; - SoundIntervalEvidenceV1 — запечатанное интервальное свидетельство (конструктор приватен; production-производитель появится только вместе с допущенным sound-bound backend'ом): classify_at_least_v1 принимает только его, голый caller-created интервал больше не повышается до Determinate-гарантии; - OutwardIntervalV1 честно понижен до интервала-заявления (проверка формы, не доказанность); - glow::solve_screen_alpha_for_dj исполняет ветви строго по скомпилированному плану — порядок методов задаёт registry, не рукописный match; поведение байт-идентично (glow 32/32, полный workspace зелёный). Wire-ключи и WASM/npm поверхности не изменены. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…BitExact, plan/manifest ядро Core компилируется, тесты ядра зелёные (RED-группы #292 включены). Осталось: conformance pack 3.0.0, WASM/verifier, table-проекция плана, доки. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…calSites; FFI атомарный маппинг Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# Conflicts: # docs/verification-map.md
…nifest (#292) package capability ≠ compiled invocation plan ≠ result evidence: - NumericalDecisionV1 атомарен: Determinate{sealed BitExact evidence} | Compatibility{registered release_id + LegacyPlatformDependentV1} | Indeterminate; незаконные комбинации непредставимы типами; dead classify_at_least_v1/AtLeastDecisionV1/DecisionGuaranteeV1 удалены (compile_fail doc-тесты фиксируют миграцию); - RoleSpec::Glow несёт typed NumericalExecutionModeV1; прежние ключи stable-v1|legacy-platform-dependent-v1 — только boundary-адаптер; - numerical_plan.rs: invocation identity (opaque node bytes + site + локальный ordinal), canonical derived-проекция + FNV drift-checksum; NamedRoleTable::numerical_plan_v1() — биекция с Glow-ролями; - capability manifest по канону #289 (length-prefixed preimage, домены labcolors.numerical-*); conformance pack 3.0.0: numericalCapabilities вместо numericalSites (vectors нетронуты, packDigest прежний); - WASM/FFI: атомарный маппинг без cross-product реконструкции; wire-байты и JSON прежние (golden/npm 106/106 без regen); additive-поверхность numericalCapabilityManifest() в WASM/npm; verifier с независимым JS FNV-оракулом; release-manifest schema 2 + read-back в publish.yml. Публичные цвета/fingerprint/CSS/adaptive trace не изменились. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Encoding — внешний versioned контракт (adapter-оракулы переигрывают его независимо): эквивалентный мутант потери mode-tag (компенсируемый release-ключом) обязан умирать замороженным вектором, а не выживать. Мутационные пробы: P1 (mode-tag) убит этим тестом; P2 (глобальный ordinal) и P3 (потеря release в манифесте) убиты существующими. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 21 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
WalkthroughPR переводит численные решения Glow на typed execution modes и атомарные outcomes, добавляет канонический numerical plan и capability manifest, обновляет Rust/WASM/FFI/npm/conformance/release-контракты, проверки checksum, документацию и тесты. ChangesЧисленные контракты и план исполнения
Boundary-манифесты и release-проверки
Документация контрактов
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related issues
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 9
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@bindings/swift/Tests/LabColorsConformanceTests/ConformanceTests.swift`:
- Around line 125-129: Update testCapabilityManifestChecksumRecomputes to assert
that manifest.schemaVersion equals 1 before validating the V1
capability-manifest fields, ensuring newer schema versions cannot pass this
conformance test with a recomputed checksum.
In `@crates/labcolors-core/src/glow.rs`:
- Around line 204-211: Обновите from_execution_mode, чтобы ExplicitCompatibility
сопоставлялся с конкретным release и site Glow, а не с шаблоном { .. }. Для
несовместимых или неизвестных release сохраните явную обработку, чтобы
добавление нового идентификатора требовало отдельной ветки и решения
компилятора.
In `@crates/labcolors-core/src/numerical_plan.rs`:
- Around line 249-255: Replace the sort_unstable_by comparator in the invocation
ordering flow with sort_by_cached_key, using each invocation’s canonical_bytes()
as the cached key. Remove the redundant site_id.key() secondary comparison,
while preserving canonical identity-byte ordering.
- Around line 402-408: Rename unregistered_release_is_a_typed_compile_error to
accurately describe the registered-release happy path, and update its comments
to explicitly mark the UnregisteredCompatibilityRelease branch as vacuous until
another release or site can be constructed. Keep the existing compatibility
assertion unchanged and do not claim coverage of the negative path.
In `@crates/labcolors-core/src/numerics.rs`:
- Around line 718-749: Усилить registry-guard тесты в
bit_exact_mint_is_refused_without_declared_capability: добавить отрицательную
fixture-запись без BitExact и проверить, что mint_bit_exact_evidence отклоняет
её, сохранив успешный сценарий для GlowTargetOrMaximumV1. Расширить
uniqueness-проверку всех checksum-списков registry, а не только
row.stable_outcomes, чтобы каждый set-поле проверялось на дубликаты.
- Around line 565-669: Сначала добавьте compile-fail тест, подтверждающий, что
внешний код не может повторно привязать скопированное evidence к произвольным
value и site_id либо напрямую создать Compatibility. Затем измените
NumericalDecisionV1: замените открытые поля вариантов на непрозрачные структуры
с приватными полями, а создание Determinate и Compatibility оставьте только
через core-owned конструкторы, валидирующие registry и фактически исполненный
release; сохраните существующие типы evidence и provenance.
In `@crates/labcolors-core/src/semantic.rs`:
- Around line 2361-2454: Вынеси локальное замыкание assemble из ветки
RoleSpec::Glow в отдельный приватный метод или функцию, принимающую GlowSolve,
GlowDecisionOutcomeV1, источник tint и фоновые данные. Сохрани без изменений
сборку слоёв, измерение core, заполнение GlowResolved и преобразование ошибок в
Resolved::Unreachable; вызов из solve_screen_alpha_for_dj должен использовать
новый выделенный символ для обоих determinate-исходов.
In `@docs/verification-map.md`:
- Line 103: Insert a blank line immediately before the “Численные решения —
`numerics.rs`, `numerical_plan.rs` (`#292`)” Markdown heading in the
documentation.
In `@packages/colors/README.md`:
- Around line 173-178: Переведите пояснительный текст в README на русский:
замените обычные термины union, stable/legacy profile, guarantee, selection
diagnostic, status, compatibility-результат, determinate и Outward interval
русскими формулировками, сохранив точные wire-значения вроде `kind: "glow"` и
`decisionGuarantee: { kind: "legacy-platform-dependent-v1" }` в обратных
кавычках.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 3d4db182-ef02-4252-bf7d-28dd230386d4
📒 Files selected for processing (33)
.cargo/mutants.toml.github/workflows/publish.ymlCHANGELOG.mdREADME.mdbindings/swift/Tests/LabColorsConformanceTests/ConformanceTests.swiftconformance/README.mdconformance/vectors/manifest.jsoncrates/labcolors-conformance/src/lib.rscrates/labcolors-conformance/tests/reference_runner.rscrates/labcolors-core/benches/glow_alpha.rscrates/labcolors-core/src/agnostic_gates.rscrates/labcolors-core/src/config.rscrates/labcolors-core/src/glow.rscrates/labcolors-core/src/lib.rscrates/labcolors-core/src/numerical_plan.rscrates/labcolors-core/src/numerics.rscrates/labcolors-core/src/semantic.rscrates/labcolors-core/tests/property_invariants.rscrates/labcolors-ffi/src/lib.rscrates/labcolors-wasm/src/dto.rscrates/labcolors-wasm/src/engine.rscrates/labcolors-wasm/src/lib.rscrates/labcolors-wasm/src/projection.rscrates/labcolors-wasm/tests/wasm_parity.rsdocs/decisions/0004-finite-alpha-glow-reference.mddocs/migrations/exact-alpha-glow.mddocs/verification-map.mdpackages/colors/README.mdpackages/colors/index.d.tspackages/colors/index.jspackages/colors/test/capability-manifest.test.mjspackages/colors/test/release-contract.test.mjsscripts/verify-package-release.mjs
| /// Обратная проекция generic mode в boundary-ключ (adapter-сторона). | ||
| pub fn from_execution_mode(mode: NumericalExecutionModeV1) -> Self { | ||
| match mode { | ||
| NumericalExecutionModeV1::StableOnly => Self::StableV1, | ||
| NumericalExecutionModeV1::ExplicitCompatibility { .. } => { | ||
| Self::LegacyPlatformDependentV1 | ||
| } | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Не стирайте identity compatibility release в обратном адаптере.
Шаблон { .. } молча спроецирует любой будущий release, включая release другого site, в legacy-профиль Glow. Сопоставьте конкретный Glow release, чтобы добавление нового ID потребовало явного решения компилятора.
Предлагаемое исправление
pub fn from_execution_mode(mode: NumericalExecutionModeV1) -> Self {
match mode {
NumericalExecutionModeV1::StableOnly => Self::StableV1,
- NumericalExecutionModeV1::ExplicitCompatibility { .. } => {
+ NumericalExecutionModeV1::ExplicitCompatibility {
+ release_id:
+ NumericalCompatibilityReleaseIdV1::GlowCam16UcsJPrimeTargetOrMaxV1,
+ } => {
Self::LegacyPlatformDependentV1
}
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /// Обратная проекция generic mode в boundary-ключ (adapter-сторона). | |
| pub fn from_execution_mode(mode: NumericalExecutionModeV1) -> Self { | |
| match mode { | |
| NumericalExecutionModeV1::StableOnly => Self::StableV1, | |
| NumericalExecutionModeV1::ExplicitCompatibility { .. } => { | |
| Self::LegacyPlatformDependentV1 | |
| } | |
| } | |
| /// Обратная проекция generic mode в boundary-ключ (adapter-сторона). | |
| pub fn from_execution_mode(mode: NumericalExecutionModeV1) -> Self { | |
| match mode { | |
| NumericalExecutionModeV1::StableOnly => Self::StableV1, | |
| NumericalExecutionModeV1::ExplicitCompatibility { | |
| release_id: | |
| NumericalCompatibilityReleaseIdV1::GlowCam16UcsJPrimeTargetOrMaxV1, | |
| } => { | |
| Self::LegacyPlatformDependentV1 | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/labcolors-core/src/glow.rs` around lines 204 - 211, Обновите
from_execution_mode, чтобы ExplicitCompatibility сопоставлялся с конкретным
release и site Glow, а не с шаблоном { .. }. Для несовместимых или неизвестных
release сохраните явную обработку, чтобы добавление нового идентификатора
требовало отдельной ветки и решения компилятора.
| /// Provenance-класс текущего нехарактеризованного legacy-пути. Тип-уровневый | ||
| /// маркер: `Compatibility` физически не может нести stable/exact provenance. | ||
| /// `PlatformCharacterized` не существует до immutable attestation registry (#258). | ||
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| pub struct LegacyPlatformDependentV1; | ||
|
|
||
| impl LegacyPlatformDependentV1 { | ||
| /// Стабильный wire key класса. | ||
| pub fn key(self) -> &'static str { | ||
| "legacy-platform-dependent-v1" | ||
| } | ||
| } | ||
|
|
||
| /// Печать evidence: тип публичен (входит в публичный enum-вариант), но его | ||
| /// приватное поле делает конструирование возможным только внутри модуля — | ||
| /// внешний код может лишь матчить вариант через `..`. | ||
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| pub struct EvidenceSeal { | ||
| _private: (), | ||
| } | ||
|
|
||
| /// Запечатанное evidence determinate-решения. В V1 минтится только реально | ||
| /// admitted `BitExact`; bounded/canonical-finite варианты появятся вместе с | ||
| /// зарегистрированным verifier и bound/artifact identity (#284/#291) — | ||
| /// фиктивные IDs запрещены. | ||
| #[derive(Debug, Clone, Copy, PartialEq)] | ||
| #[non_exhaustive] | ||
| pub enum DecisionGuaranteeV1 { | ||
| /// Решение следует только из exact integer/rational state. | ||
| BitExact, | ||
| /// Решение следует из непересекающегося outward-интервала. | ||
| OutwardIntervalV1(OutwardIntervalV1), | ||
| /// Явно выбранный прежний platform/libm-dependent путь. | ||
| LegacyPlatformDependentV1, | ||
| pub enum NumericalDecisionEvidenceV1 { | ||
| /// Решение следует из точного конечного состояния объявленного | ||
| /// reference-профиля. | ||
| BitExact { | ||
| /// Профиль, в чьём точном домене доказан результат. | ||
| reference_profile_id: ReferenceProfileIdV1, | ||
| /// Печать: внешняя конструкция невозможна (тип поля приватен). | ||
| _seal: EvidenceSeal, | ||
| }, | ||
| } | ||
|
|
||
| impl DecisionGuaranteeV1 { | ||
| /// Стабильный wire key. | ||
| pub fn key(self) -> &'static str { | ||
| impl NumericalDecisionEvidenceV1 { | ||
| /// Стабильный wire key класса evidence. | ||
| pub fn class_key(&self) -> &'static str { | ||
| match self { | ||
| Self::BitExact => "bit-exact", | ||
| Self::OutwardIntervalV1(_) => "outward-interval-v1", | ||
| Self::LegacyPlatformDependentV1 => "legacy-platform-dependent-v1", | ||
| Self::BitExact { .. } => "bit-exact", | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// Доказанный interval, если именно он является certificate решения. | ||
| pub fn interval(self) -> Option<OutwardIntervalV1> { | ||
| match self { | ||
| Self::OutwardIntervalV1(interval) => Some(interval), | ||
| Self::BitExact | Self::LegacyPlatformDependentV1 => None, | ||
| } | ||
| /// Registry-owned минт BitExact-evidence: допустим только для site, чья | ||
| /// capability-строка объявляет класс BitExact. | ||
| /// | ||
| /// # Errors | ||
| /// | ||
| /// Site не зарегистрирован либо не объявляет BitExact. | ||
| pub(crate) fn mint_bit_exact_evidence( | ||
| site_id: NumericalSiteIdV1, | ||
| reference_profile_id: ReferenceProfileIdV1, | ||
| ) -> Result<NumericalDecisionEvidenceV1, String> { | ||
| let row = registry_row(site_id) | ||
| .ok_or_else(|| format!("site {} отсутствует в registry V1", site_id.key()))?; | ||
| if !row | ||
| .evidence_classes | ||
| .contains(&NumericalEvidenceClassV1::BitExact) | ||
| { | ||
| return Err(format!( | ||
| "site {} не объявляет evidence class bit-exact", | ||
| site_id.key() | ||
| )); | ||
| } | ||
| Ok(NumericalDecisionEvidenceV1::BitExact { | ||
| reference_profile_id, | ||
| _seal: EvidenceSeal { _private: () }, | ||
| }) | ||
| } | ||
|
|
||
| /// Semantic result с явным numerical proof class. | ||
| /// Атомарный терминальный результат численного решения. | ||
| /// | ||
| /// Три законных класса; их смешение непредставимо типами: | ||
| /// | ||
| /// * `Determinate` — доказанное решение с запечатанным evidence; | ||
| /// * `Compatibility` — явный прежний алгоритм (registered release) c | ||
| /// provenance-классом `LegacyPlatformDependentV1`; не determinate evidence | ||
| /// и не конвертируется в BitExact/Bounded/Proven*; | ||
| /// * `Indeterminate` — stable branch честно не выбран. | ||
| #[derive(Debug, Clone, PartialEq)] | ||
| #[non_exhaustive] | ||
| pub enum NumericalDecisionV1<T> { | ||
| /// Решение принято под указанной гарантией. | ||
| /// Решение принято под запечатанным evidence. | ||
| Determinate { | ||
| /// Зарегистрированный site. | ||
| site_id: NumericalSiteIdV1, | ||
| /// Предметный результат. | ||
| value: T, | ||
| /// Запечатанное registry-owned evidence. | ||
| evidence: NumericalDecisionEvidenceV1, | ||
| }, | ||
| /// Явно выбранный зарегистрированный прежний алгоритм. | ||
| Compatibility { | ||
| /// Зарегистрированный site. | ||
| site_id: NumericalSiteIdV1, | ||
| /// Registered release, реально исполнивший invocation. | ||
| release_id: NumericalCompatibilityReleaseIdV1, | ||
| /// Предметный результат. | ||
| value: T, | ||
| /// Почему branch считается доказанным в объявленном профиле. | ||
| guarantee: DecisionGuaranteeV1, | ||
| /// Класс происхождения (не заменяет release identity). | ||
| provenance: LegacyPlatformDependentV1, |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Запретите внешнюю реконструкцию атомарных исходов.
Приватная печать блокирует только создание нового BitExact, но evidence имеет Copy: клиент может скопировать его из законного результата и собрать новый Determinate с произвольными value и site_id. Compatibility вообще конструируется напрямую без доказательства исполнения release. Поэтому незаконные комбинации остаются представимыми.
Сначала добавьте compile-fail тест на повторную привязку evidence, затем перенесите поля вариантов в непрозрачные структуры с приватными полями и core-only конструкторами.
Рекомендуемая форма API
-pub enum NumericalDecisionV1<T> {
- Determinate { site_id: NumericalSiteIdV1, value: T, evidence: NumericalDecisionEvidenceV1 },
- Compatibility { site_id: NumericalSiteIdV1, release_id: NumericalCompatibilityReleaseIdV1, value: T, provenance: LegacyPlatformDependentV1 },
- Indeterminate { site_id: NumericalSiteIdV1, evidence: NumericalIndeterminacyV1 },
-}
+pub enum NumericalDecisionV1<T> {
+ Determinate(DeterminateDecisionV1<T>),
+ Compatibility(CompatibilityDecisionV1<T>),
+ Indeterminate(IndeterminateDecisionV1),
+}
+
+pub struct DeterminateDecisionV1<T> {
+ site_id: NumericalSiteIdV1,
+ value: T,
+ evidence: NumericalDecisionEvidenceV1,
+}As per coding guidelines: «Для дефекта сначала создать тест, проходящий через сломанный путь и падающий по правильной причине, затем реализовать минимальный полный срез».
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/labcolors-core/src/numerics.rs` around lines 565 - 669, Сначала
добавьте compile-fail тест, подтверждающий, что внешний код не может повторно
привязать скопированное evidence к произвольным value и site_id либо напрямую
создать Compatibility. Затем измените NumericalDecisionV1: замените открытые
поля вариантов на непрозрачные структуры с приватными полями, а создание
Determinate и Compatibility оставьте только через core-owned конструкторы,
валидирующие registry и фактически исполненный release; сохраните существующие
типы evidence и provenance.
Source: Coding guidelines
| RoleSpec::Glow { tint, step, mode } => { | ||
| // Свечение: halo = якорь источника по теме; core — пересвет; | ||
| // интенсивность решается под контрактную ступень на фоне резолва. | ||
| // Typed execution mode исполняется ПРЯМО из compiled spec (#292): | ||
| // никакого plan lookup или string policy selection в hot path. | ||
| let halo_hex = crate::spaces::srgb::hex_from_srgb_encoded(tint.for_vc(vc)); | ||
| let bg_hex = | ||
| crate::spaces::srgb::hex_from_srgb_encoded(quantise_encoded(bg.encoded_display())); | ||
| // Общая сборка полного Glow-результата из решённого состояния — | ||
| // одна для обоих атомарных законных исходов. | ||
| let assemble = |g: &crate::glow::GlowSolve, | ||
| outcome: crate::glow::GlowDecisionOutcomeV1| | ||
| -> Resolved { | ||
| let (core_hex, halo_hex) = match crate::glow::glow_layers_from_source(&halo_hex, vc) | ||
| { | ||
| Ok(pair) => pair, | ||
| Err(e) => { | ||
| return Resolved::Unreachable(Unreachable::InternalInvariant(format!( | ||
| "generated Glow layer recipe was rejected: {e}" | ||
| ))); | ||
| } | ||
| }; | ||
| let core_measurement = match crate::glow::measure_screen_layer_at_alpha( | ||
| &core_hex, | ||
| &bg_hex, | ||
| g.alpha(), | ||
| vc, | ||
| ) { | ||
| Ok(measurement) => measurement, | ||
| Err(e) => { | ||
| return Resolved::Unreachable(Unreachable::InternalInvariant(format!( | ||
| "generated Glow core measurement was rejected: {e}" | ||
| ))); | ||
| } | ||
| }; | ||
| Resolved::Glow(GlowResolved { | ||
| core_hex, | ||
| halo_hex, | ||
| alpha: g.alpha(), | ||
| alpha_css: g.alpha_css().to_string(), | ||
| target_dj: g.target_dj(), | ||
| halo_composite_hex: g.composite_hex().to_string(), | ||
| halo_achieved_dj: g.achieved_dj(), | ||
| core_composite_hex: core_measurement.composite_hex, | ||
| core_achieved_dj: core_measurement.achieved_dj, | ||
| target_status: g.status(), | ||
| layer_recipe_profile: | ||
| crate::glow::GlowLayerRecipeProfileV1::Cam16JPrimeOklabCuspV1, | ||
| appearance_diagnostic_profile: | ||
| crate::glow::GlowDiagnosticProfileV1::Cam16UcsJPrimeLi2017V1, | ||
| selection_diagnostic_profile: g.selection_diagnostic_profile(), | ||
| decision_outcome: outcome, | ||
| halo_composite_certificate: g.composite_certificate().clone(), | ||
| core_composite_certificate: core_measurement.certificate, | ||
| }) | ||
| }; | ||
| return match crate::glow::solve_screen_alpha_for_dj( | ||
| &halo_hex, | ||
| &bg_hex, | ||
| step.target_dj(), | ||
| decision_profile, | ||
| mode, | ||
| vc, | ||
| ) { | ||
| Ok(crate::numerics::NumericalDecisionV1::Indeterminate { site_id, evidence }) => { | ||
| Resolved::GlowIndeterminate(GlowIndeterminateResolved { | ||
| source_hex: halo_hex, | ||
| target_dj: step.target_dj(), | ||
| decision_profile, | ||
| decision_profile: crate::glow::GlowDecisionProfileV1::from_execution_mode( | ||
| mode, | ||
| ), | ||
| site_id, | ||
| evidence, | ||
| }) | ||
| } | ||
| Ok(crate::numerics::NumericalDecisionV1::Determinate { | ||
| value: g, | ||
| guarantee, | ||
| }) => { | ||
| let (core_hex, halo_hex) = | ||
| match crate::glow::glow_layers_from_source(&halo_hex, vc) { | ||
| Ok(pair) => pair, | ||
| Err(e) => { | ||
| return Resolved::Unreachable(Unreachable::InternalInvariant( | ||
| format!("generated Glow layer recipe was rejected: {e}"), | ||
| )); | ||
| } | ||
| }; | ||
| let core_measurement = match crate::glow::measure_screen_layer_at_alpha( | ||
| &core_hex, | ||
| &bg_hex, | ||
| g.alpha(), | ||
| vc, | ||
| ) { | ||
| Ok(measurement) => measurement, | ||
| Err(e) => { | ||
| return Resolved::Unreachable(Unreachable::InternalInvariant(format!( | ||
| "generated Glow core measurement was rejected: {e}" | ||
| ))); | ||
| } | ||
| }; | ||
| Resolved::Glow(GlowResolved { | ||
| core_hex, | ||
| halo_hex, | ||
| alpha: g.alpha(), | ||
| alpha_css: g.alpha_css().to_string(), | ||
| target_dj: g.target_dj(), | ||
| halo_composite_hex: g.composite_hex().to_string(), | ||
| halo_achieved_dj: g.achieved_dj(), | ||
| core_composite_hex: core_measurement.composite_hex, | ||
| core_achieved_dj: core_measurement.achieved_dj, | ||
| target_status: g.status(), | ||
| layer_recipe_profile: | ||
| crate::glow::GlowLayerRecipeProfileV1::Cam16JPrimeOklabCuspV1, | ||
| appearance_diagnostic_profile: | ||
| crate::glow::GlowDiagnosticProfileV1::Cam16UcsJPrimeLi2017V1, | ||
| selection_diagnostic_profile: g.selection_diagnostic_profile(), | ||
| decision_profile, | ||
| decision_guarantee: guarantee, | ||
| halo_composite_certificate: g.composite_certificate().clone(), | ||
| core_composite_certificate: core_measurement.certificate, | ||
| }) | ||
| } | ||
| evidence, | ||
| .. | ||
| }) => assemble( | ||
| &g, | ||
| crate::glow::GlowDecisionOutcomeV1::StableExactNoop { evidence }, | ||
| ), | ||
| Ok(crate::numerics::NumericalDecisionV1::Compatibility { | ||
| value: g, | ||
| release_id, | ||
| provenance, | ||
| .. | ||
| }) => assemble( | ||
| &g, | ||
| crate::glow::GlowDecisionOutcomeV1::Compatibility { | ||
| release_id, | ||
| provenance, | ||
| }, | ||
| ), |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
Замыкание assemble можно выделить в отдельный приватный метод.
Логика довольно объёмная (сборка core/halo слоёв, измерение композита, конструирование GlowResolved) и живёт как локальное замыкание внутри match-ветки. Поведение корректно (поля GlowResolved собраны верно для обоих атомарных исходов, ошибки не паникуют, а превращаются в Resolved::Unreachable), но вынесение в приватную функцию упростило бы читаемость и позволило бы точечно протестировать сборку без похода через solve_screen_alpha_for_dj.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/labcolors-core/src/semantic.rs` around lines 2361 - 2454, Вынеси
локальное замыкание assemble из ветки RoleSpec::Glow в отдельный приватный метод
или функцию, принимающую GlowSolve, GlowDecisionOutcomeV1, источник tint и
фоновые данные. Сохрани без изменений сборку слоёв, измерение core, заполнение
GlowResolved и преобразование ошибок в Resolved::Unreachable; вызов из
solve_screen_alpha_for_dj должен использовать новый выделенный символ для обоих
determinate-исходов.
- Swift-оракул пинует capability schemaVersion == 1; - from_execution_mode матчит точный Glow-release (без стирания identity); - план: sort_by_cached_key (ключ один раз), тайбрейкер удалён как избыточный; - честное имя вакуумного теста незарегистрированного release (+ маркер #291); - mint-гард тестируем на row-уровне (отказная ветвь исполняется), uniqueness покрывает все checksum-списки; - MD022 в verification-map; русификация терминов в packages/colors/README. Отклонено с аргументацией: полная непрозрачность вариантов NumericalDecisionV1 (heavy-lift вне bounded slice: исходы текут только ИЗ core, ядро их не принимает на вход; consumer-verifier — #284/#291) и вынос assemble-замыкания (low value по оценке самого ревьюера). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@coderabbitai Обработаны все 9 замечаний (head Исправлено (7): Swift-пин Отклонено с основанием (2):
|
|
Tip For best results, initiate chat on the files or code changes.
По исправленным (7) — принято, изменения выглядят обоснованными и точечными. По отклонённым (2):
Если понадобится, могу открыть follow-up issue со ссылкой на (๑˃̵ᴗ˂̵)و |
Closes #292. Owner: #289.
Root
OutwardIntervalV1::try_new(форма без доказанности) повышалсяclassify_at_least_v1до Determinate-гарантии; legacy Glow возвращался какDeterminateсо слабой provenance внутри типа «гарантии»; WASM/FFI вручную реконструировали cross-product profile×guarantee×status; registry не отделял capability от compiled invocation.Что сделано (по live-acceptance #292/#289)
NumericalDecisionV1атомарен —Determinate{sealed BitExact}|Compatibility{registered release_id + LegacyPlatformDependentV1}|Indeterminate; незаконные комбинации непредставимы типами; evidence минтится только registry-owned минтером (приватная печать).RoleSpec::Glowнесёт typedNumericalExecutionModeV1(resolver без plan-lookup в hot path);numerical_plan.rs— invocation identity (opaque node bytes + site + локальный ordinal), canonical derived-проекция + FNV drift-checksum;NamedRoleTable::numerical_plan_v1()— точная биекция с Glow-ролями.numericalCapabilitiesвместоnumericalSites[].legacyProfile; пять vector-семейств нетронуты,packDigestпрежний.numericalCapabilityManifest()в WASM/npm; verifier с независимым JS FNV-оракулом; release-manifest schema 2 + read-back в publish.yml.classify_at_least_v1/AtLeastDecisionV1/DecisionGuaranteeV1удалены; дваcompile_faildoc-теста фиксируют миграцию (E0432, E0451 верифицированы standalone).RED → GREEN
Структурный RED (E0425/E0433: несуществующие типы нового контракта) воспроизведён до реализации; RED-группы Issue: биекция/permutation/rename (semantic), legacy=Compatibility (glow/numerics), canonical manifest (numerics) — все GREEN без ослабления.
Proof ledger
entries(); arb_config генерирует Glow (оба mode); ordinals 0,1 на synthetic повторе (node,site).49e5b6b7) — encoding как внешний контракт.release:verify(npm@11.9.0) зелёный.API drift
Breaking Rust API (crate не опубликован на crates.io): перечислено в CHANGELOG. Wire/npm/config fingerprint/цвета/CSS/adaptive trace — без изменений.
Performance (A/B, main 9ac7785 ↔ branch, criterion s=10)
glow stable: 84–106ns → 81–92ns; stable exact-noop: 268–371ns → 250–252ns; legacy base: ~4.8–5.4µs → ~4.6–5.0µs — в пределах шума, регрессии нет; план не в resolve/frame path. WASM: raw 434,158 B, gzip 184,320 B; npm tarball 235,860 B против baseline 0.10.0 211,922 B (+11.3% — включает и #307 appearance-граф, и manifest/plan поверхности; бюджеты — Gate 10 по baseline).
7-lens
Rollback
Revert одного squash-коммита; публичные байты не менялись.
🤖 Generated with Claude Code
Summary by CodeRabbit
Новые возможности
Изменения API
Форматы и проверка
numericalCapabilities.Документация