Skip to content

V2a: совместный hard-feasible point selection - #362

Merged
lemone112 merged 1 commit into
mainfrom
agent/v2a-joint-hard-selection
Jul 20, 2026
Merged

V2a: совместный hard-feasible point selection#362
lemone112 merged 1 commit into
mainfrom
agent/v2a-joint-hard-selection

Conversation

@lemone112

@lemone112 lemone112 commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Срез

Первый private V2a consumer после V1b/S0. Две Paint-переменные исполняются как одна code-owned physical topology:

root Surface
  → lower Paint occurrence
  → surfaceFrom(lower visible)
  → upper Paint occurrence

Что реализовано

  • order-free JointCandidateSetV1 с уникальными CandidateOrdinalV1 и fail-closed duplicate physical tuple admission;
  • отдельный validated DeclaredTotalOrderV1, который не участвует в measurement/report;
  • полный candidate × case joint execution report и candidate × constraint × case hard-report без short-circuit;
  • every-unique-physical-case admission: average, scalar worst и duplicate-provenance weighting отсутствуют;
  • один exact evaluator/classifier path из V1b; Error = Infallible, отдельного fault-verdict нет;
  • checked cardinality и try_reserve_exact до первого compositor/evaluator call;
  • joint evidence связывает program identity, candidate Paint values, derived lower surface, full report и revision-bound observation;
  • fresh joint recheck выбранного tuple на том же immutable observation;
  • только modeled RevisionBoundVerifiedSelectionV1: terminal output certificate, Session, Pair и public Program не вводятся.

RED/acceptance

  • canonical counterexample: локально допустимый lower choice ломает upper target, joint choice проходит;
  • изменение candidate/constraint/scenario declaration order не меняет report;
  • duplicate provenance исполняет physical case один раз;
  • первая violation не обрывает full report;
  • policy является единственным tie-break и валидируется против полного candidate domain до selection;
  • candidate identity mismatch и cardinality overflow отклоняются до композиции;
  • fresh recheck повторно исполняет обе linked occurrences для всех cases;
  • duplicate ordinal/physical tuple и неполный total order имеют typed errors.

Границы

  • Base: S0 merge a32491d7.
  • Один clean commit 6751e624; changed files: joint.rs, joint_tests.rs, lib.rs.
  • Только private labcolors-core; public Rust/WASM/TypeScript/FFI surface не меняется.
  • Pair lowering остаётся отдельным P1 после V2a; F2 Session остаётся независимой параллельной ветвью.
  • Candidate-domain optimization за пределами узкого code-owned finite domain принадлежит общему compiler resource profile, а не этому срезу.

Проверено на предфинальном идентичном code tree

  • cargo fmt --all --check — PASS;
  • cargo clippy --workspace --all-targets --locked -- -D warnings — PASS;
  • cargo test --workspace --locked — PASS;
  • MSRV 1.85 all-targets — PASS;
  • rustdoc, packaged doctests, capability projection и WCAG proof gates — PASS;
  • reproducible runtime WASM build и exact size ratchet — PASS;
  • npm typecheck/runtime tests — PASS;
  • CodeRabbit: changed-files summary produced, unresolved review threads — 0.

Финальный one-commit head повторно проходит canonical Linux и Swift gates; merge только после их полного GREEN.

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Добавлен приватный модуль совместной оценки lower/upper Paint-кандидатов. Он формирует hard-отчёты по уникальным сценариям, классифицирует feasible-кортежи, выбирает их по declared order и выполняет revision-bound recheck выбранного результата.

Changes

Совместная оценка point-selection

Layer / File(s) Summary
Контракты кандидатов и joint-программы
crates/labcolors-core/src/joint.rs, crates/labcolors-core/src/joint_tests.rs, crates/labcolors-core/src/lib.rs
Добавлены типы кандидатов, constraint’ов и программ, проверки уникальности и совместимости Paint, тестовые помощники и подключение модулей.
Hard-оценка и полный отчёт
crates/labcolors-core/src/joint.rs, crates/labcolors-core/src/joint_tests.rs
Реализованы executions и constraint cells для кандидатов и сценариев, provenance, обработка нарушений, проверка кардинальности и каноничность отчёта.
Классификация, выбор и recheck
crates/labcolors-core/src/joint.rs, crates/labcolors-core/src/joint_tests.rs
Добавлены feasibility-классификация, declared total order, выбор кандидата и повторная проверка выбранного ordinal на той же revision со свежими матрицами.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant JointPointProgramV1
  participant FullHardReportV1
  participant DeclaredTotalOrderV1
  participant SelectedJointTupleV1
  Client->>JointPointProgramV1: передаёт candidates и observation
  JointPointProgramV1-->>FullHardReportV1: формирует executions и constraint cells
  FullHardReportV1->>FullHardReportV1: классифицирует feasible ordinals
  Client->>DeclaredTotalOrderV1: передаёт порядок кандидатов
  DeclaredTotalOrderV1->>SelectedJointTupleV1: выбирает feasible ordinal
  SelectedJointTupleV1->>JointPointProgramV1: повторно оценивает выбранный кандидат
  JointPointProgramV1-->>SelectedJointTupleV1: возвращает fresh executions и cells
Loading

Possibly related PRs

  • Labpics-Team/lab-colors#352 — добавляет типы surface-port и revision-bound observation, используемые joint-логикой.
  • Labpics-Team/lab-colors#360 — добавляет hard-decision и evidence-модель, используемые joint-оценкой.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Заголовок кратко и точно отражает основное изменение: совместный hard-feasible point selection для V2a.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent/v2a-joint-hard-selection

Comment @coderabbitai help to get the list of available commands.

@lemone112
lemone112 marked this pull request as ready for review July 20, 2026 04:58

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 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 `@crates/labcolors-core/src/joint.rs`:
- Around line 545-599: Согласуйте API `DeclaredTotalOrderV1` с тестами: измените
`new` на принимающий только `Vec<CandidateOrdinalV1>` и возвращающий `Self` без
валидации, а `select` на возвращающий `Result<SelectedJointTupleV1,
SelectionPolicyErrorV1>`. Перенесите проверку длины, дубликатов и полного
соответствия кандидатов в `select` либо вспомогательный `validate_against`;
замените оба `unreachable!` типизированными ошибками и обновите вызываемый код
под новый результат.
🪄 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: f11ab933-5aae-4302-8b76-10488b5d8692

📥 Commits

Reviewing files that changed from the base of the PR and between a32491d and 957ed8f.

📒 Files selected for processing (3)
  • crates/labcolors-core/src/joint.rs
  • crates/labcolors-core/src/joint_tests.rs
  • crates/labcolors-core/src/lib.rs

Comment on lines +545 to +599
pub(crate) fn select(self, policy: DeclaredTotalOrderV1) -> SelectedJointTupleV1 {
let ordinal = policy
.order
.iter()
.copied()
.find(|ordinal| self.feasible.binary_search(ordinal).is_ok())
.unwrap_or_else(|| unreachable!("validated total order covers nonempty feasible set"));
let candidate = *self
.report
.candidates
.candidates()
.iter()
.find(|candidate| candidate.ordinal == ordinal)
.unwrap_or_else(|| unreachable!("validated ordinal belongs to candidate set"));
SelectedJointTupleV1 {
report: self.report,
policy,
candidate,
}
}
}

/// Полный client-declared tie-break. Он не участвует в measurement/report.
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct DeclaredTotalOrderV1 {
order: Box<[CandidateOrdinalV1]>,
}

impl DeclaredTotalOrderV1 {
pub(crate) fn new(
candidates: &JointCandidateSetV1,
order: Vec<CandidateOrdinalV1>,
) -> Result<Self, SelectionPolicyErrorV1> {
if order.len() != candidates.candidates.len() {
return Err(SelectionPolicyErrorV1::NotATotalOrder);
}
let mut canonical = order.clone();
canonical.sort_unstable();
for pair in canonical.windows(2) {
if pair[0] == pair[1] {
return Err(SelectionPolicyErrorV1::DuplicateOrdinal(pair[0]));
}
}
if canonical
.iter()
.copied()
.ne(candidates.candidates.iter().map(|candidate| candidate.ordinal))
{
return Err(SelectionPolicyErrorV1::NotATotalOrder);
}
Ok(Self {
order: order.into_boxed_slice(),
})
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | 🏗️ Heavy lift

Контракт select / DeclaredTotalOrderV1::new расходится с тестами — модуль не скомпилируется.

В этой реализации:

  • DeclaredTotalOrderV1::new(candidates: &JointCandidateSetV1, order: Vec<…>) -> Result<…> принимает два аргумента и валидирует порядок при построении;
  • select(self, policy) -> SelectedJointTupleV1 возвращает значение напрямую и опирается на unreachable!.

Но joint_tests.rs использует другой контракт:

  • DeclaredTotalOrderV1::new(vec![...])один аргумент (например, строки 121, 259, 355);
  • feasible.select(...).unwrap() и assert_eq!(feasible.select(DeclaredTotalOrderV1::new(vec![])), Err(SelectionPolicyErrorV1::NotATotalOrder)) (строки 125, 354-357), т.е. select должен возвращать Result<SelectedJointTupleV1, SelectionPolicyErrorV1>, а new — не выполнять валидацию.

Эти два контракта несовместимы, тестовый модуль не соберётся. Нужно выбрать единый контракт. Перенос валидации в select дополнительно устраняет unreachable! в public path.

Как per coding guidelines: «Новый или изменяемый public path не должен вызывать panic … invalid, unreachable, unsupported и incomplete context должны возвращаться типизированно».

🐛 Вариант выравнивания под контракт тестов (валидация в `select`)
-    pub(crate) fn select(self, policy: DeclaredTotalOrderV1) -> SelectedJointTupleV1 {
-        let ordinal = policy
-            .order
-            .iter()
-            .copied()
-            .find(|ordinal| self.feasible.binary_search(ordinal).is_ok())
-            .unwrap_or_else(|| unreachable!("validated total order covers nonempty feasible set"));
-        let candidate = *self
-            .report
-            .candidates
-            .candidates()
-            .iter()
-            .find(|candidate| candidate.ordinal == ordinal)
-            .unwrap_or_else(|| unreachable!("validated ordinal belongs to candidate set"));
-        SelectedJointTupleV1 {
-            report: self.report,
-            policy,
-            candidate,
-        }
-    }
+    pub(crate) fn select(
+        self,
+        policy: DeclaredTotalOrderV1,
+    ) -> Result<SelectedJointTupleV1, SelectionPolicyErrorV1> {
+        policy.validate_against(self.candidate_set())?;
+        let ordinal = policy
+            .order
+            .iter()
+            .copied()
+            .find(|ordinal| self.feasible.binary_search(ordinal).is_ok())
+            .ok_or(SelectionPolicyErrorV1::NotATotalOrder)?;
+        let candidate = *self
+            .report
+            .candidates
+            .candidates()
+            .iter()
+            .find(|candidate| candidate.ordinal == ordinal)
+            .ok_or(SelectionPolicyErrorV1::NotATotalOrder)?;
+        Ok(SelectedJointTupleV1 {
+            report: self.report,
+            policy,
+            candidate,
+        })
+    }

и соответственно DeclaredTotalOrderV1::new(order: Vec<CandidateOrdinalV1>) -> Self без валидации, с выносом текущей логики в validate_against.

🤖 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/joint.rs` around lines 545 - 599, Согласуйте API
`DeclaredTotalOrderV1` с тестами: измените `new` на принимающий только
`Vec<CandidateOrdinalV1>` и возвращающий `Self` без валидации, а `select` на
возвращающий `Result<SelectedJointTupleV1, SelectionPolicyErrorV1>`. Перенесите
проверку длины, дубликатов и полного соответствия кандидатов в `select` либо
вспомогательный `validate_against`; замените оба `unreachable!` типизированными
ошибками и обновите вызываемый код под новый результат.

Source: Coding guidelines

@lemone112
lemone112 force-pushed the agent/v2a-joint-hard-selection branch from 9d248c5 to 6751e62 Compare July 20, 2026 05:18

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
crates/labcolors-core/src/joint_tests.rs (1)

335-397: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Отсутствует тест на SelectionPolicyErrorV1::DuplicateOrdinal.

Здесь проверяется только NotATotalOrder (через несовпадение длины: vec![] и vec![0,0] на домене из одного candidate — оба случая падают на проверке длины раньше, чем на проверке дублей). Ветка DuplicateOrdinal (когда order.len() == candidates.len(), но есть повтор — требует минимум 2 candidate) остаётся непокрытой, хотя это тот же изменяемый валидационный путь в DeclaredTotalOrderV1::new.

✅ Предложение добавить кейс
     assert_eq!(
         DeclaredTotalOrderV1::new(
             &domain,
             vec![CandidateOrdinalV1::new(0), CandidateOrdinalV1::new(0)],
         ),
         Err(SelectionPolicyErrorV1::NotATotalOrder)
     );
+
+    let two = candidates(vec![
+        candidate(0, ([0; 3], 1.0), ([0; 3], 1.0)),
+        candidate(1, ([1; 3], 1.0), ([1; 3], 1.0)),
+    ]);
+    assert_eq!(
+        DeclaredTotalOrderV1::new(
+            &two,
+            vec![CandidateOrdinalV1::new(0), CandidateOrdinalV1::new(0)],
+        ),
+        Err(SelectionPolicyErrorV1::DuplicateOrdinal(CandidateOrdinalV1::new(0)))
+    );
 }
🤖 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/joint_tests.rs` around lines 335 - 397, Добавьте в
invalid_domains_and_policies_fail_before_compositing отдельный тест для
DeclaredTotalOrderV1::new с доменом минимум из двух кандидатов и order той же
длины, но с повторяющимся CandidateOrdinalV1; проверьте, что результатом
является SelectionPolicyErrorV1::DuplicateOrdinal, сохранив существующие
проверки NotATotalOrder.
crates/labcolors-core/src/joint.rs (2)

618-641: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Магическая константа 1 без комментария об инварианте.

checked_joint_cardinality(1, cases, self.report.program.constraints.len()) — литерал 1 кодирует «recheck выполняется ровно для одного выбранного candidate», что неочевидно без контекста ниже (core::slice::from_ref(&self.candidate)). Согласно path instructions для crates/labcolors-core/**/*.rs: «Отсутствие магических констант без комментариев». Короткий комментарий, объясняющий инвариант (а не пересказ оператора), сделает этот участок нагляднее для будущих изменений.

✏️ Предложение
         let cases = self.report.observation.set().cases().len();
+        // recheck затрагивает ровно один ранее выбранный candidate —
+        // поэтому cardinality считается для единичного кандидата.
         let (execution_count, cell_count) =
             checked_joint_cardinality(1, cases, self.report.program.constraints.len())
                 .map_err(|_| SelectedRecheckErrorV1::ResourceExhausted)?;
🤖 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/joint.rs` around lines 618 - 641, Добавьте краткий
комментарий непосредственно перед вызовом checked_joint_cardinality в методе
recheck, поясняющий, что значение 1 соответствует инварианту выполнения recheck
ровно для одного выбранного candidate, передаваемого через
core::slice::from_ref(&self.candidate). Не изменяйте вычисления или остальную
логику.

Source: Path instructions


572-598: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

select() не должен опираться на unreachable!()
crates/labcolors-core/src/joint.rs:544-563
Сейчас обе fallback-ветки могут паниковать; для crate-visible API лучше протянуть Result и вернуть типизированную ошибку вместо panic-пути.

🤖 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/joint.rs` around lines 572 - 598, Update select()
to return a Result instead of relying on unreachable!() in either fallback
branch. Replace both panic paths with the appropriate typed
SelectionPolicyErrorV1 value, propagate the result through callers, and preserve
the existing successful selection behavior.

Source: Coding guidelines

🤖 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.

Outside diff comments:
In `@crates/labcolors-core/src/joint_tests.rs`:
- Around line 335-397: Добавьте в
invalid_domains_and_policies_fail_before_compositing отдельный тест для
DeclaredTotalOrderV1::new с доменом минимум из двух кандидатов и order той же
длины, но с повторяющимся CandidateOrdinalV1; проверьте, что результатом
является SelectionPolicyErrorV1::DuplicateOrdinal, сохранив существующие
проверки NotATotalOrder.

In `@crates/labcolors-core/src/joint.rs`:
- Around line 618-641: Добавьте краткий комментарий непосредственно перед
вызовом checked_joint_cardinality в методе recheck, поясняющий, что значение 1
соответствует инварианту выполнения recheck ровно для одного выбранного
candidate, передаваемого через core::slice::from_ref(&self.candidate). Не
изменяйте вычисления или остальную логику.
- Around line 572-598: Update select() to return a Result instead of relying on
unreachable!() in either fallback branch. Replace both panic paths with the
appropriate typed SelectionPolicyErrorV1 value, propagate the result through
callers, and preserve the existing successful selection behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 34343a2a-f681-418e-be83-697b480ba74d

📥 Commits

Reviewing files that changed from the base of the PR and between 957ed8f and 7f897f3.

📒 Files selected for processing (2)
  • crates/labcolors-core/src/joint.rs
  • crates/labcolors-core/src/joint_tests.rs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant