Skip to content

test(core): #297 slice A — RED characterization of the legacy local-search solver - #327

Merged
lemone112 merged 6 commits into
mainfrom
agent/issue-297-honest-local-search
Jul 15, 2026
Merged

test(core): #297 slice A — RED characterization of the legacy local-search solver#327
lemone112 merged 6 commits into
mainfrom
agent/issue-297-honest-local-search

Conversation

@lemone112

@lemone112 lemone112 commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

#297 slice A — RED characterization of the legacy local-search solver

Pre-rename byte/status-shape baseline demanded by #297 («Byte/status-shape characterization happens before rename»). Zero shipped-behavior change: every solve.rs hunk is cfg(test); runtime/compiler WASM bytes untouched (budget stays V6, whole-call artifacts remain bound to their recorded admission versions).

solve / solve_many (issue §RED)

  • Immutable fixture crates/labcolors-core/contracts/solve-characterization-v1.json — 77 cases across bg × sign × magnitude × hue × chroma × floor; hex bytes + f64 payload bits (lc, wcag_ratio, jp, h_ok, s) + floor_override + every error-variant field. Blind rebaseline forbidden by test docstring; recording env-gated, absent from CI.
  • Anti-vacuum counters: successes ≥10 with both polarities and floored/unfloored ≥2, every publicly reachable error class ≥1; polarity_mismatch/internal_invariant pinned ==0. All-Err cannot pass.
  • QuantizationGap pinned publicly EXTINCT (==0): structurally, the −1 Lc acceptance tolerance + QUANT_BUDGET=1 forms a 2-Lc window while wider same-polarity grid windows exist only against the analytic clip (owned by BelowContrastFloor pre-quantisation); empirically corroborated by multi-million-call public-API scans (both polarities, grey+chromatic bgs, hue grid, Relative up to 1.0, all floors, srgb+dim; DisplayP3 dies at the outer gate). Review lane independently reproduced extinction with a 2.88M-call adversarial scan including AaUi low-target pockets and dense >2 Lc grid-window sweeps.
  • solve_many == positional jobs.map(solve): successes, per-job errors, duplicate jobs, empty batch, mixed valid/invalid batch (invalid chroma job is a positional invalid_input that neither shifts nor poisons neighbours), per-job FloorUnreachable on #6E6E6E (dark-side AA ceiling ~4.14 < 4.5) differential-compared to sequential, and the outer GamutUnsupported staying a whole-batch outer error.
  • JND-band characterization vs recheck_against (independent public path, same measurement core — axis consistency, honestly framed): ±1 budget held, reported lc bit-equal to re-measurement, and the tolerant lower acceptance pinned as REAL — target −7.36 on black accepts #323232 at lc −7.3502 (undershoot 0.0098 within the −1 tolerance). «Решено» на этой поверхности значит «в допуске», а не «на-или-за целью» — честные имена обязаны это проговорить.

local-search truth (issue §local-search truth)

  • cfg(test) probe_log: both local searches record every materialized on-grid candidate; tests prove every report (returned colour, gap nearest, degraded achieved_dj) is drawn ONLY from the examined set, and the walk never examines more than seed + NEIGHBOR_STEPS.
  • Counterexample killing the global QuantizationGap/nearest wording at its own seam: on the real hex grid with an injected measurement law, the walk (seed + 2 distinct neighbours) returns QuantizationGap while the 3rd distinct step — the first point the search is forbidden to visit — passes in budget; nearest equals the closest EXAMINED, not the global nearest.
  • Real public-path counterexample for dJ' «nearest achievable»: solve_dj(#FFFFFF, 98.75, +1) degrades to #000000 (achieved 100.0, err 1.25) with the ENTIRE examined set being that one colour, while unexamined same-polarity #010101 achieves 98.0964 (err 0.65) — strictly closer, behind the one-directional walk.
  • No local miss mints GlobalOverDeclaredDomain/ProvenInfeasible — the matrix pins exact terminal classes.

numerical/claim hygiene (issue §numerical/claim hygiene)

  • exposure_quant_and_dj_budgets converted from printed-only to asserted (pinned flip counts 39/2121 Lc and 31/2001 dJ'), scoped to the current 8-bit grey readability grids with an explicit no-perceptual/universal-claim disclaimer. Names in docs/empirical-inventory.md rows 50–51 unchanged.

Evidence

  • The instrumentation sits in the admission benchmark's broad source cone → source-bound re-record: admission V6 (closed recipe, durable-admission PASS, 71 mutation self-checks, same pinned toolchain binaries; subject drift vs V5 = exactly the recorder identity bump — solve.rs is cone-covered, not a subject).
  • ci.yml: V5 → historical replay chain (snapshot 9819bbd, replays PASS via the v5-era checker), V6 = current no-worktree validation; immutable sha block extended v1..v6.
  • release-contract: V1–V5 frozen byte-exact, canonical = V6, identityProjection(V6)==identityProjection(V5) (provenance may move, the admitted finite algorithm may not).
  • No cascade: whole-call boundary artifacts, size budgets, WASM bytes, conformance pack — untouched and re-verified.

Review lanes (already run)

  • Hostile characterization review: PASS on all 8 mandates; found 1 MINOR (mixed valid/invalid batch — fixed in the follow-up commit) + 2 comment nits (fixed); independently corroborated gap extinction (2.88M-call scan).
  • Evidence-chain review: PASS on all 7 items incl. a negative control (v6-era checker rejects the v5 artifact → the historical worktree replay is load-bearing) and a full clone-simulation of the v5 replay.

Local gates: core suite (597 lib + all integration), workspace --locked, clippy -D warnings parity, rustfmt, npm 143/0, docs-drift 25/0, naming inventory, admission V6 durable PASS.

GREEN phase (rename migration Rust→WASM/TS→npm→conformance→Swift→docs, new truthful status family, LocalCandidateSearchReportV1 semantics incl. whether bisection probes count as examined) lands as the next slice per the issue's atomic-migration plan.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Тесты

    • Добавлены платформенные эталонные проверки решения цветов для Linux и macOS.
    • Расширена проверка точности локального поиска и результатов пакетного решения.
    • Зафиксированы детерминированные результаты квантования и обработки JND-полос.
  • Инфраструктура

    • Обновлена проверка WCAG 2.2 feasibility benchmark до версии v6.
    • Добавлены контрольные данные, проверки целостности и воспроизведение исторических версий benchmark.
  • Надёжность

    • Усилена валидация результатов и обнаружение отклонений между платформами и версиями артефактов.

lemone112 and others added 4 commits July 16, 2026 02:00
)

Pre-rename byte/status-shape baseline on current main:

- immutable fixture contracts/solve-characterization-v1.json (77 cases:
  bg x sign x magnitude x hue x chroma x floor; f64 pinned by bits) with
  anti-vacuum counters — successes both polarities, floored/unfloored,
  every publicly reachable error class >= 1;
- QuantizationGap pinned as publicly EXTINCT on the solid-bg matrix
  (post-#44 walk always bridges the 8-bit dead zone; ~3.5M-call scan
  across hue/chroma/floor/vc/gamut found zero) — the variant's truth is
  pinned at its own seam instead;
- solve_many positional property incl. duplicates, empty batch and outer
  gamut failure staying outer;
- cfg(test) probe_log instrumentation: both local searches record every
  materialized candidate; reports are proven to be drawn ONLY from the
  examined set;
- counterexamples killing the global wording (#297 local-search truth):
  solve_quantization_neighbor returns QuantizationGap while the 3rd
  distinct step (outside NEIGHBOR_STEPS) passes in budget — 'nearest' is
  closest EXAMINED, not global; solve_dj at dJ' 98.75 on white degrades
  to #000000 (err 1.25) while unexamined #010101 sits strictly closer
  (err 0.65) BEHIND the one-directional walk;
- JND-band characterization vs the independent recheck_against oracle:
  +-1 budget honoured, reported lc bit-equal to re-measurement, and the
  tolerant lower acceptance (undershoot within -1 Lc) actually fires
  (target -7.36 on black accepts #323232 at lc -7.3502);
- exposure_quant_and_dj_budgets converted from printed-only to asserted
  (pinned 39/2121 and 31/2001 flip counts on the current grey grids).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-record (#297)

The characterization instrumentation lands inside the admission dependency
cone (crates/labcolors-core/src), so the source-bound benchmark must be
re-recorded; per the closed-recipe rule the recorder identity is bumped
BEFORE recording so the new snapshot covers the already-bumped machinery.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The cfg(test) instrumentation lands in crates/labcolors-core/src, which sits
in the admission benchmark's broad source cone, so the source-bound artifact
is re-recorded on the canonical local machine (closed recipe, PASS,
71 mutation self-checks; durable-admission mode).

- wcag22-feasibility-benchmark-v6.json: new canonical admission
  (sha256 2e319643…, benchmark binary 1e43b4c8…, same pinned toolchain
  c5922366…/fec239e6…); subject drift vs V5 is exactly the recorder
  identity bump — solve.rs is cone-covered but not an admission subject;
- ci.yml: V5 moves to the historical replay chain (snapshot 9819bbd),
  V6 becomes the current no-worktree validation; immutable sha block
  extended;
- release-contract: V1-V5 frozen byte-exact (V5 hash added), canonical
  switched to V6, identity projection pinned V6==V5 (provenance may move,
  the admitted finite algorithm may not).

Runtime and compiler WASM bytes are untouched (cfg(test) never ships):
size budget stays V6, whole-call boundary artifacts remain bound to their
recorded admission versions (append-only, byte-exact).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Hostile-review lane findings addressed:

- MINOR: the solve_many positional differential now carries the
  issue-mandated mixed valid/invalid batch (a chroma-ratio job outside
  [0,1] between valid neighbours must be a positional invalid_input that
  neither shifts nor poisons position 8) and a separate mid-grey #6E6E6E
  batch where dark-on-light AA-text is mathematically floor-unreachable
  (ceiling ~4.14 < 4.5), differential-compared against sequential solve;
- NIT: the extinction comment now leads with the structural argument
  (the -1 Lc acceptance tolerance + QUANT_BUDGET=1 forms a 2-Lc window;
  wider same-polarity grid windows exist only against the analytic clip,
  owned by BelowContrastFloor pre-quantisation) and states scan axes
  honestly (DisplayP3 dies at the outer gate and cannot exercise the
  search);
- NIT: the JND-band docstring no longer calls recheck_against an
  'independent oracle' — it is an independent public PATH sharing
  lpc::contrast_core; the test pins axis consistency, not independent
  derivation of the metric.

Fixture unchanged (the batch tests do not record).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 52 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7aad41cb-c6a9-4ea8-8cc8-9907530e9331

📥 Commits

Reviewing files that changed from the base of the PR and between 4904347 and 0a9f4ec.

📒 Files selected for processing (1)
  • crates/labcolors-core/tests/solve_characterization.rs

Walkthrough

Добавлены проверки локальности результатов solver и платформенные characterization-контракты. WCAG22 feasibility benchmark обновлён до raw-v6, а CI и release-contract tests теперь проверяют v5 historical replay и текущий v6 artifact.

Changes

Solver и characterization

Layer / File(s) Summary
Локальность результатов solver
crates/labcolors-core/src/solve.rs
Test-only журнал фиксирует реально исследованные кандидаты; новые тесты проверяют локальные nearest и degraded результаты и закрепляют budget counters.
Битовая characterization-проверка solver
crates/labcolors-core/contracts/solve-characterization-v1-*.json, crates/labcolors-core/tests/solve_characterization.rs
Добавлены платформенные fixtures и интеграционные тесты с побитовой сериализацией, проверками solve_many, JND, ошибок и допустимого platform drift.

WCAG22 benchmark

Layer / File(s) Summary
WCAG22 v6 artifact contract
crates/labcolors-core/contracts/wcag22-feasibility-benchmark-v6.json, crates/labcolors-core/benches/wcag22_feasibility_admission.rs, scripts/check_wcag22_feasibility_benchmark.py
Добавлен v6 feasibility report; benchmark producer и checker используют raw-v6 artifact и artifactId.
Исторический replay и CI validation
.github/workflows/ci.yml, packages/colors/test/release-contract.test.mjs
CI и release-contract tests разделяют v5 historical replay и v6 current validation, проверяя pinned SHA256, snapshots, protocols и шесть worktrees.

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

Possibly related issues

  • Issue 297 — изменения в solve.rs реализуют instrumentation и проверки локальности examined-кандидатов.

Possibly related PRs

  • Labpics-Team/lab-colors#316 — связан с WCAG22 feasibility compilation sets и contract evidence, используемыми benchmark validation.
  • Labpics-Team/lab-colors#317 — затрагивает тот же CI и script pipeline для historical/current benchmark validation.
  • Labpics-Team/lab-colors#323 — связан с обновлением pinned artifacts и historical/current feasibility checks.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Заголовок точно отражает основное изменение: RED-characterization для legacy local-search solver.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent/issue-297-honest-local-search

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

CI exposed the very thesis of #297 on the first replay: the fixture recorded
on macos-aarch64 does not replay bit-for-bit on linux-x64 — the current
release is LegacyPlatformDependent, concretely and narrowly:

- 74/77 cases are bit-identical across both canonical platforms, including
  every emitted hex byte, lc, wcag_ratio, floor_override, jp and s bit and
  every error payload;
- the ENTIRE drift is the ulp tail of one field in three cases: the
  CAM16->Oklab hue correlate h_ok (libm atan2/cbrt differences; 5 ulp on two
  chromatic mid-grey cases, and an amplified ~1e-8 delta on one achromatic
  result where hue is degenerate — atan2 of noise against noise).

The fixture is therefore pinned PER PLATFORM:
- solve-characterization-v1-macos-aarch64.json — recorded on the canonical
  local machine (renamed from the original single fixture, bytes unchanged);
- solve-characterization-v1-linux-x64.json — the canonical CI runner's own
  rendered observation from the PR #327 replay, which that runner verifies
  bit-for-bit from now on;
- fixture_path() selects by target platform and fails LOUDLY on platforms
  without a recorded baseline (no silent skip);
- platform_fixtures_agree_except_documented_hue_ulp_drift pins the drift
  exhibit exactly: same 77-case matrix, and the differing (case, field) set
  must remain precisely the three documented h_ok entries — growth of the
  exhibit is a numerical behavior change, not new-platform noise.

This gives the #290/#292 numerical-provenance work its first committed
cross-platform exhibit while keeping the rename-migration invariance gate
bit-exact on both CI platforms.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lemone112

Copy link
Copy Markdown
Collaborator Author

CI-реплей на linux-x64 сработал как задумано и немедленно поймал тезис #297: фикстура macos-aarch64 не реплеится бит-в-бит на Linux. Дрифт хирургический — 3 кейса из 77, только ulp-хвост h_ok (CAM16→Oklab hue; 5 ulp на хроматике + вырожденный ахроматический hue). Всё остальное — hex/lc/wcag_ratio/floor_override/jp/s и все payload'ы ошибок — бит-идентично. Фикстуры теперь платформенные (macos-aarch64 + linux-x64, вторая — дословный вывод самого CI-раннера из этого PR), а сам дрифт запинен тестом platform_fixtures_agree_except_documented_hue_ulp_drift как первый закоммиченный кросс-платформенный экспонат для #290/#292. Push перезапустит CI.

@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: 2

🤖 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/tests/solve_characterization.rs`:
- Around line 350-356: Измените recorder-ветку теста solve characterization: не
перезаписывайте committed fixture, а записывайте наблюдаемый результат в
отдельный candidate-файл. После записи завершайте recorder-run с ошибкой, чтобы
переменная LABCOLORS_RECORD_SOLVE_CHARACTERIZATION, включая значение 0, не могла
скрыть регрессию и дать успешный CI.

In `@scripts/check_wcag22_feasibility_benchmark.py`:
- Around line 31-32: Update DEFAULT_ARTIFACT in the checker to derive its
default directory from tempfile.gettempdir(), matching the benchmark’s
std::env::temp_dir() output location; preserve the existing artifact filename
and avoid hardcoding /private/tmp.
🪄 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: 54842b2d-d8df-4cde-9503-6a129de58fd3

📥 Commits

Reviewing files that changed from the base of the PR and between 9819bbd and 4904347.

📒 Files selected for processing (9)
  • .github/workflows/ci.yml
  • crates/labcolors-core/benches/wcag22_feasibility_admission.rs
  • crates/labcolors-core/contracts/solve-characterization-v1-linux-x64.json
  • crates/labcolors-core/contracts/solve-characterization-v1-macos-aarch64.json
  • crates/labcolors-core/contracts/wcag22-feasibility-benchmark-v6.json
  • crates/labcolors-core/src/solve.rs
  • crates/labcolors-core/tests/solve_characterization.rs
  • packages/colors/test/release-contract.test.mjs
  • scripts/check_wcag22_feasibility_benchmark.py

Comment thread crates/labcolors-core/tests/solve_characterization.rs Outdated
Comment thread scripts/check_wcag22_feasibility_benchmark.py
CodeRabbit major: the env-gated recorder could silently rebaseline the
committed fixture and pass. Adopted the repo's append-only recorder
discipline (same as admission/whole-call evidence):

- recording refuses to overwrite an existing committed fixture — a
  rebaseline requires deliberately deleting the file first;
- a recording run always ends in a loud panic, so it can never be
  mistaken for a verified replay; the replay must be rerun without the
  env var to go green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lemone112
lemone112 merged commit 3a8d0a6 into main Jul 15, 2026
11 checks passed
@lemone112
lemone112 deleted the agent/issue-297-honest-local-search branch July 15, 2026 23:57
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