Skip to content

Proof: the decision chain binds what the sources produce, not who watched the build (V5b2d-4b) - #547

Merged
lemone112 merged 8 commits into
mainfrom
v5b2d-4b-comparator-source-identity
Aug 6, 2026
Merged

Proof: the decision chain binds what the sources produce, not who watched the build (V5b2d-4b)#547
lemone112 merged 8 commits into
mainfrom
v5b2d-4b-comparator-source-identity

Conversation

@lemone112

@lemone112 lemone112 commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Наблюдаемый дефект

Два прогона идентичного main (31089986150, 31090010890) дают разную comparator.identity. Полосы, посчитанные против одного RUN, не принимаются свежим RUN — а значит дуальное доказательство недостижимо в принципе:

  • source-bound квитанции не имеют wire-формы намеренно (иначе провенанс подделывается из байтов), поэтому join обязан идти в процессе RUN;
  • посчитать там же 512 полос нельзя: ~46 ч однопоточной реплики против конверта 8 ч.

Что показал замер

координата между прогонами
binary_identity равны — сборка из закреплённых исходников байт-воспроизводима между раннерами
8 source-координат манифеста равны
build_identity различаются ровно 96 байт: 32 docker capability + 2×32 наблюдения процессов сборки
test_observation (Arb) различается

То есть математика доказательства воспроизводима целиком; не воспроизводится только наблюдение среды сборки, замешанное в идентичность.

Закон

У компаратора две идентичности, и граница проходит по вопросу «может ли решение от этого зависеть».

Решающая цепь связывает source_identity = kind + engine_release, upstream_source, arithmetic_input_set, wrapper_source, evaluator_source, operation_allowlist, legal_file_set, exclusions. Движку передаётся именно она в --manifest-identity, поэтому её несут транскрипт, run claim, accounting-префикс и lane-манифест.

Цепь провенанса связывает полную identity: дуальный клейм, семантическая квитанция и обе source-bound квитанции. Какая сборка произвела каждый движок — ровно то, что дуальное доказательство удостоверяет, и эта запись не ослаблена.

Почему не наоборот — почему не вынести наблюдение из идентичности вообще: оно там намеренно, arb/tests/test_build_identity_v2.py:476-481 явными ассертами требует участия docker_capability_identity. Это решение о провенансе, а не недосмотр, и правка аддитивна.

source_identity — производное свойство. Wire-грамматика манифеста не меняется, бандл компаратора по-прежнему едет вместе с evidence.

Ключ lane-манифеста переименован в comparator_source_identity: старые артефакты обязаны отвергаться закрыто, а не переинтерпретироваться.

Доказательства

  • test_a_lane_survives_another_run_of_the_same_sources — компараторы, различающиеся ТОЛЬКО в исключённых координатах, имеют разные полные идентичности, равные source-идентичности, и полоса одного допускается под другим.
  • test_a_lane_still_dies_on_a_different_source_closure — anti-vacuity: смена evaluator_source по-прежнему отвергает полосу закрыто.

Проверки

  • Локально: 254 теста, 7 ошибок — ровно базовые пробелы среды Windows (fcntl / invalid native process cwd), новых нет.
  • Слой arb//mpfi/ локально не импортируется, его проверяет CI.

Rollback

git revert одного коммита. Изменение чисто вычислительное: замороженные фикстуры и пины controller.py не тронуты, wire-грамматика не менялась.

Что открывает

После мержа: новый RUN → 512 полос → полосы переживают повторный RUN → in-process join → DualProofReceiptV1(full_domain=True) и mint-гейт. Exit-критерии V5b2d №1 и №2 уже закрыты замером (counters=(1, 16777215, 0, 0), decision_bits побайтно идентичны у обоих движков).

Summary by CodeRabbit

  • Новые возможности

    • Добавлено разделение идентичности компаратора на исходную и полную.
    • Транскрипты, claims запуска, accounting и lane-манифесты теперь используют идентичность исходных данных.
    • Обновлена схема lane-манифестов до версии 2.
    • Изменения наблюдений сборки не влияют на идентичность исходников, обеспечивая совместимость артефактов между запусками.
  • Исправления

    • Улучшена проверка связности манифестов, receipts, транскриптов и claims.
    • Изменения исходных компонентов корректно выявляются и отклоняются при проверке.
  • Тесты

    • Расширены проверки переносимости lane и корректности идентичностей.

…ched the build (V5b2d-4b)

Наблюдаемый дефект: два прогона идентичного main дают РАЗНУЮ
`comparator.identity`, поэтому полосы, посчитанные против одного RUN, не
принимаются свежим RUN. Дуальное доказательство становится недостижимым:
source-bound квитанции wire-формы не имеют намеренно, значит join обязан идти в
процессе RUN, а посчитать там 512 полос нельзя (~46 ч реплики против конверта 8 ч).

Замер на прогонах 31089986150/31090010890: `binary_identity` РАВНЫ — сборка из
закреплённых исходников байт-воспроизводима между раннерами. Различаются ровно
96 байт внутри `build_identity` (32 docker capability + 2×32 наблюдения
процессов сборки) и `test_observation` у Arb. Восемь координат из десяти,
выводимых из источников, совпадают.

Закон: у компаратора две идентичности, и граница проходит по вопросу «может ли
решение от этого зависеть».

- Решающая цепь связывает `source_identity` (kind + восемь source-координат).
  Движку передаётся именно она в `--manifest-identity`, поэтому её несут
  транскрипт, run claim, accounting-префикс и lane-манифест. Ключ манифеста
  полосы ПЕРЕИМЕНОВАН в `comparator_source_identity`, чтобы старые артефакты
  отвергались закрыто, а не переинтерпретировались.
- Цепь провенанса связывает полную `identity`: дуальный клейм, семантическая
  квитанция и обе source-bound квитанции. Какая сборка произвела каждый движок
  — ровно то, что дуальное доказательство удостоверяет; запись не ослаблена.

`source_identity` — производное свойство, wire-грамматика манифеста не меняется.
Наблюдение среды остаётся в бандле компаратора, который едет вместе с evidence.

Доказательства: `test_a_lane_survives_another_run_of_the_same_sources` —
компараторы, различающиеся ТОЛЬКО в исключённых координатах, имеют разные
полные идентичности, равные source-идентичности, и полоса одного допускается
под другим. Anti-vacuity: `test_a_lane_still_dies_on_a_different_source_closure`
— смена `evaluator_source` по-прежнему отвергает полосу закрыто.

Локально: 254 теста, 7 ошибок — ровно базовые пробелы среды Windows. Слой
arb/mpfi локально не грузится, его проверяет CI.
@coderabbitai

coderabbitai Bot commented Aug 6, 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: 55 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: 9b494515-4320-4196-a4a3-22e16727a8bd

📥 Commits

Reviewing files that changed from the base of the PR and between 5c5b071 and f0079bb.

📒 Files selected for processing (5)
  • proof/region/v1/PROTOCOL.md
  • proof/region/v1/corpus_assembly.py
  • proof/region/v1/corpus_lane.py
  • proof/region/v1/tests/test_region_proof_protocol.py
  • proof/region/v1/tests/test_semantic_replay.py

Walkthrough

Изменение разделяет полную identity компаратора и производную source_identity. source_identity используется в transcript, run claim, accounting и lane-манифестах. Полная identity сохраняется для provenance и dual comparison.

Changes

Source-bound identity

Layer / File(s) Summary
Контракт source_identity
proof/region/v1/region_proof_protocol.py, proof/region/v1/PROTOCOL.md, proof/region/v1/tests/test_region_proof_protocol.py
Добавлены source-bound координаты и методы source_identity. Transcript и RunClaimV1 используют эту identity. Dual comparison сохраняет полные identities манифестов.
Привязка выполнения и accounting
proof/region/v1/corpus.py, proof/region/v1/semantic/verifier.py, proof/region/v1/arb/receipt.py, proof/region/v1/mpfi/receipt.py, proof/region/v1/tests/test_semantic_replay.py, proof/region/v1/tests/test_dual_proof.py, proof/region/v1/tests/test_corpus_shards.py, proof/region/v1/tests/test_build.py
Accounting, replay, evaluator и RUN-транскрипты используют source_identity. Тесты обновляют проверки производных идентификаторов.
Lane assembly и verification
proof/region/v1/corpus_lane.py, proof/region/v1/corpus_assembly.py, proof/region/v1/tests/test_corpus_assembly.py, proof/region/v1/tests/test_corpus_lanes.py, proof/region/v1/tests/test_comparator_wire.py
Схема lane изменена на corpus-lane-v2. Поле lane-манифеста переименовано в comparator_source_identity. Assembly и accounting используют source-bound identity.
Проверка verification evidence
proof/region/v1/tests/test_verification_evidence.py, proof/region/v1/arb/tests/full_domain_receipt.py, proof/region/v1/mpfi/tests/full_domain_receipt.py, proof/region/v1/tests/test_verification_assembly.py
Тесты проверяют связывание transcript и run claim с source_identity, переносимость lane между прогонами тех же исходников и отказ при изменении evaluator_source.

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

Sequence Diagram(s)

sequenceDiagram
  participant ComparatorManifestV2
  participant ShardCorpusRunnerV1
  participant LaneAssembly
  participant Evaluator
  ComparatorManifestV2->>ShardCorpusRunnerV1: source_identity
  ShardCorpusRunnerV1->>LaneAssembly: transcript и accounting с source_identity
  LaneAssembly->>Evaluator: comparator_source_identity
  Evaluator->>LaneAssembly: проверка transcript и run claim
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Заголовок точно описывает основной переход: цепочка решений использует идентичность источника, а не наблюдения сборки.
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 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch v5b2d-4b-comparator-source-identity

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

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

Caution

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

⚠️ Outside diff range comments (2)
proof/region/v1/corpus_assembly.py (2)

135-151: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Рассмотрите поднятие версии LANE_SCHEMA_V1 вместе с переименованием ключа.

Ключ манифеста изменён с comparator_identity на comparator_source_identity, но значение schema осталось прежним. Артефакт, созданный предыдущей версией, проходит проверку схемы на строке 135 и отвергается позже как lane manifest binds a foreign comparator_source_identity. Причина отказа при этом — устаревший формат, а не чужой компаратор.

Если поднять версию схемы, отказ станет точным (foreign lane schema), и старые lane-каталоги будут отличимы от подделанных.

🤖 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 `@proof/region/v1/corpus_assembly.py` around lines 135 - 151, Поднимите версию
схемы, используемую при проверке в assembly flow вокруг LANE_SCHEMA_V1,
одновременно с переименованием manifest-ключа comparator_identity в
comparator_source_identity. Обновите все связанные определения и генерацию
манифеста так, чтобы старые lane-каталоги отвергались на проверке schema с
причиной FOREIGN_INPUT/"foreign lane schema", а не на проверке identities.

415-431: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Сохраните совместимость assembly-receipt.json.

ASSEMBLY_SCHEMA_V1 не изменился, но ключ comparator_identity заменён на comparator_source_identity. Сохраните comparator_identity со значением comparator.identity или выпустите новую версию схемы с миграцией потребителей.

🤖 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 `@proof/region/v1/corpus_assembly.py` around lines 415 - 431, В формировании
receipt рядом с comparator_source_identity восстановите ключ comparator_identity
со значением comparator.identity, сохранив совместимость assembly-receipt.json
при неизменном ASSEMBLY_SCHEMA_V1; не заменяйте его новой схемой без миграции
потребителей.
🤖 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 `@proof/region/v1/PROTOCOL.md`:
- Around line 499-516: Согласуйте списки wire-полей с новым разделом: в
описаниях третьего поля DecisionTranscriptV1 и второго поля RunClaimV1 замените
«comparator manifest identity» на «comparator source identity». Другие поля и
утверждения не изменяйте.

In `@proof/region/v1/region_proof_protocol.py`:
- Around line 873-897: Update source_identity to encode the ordered coordinate
count and each coordinate’s length in the folded preimage before the existing
coordinate bytes, using SOURCE_BOUND_COORDINATES_V2 and the values retrieved
from self. Preserve the current identity domain label, kind binding, and
coordinate order while making future variable-length coordinates unambiguous.
- Around line 60-75: Добавьте в proof/region/v1/region_proof_platform.py в
области BUILD_OBSERVATION_COORDINATES_V2 инвариант, проверяющий, что объединение
build- и source-координат в точности совпадает с полями ComparatorManifestV2 без
kind; сохраните SOURCE_BOUND_COORDINATES_V2 синхронизированным со схемой. В
proof/region/v1/tests/test_region_proof_protocol.py:1289-1294 добавьте проверки,
что source_identity не меняется при изменении build_identity и test_observation,
но меняется при изменении каждой из восьми source-координат.

In `@proof/region/v1/semantic/verifier.py`:
- Line 121: В цикле вокруг transcript.point_count переименуйте неиспользуемую
переменную expected_index в _expected_index, не изменяя остальную логику цикла.

In `@proof/region/v1/tests/test_corpus_assembly.py`:
- Around line 114-120: Move the lane comparator content map into corpus_lane as
a shared LANE_COMPARATOR_CONTENTS_V1 symbol, use it when constructing
lane_comparator_v1, and update _lane_bundle_contents in test_corpus_assembly.py
to return that map instead of recreating the coordinate literals and range.

In `@proof/region/v1/tests/test_semantic_replay.py`:
- Around line 363-377: Update both accounting grammar tests, including
test_mpfi_accounting_digest_matches_independent_packing, to use
comparator.source_identity instead of comparator.identity in the expected manual
packing and accounting_prefix_v1 call. Keep the rest of the digest construction
unchanged.

In `@proof/region/v1/tests/test_verification_evidence.py`:
- Around line 255-265: Replace receipt.comparator.manifest.identity with
receipt.comparator.manifest.source_identity in all three manually constructed
DecisionTranscriptV1 and RunClaimV1 instances referenced here, including the
cases around the foreign job identity and transcript binding tests. Preserve the
intended setup so each rejection test differs in only the single coordinate it
is designed to invalidate.

---

Outside diff comments:
In `@proof/region/v1/corpus_assembly.py`:
- Around line 135-151: Поднимите версию схемы, используемую при проверке в
assembly flow вокруг LANE_SCHEMA_V1, одновременно с переименованием
manifest-ключа comparator_identity в comparator_source_identity. Обновите все
связанные определения и генерацию манифеста так, чтобы старые lane-каталоги
отвергались на проверке schema с причиной FOREIGN_INPUT/"foreign lane schema", а
не на проверке identities.
- Around line 415-431: В формировании receipt рядом с comparator_source_identity
восстановите ключ comparator_identity со значением comparator.identity, сохранив
совместимость assembly-receipt.json при неизменном ASSEMBLY_SCHEMA_V1; не
заменяйте его новой схемой без миграции потребителей.
🪄 Autofix

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: b5f50a31-887e-4b8e-b913-061c6c9329bd

📥 Commits

Reviewing files that changed from the base of the PR and between d2a09aa and 96e00c8.

📒 Files selected for processing (16)
  • proof/region/v1/PROTOCOL.md
  • proof/region/v1/arb/receipt.py
  • proof/region/v1/corpus.py
  • proof/region/v1/corpus_assembly.py
  • proof/region/v1/corpus_lane.py
  • proof/region/v1/mpfi/receipt.py
  • proof/region/v1/region_proof_protocol.py
  • proof/region/v1/semantic/verifier.py
  • proof/region/v1/tests/test_comparator_wire.py
  • proof/region/v1/tests/test_corpus_assembly.py
  • proof/region/v1/tests/test_corpus_lanes.py
  • proof/region/v1/tests/test_corpus_shards.py
  • proof/region/v1/tests/test_dual_proof.py
  • proof/region/v1/tests/test_region_proof_protocol.py
  • proof/region/v1/tests/test_semantic_replay.py
  • proof/region/v1/tests/test_verification_evidence.py

Comment thread proof/region/v1/PROTOCOL.md
Comment thread proof/region/v1/region_proof_protocol.py
Comment on lines +873 to +897
@cached_property
def source_identity(self) -> bytes:
"""Identity of everything the comparator derives from its sources.

The full identity also binds `build_identity` and `test_observation`,
and those two coordinates fold in the build observation — the docker
capability and the build processes' console digests. That is a
deliberate provenance record, but it is not reproducible: two runs of
the identical source tree on two runners produce identical binaries
and identical decisions while their observations differ.

A decision the engine reaches does not depend on which daemon watched
the build, so the decision chain binds this coordinate instead: the
engine's transcript, its accounting and the replay lanes stay portable
across runs, while the receipt keeps the full identity and loses
nothing about the environment it was built in.
"""

return _identity(
COMPARATOR_SOURCE_ID_LABEL_V2,
bytes((int(self.kind),))
+ b"".join(
getattr(self, name) for name in SOURCE_BOUND_COORDINATES_V2
),
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

Кодируйте порядок и длину координат явно.

source_identity конкатенирует восемь digest-ов фиксированной длины 32 байта, поэтому текущая склейка однозначна. Однако _require_digest проверяет длину только в __post_init__, а домен-разделение опирается на неизменность набора координат. Зафиксируйте количество координат в префиксе фолда, чтобы будущее добавление координаты переменной длины не создало неоднозначного preimage.

Это не текущий дефект, а защита от дрейфа. Решение остаётся за вами.

🤖 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 `@proof/region/v1/region_proof_protocol.py` around lines 873 - 897, Update
source_identity to encode the ordered coordinate count and each coordinate’s
length in the folded preimage before the existing coordinate bytes, using
SOURCE_BOUND_COORDINATES_V2 and the values retrieved from self. Preserve the
current identity domain label, kind binding, and coordinate order while making
future variable-length coordinates unambiguous.

Comment thread proof/region/v1/semantic/verifier.py Outdated
Comment thread proof/region/v1/tests/test_corpus_assembly.py Outdated
Comment thread proof/region/v1/tests/test_semantic_replay.py Outdated
Comment thread proof/region/v1/tests/test_verification_evidence.py Outdated
Claude Code added 4 commits August 6, 2026 15:07
Пин run_identity сдвинулся: run claim теперь связывает source-идентичность
компаратора. Обновлён по замеру CI.

Блок перестроен на subTest по координатам: характеризационный пин, падающий на
первом несовпадении, скрывает, какая часть цепи реально сдвинулась, — а цепь и
есть то, что этот тест характеризует. Диагностическая ценность падения теперь
равна полному срезу цепи, а не одной строке.
Сдвиг цепи объяснён полностью и проверен арифметикой, а не принят на веру:
evidence.identity = H(label, source_identity, build_identity, run_identity),
и локальный пересчёт с НЕИЗМЕННЫМИ source_identity/build_identity и новой
run_identity даёт ровно то значение, которое сообщил CI
(f4a4431f2f9a92070e1ad2ada94bf7f9fc83a4b8c7c0d7c26a2e30e80567e2f9).

Значит посторонних сдвигов нет: из шести характеризуемых координат четыре
совпали, включая comparator.identity (полную) и build_identity — провенанс не
тронут, как и заявлено. Сдвинулись ровно две производные от run claim, который
теперь связывает source-идентичность компаратора.
…ail after six hours

Две независимые линзы ревью дали FAIL. Находки закрыты.

1. КРИТИЧНОЕ. arb/tests/full_domain_receipt.py и mpfi/tests/full_domain_receipt.py
   сравнивали transcript.comparator_identity с ПОЛНОЙ идентичностью манифеста —
   после перехода решающей цепи на source-идентичность это вечно ложно. Оба
   модуля исполняются только диспатчем native_gate.py full-domain-receipt: ни
   локальный набор, ни PR-CI их не запускают, поэтому прогон на 2^24 упал бы в
   самом конце, потеряв и RUN, и весь последующий диспатч полос. Добавлена
   парная anti-vacuity: две идентичности обязаны различаться, иначе проверка
   сравнивала бы значение с самим собой.

2. Версия схемы полосы поднята до corpus-lane-v2. Грамматика манифеста
   изменилась (ключ и семантика значения), а версия-строка называла обе
   несовместимые грамматики одним именем: старые полосы отвергались как «чужой
   компаратор» вместо честного отказа по версии — вводящий в заблуждение
   диагноз для артефакта, который никогда не был неверным.

3. PROTOCOL.md противоречил сам себе: wire-разделы DecisionTranscriptV1 и
   RunClaimV1 по-прежнему называли координату «comparator manifest identity»,
   тогда как соседний раздел и код кладут туда source-идентичность.
   Имплементатор по этим разделам отверг бы каждый текущий транскрипт.

4. Негативные фикстуры в test_verification_evidence.py и
   test_verification_assembly.py строились с полной идентичностью, поэтому
   отказ срабатывал на посторонней координате раньше заявленной: при регрессии
   проверок job_identity или transcript_identity тесты остались бы зелёными.
   Прицел восстановлен.

5. Уточнено утверждение о замере: «ровно 96 байт» относится к build_identity,
   отдельно различается test_observation у Arb.

Локально: 254 теста, 7 ошибок — ровно базовые пробелы среды Windows.
…and-kept list

Находка CodeRabbit (Major), подтверждена: `SOURCE_BOUND_COORDINATES_V2` был
свободным списком строк, а протокольные тесты проверяли только полную
`identity`. Потерянная или переименованная координата молча выпала бы из фолда
`source_identity`, и решающая цепь перестала бы различать компараторы,
отличающиеся по ней. Ни один тест этого не заметил бы.

Закрыто выводом, а не заплаткой: перечисляются ДВЕ координаты наблюдения сборки
(`BUILD_OBSERVATION_COORDINATES_V2`), а source-bound набор выводится из
`fields(ComparatorManifestV2)` как всё остальное. Новая координата манифеста
теперь попадает в фолд автоматически, а не ускользает из него молча. Порядок
объявления совпадает с прежним ручным списком, поэтому значения не сдвинулись —
перепинивать нечего (проверено на артефактах обоих прогонов).

Защита сделана настоящей четырьмя тестами: разбиение покрывает манифест точно и
без пересечения; КАЖДАЯ source-координата сдвигает source_identity (subTest по
координатам); обе координаты наблюдения сдвигают только полную identity; kind
связан — полоса под бюджетом чужого движка не пройдёт.

Прочее из того же ревью: B007 в semantic/verifier.py; координаты lane-компаратора
больше не дублируются в тесте — вынесены в `lane_comparator_contents_v1()`, и
обе стороны читают один источник (дубль дрейфовал бы и всплывал как
«unresolved content address», пряча настоящую причину).

Замечание про длину-префикс в фолде отклоняю обоснованно: элементы —
digest-ы фиксированных 32 байт, а набор теперь выводится из схемы, поэтому
склейка однозначна и без счётчика; добавление сдвинуло бы все идентичности
решающей цепи без выигрыша.

Локально: 258 тестов, 7 ошибок — ровно базовые пробелы среды Windows.
@lemone112

Copy link
Copy Markdown
Collaborator Author

@coderabbitai Все находки обработаны в afd4593.

SOURCE_BOUND_COORDINATES_V2 без инварианта схемы (Major) — подтверждено и закрыто выводом, а не заплаткой. Перечисляются теперь ДВЕ координаты наблюдения сборки (BUILD_OBSERVATION_COORDINATES_V2), а source-bound набор выводится из fields(ComparatorManifestV2) как всё остальное. Новая координата манифеста попадает в фолд автоматически, а не ускользает молча. Порядок объявления совпал с прежним ручным списком, поэтому значения не сдвинулись — проверено на артефактах обоих полнодоменных прогонов.

Защита сделана проверяемой четырьмя тестами (ComparatorSourceIdentityTests): разбиение покрывает манифест точно и без пересечения; КАЖДАЯ source-координата сдвигает source_identity (subTest по координатам); обе координаты наблюдения сдвигают только полную identity; kind связан.

Противоречие нового раздела с wire-списками (Minor) — уже закрыто в 79dfdc7 до вашего ревью: разделы DecisionTranscriptV1 и RunClaimV1 теперь называют координату source-идентичностью.

B007 в semantic/verifier.py — исправлено.

Дублирование координат lane-компаратора — согласен, две копии одной истины. Вынесено в corpus_lane.lane_comparator_contents_v1(), тест читает продакшн-источник. Ровно как вы описали: дубль дрейфовал бы и всплывал как «unresolved content address», пряча настоящую причину.


Длина/порядок координат в префиксе фолда (Trivial) — отклоняю обоснованно.

Элементы фолда — digest-ы фиксированных 32 байт (_require_digest в __post_init__), а сам набор после правки выводится из схемы, а не хранится рядом с ней. Значит склейка однозначна и без счётчика: неоднозначность требовала бы элементов переменной длины либо набора, способного разойтись со схемой, — ни того, ни другого больше нет.

Цена при этом реальная: префикс сдвинул бы source_identity, а с ней транскрипт, run claim и все производные пины решающей цепи — без выигрыша в различимости. Если видите конкретный вход, где текущая склейка неоднозначна, покажите — переоценю.

Прошу перепроверить afd4593.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

git show --no-ext-diff --format=fuller --stat afd4593
printf '\n--- changed paths ---\n'
git diff-tree --no-commit-id --name-only -r afd4593

printf '\n--- target symbols ---\n'
ast-grep outline proof/region/v1/region_proof_protocol.py --items all --match 'ComparatorManifestV2|BUILD_OBSERVATION_COORDINATES_V2|SOURCE_BOUND_COORDINATES_V2|source_identity|identity' --view expanded || true
ast-grep outline proof/region/v1/corpus_lane.py --items all --match 'lane_comparator_contents_v1' --view expanded || true
ast-grep outline proof/region/v1/tests/test_region_proof_protocol.py --items all --match 'ComparatorSourceIdentityTests' --view expanded || true

printf '\n--- relevant diff ---\n'
git show --no-ext-diff --format= --unified=100 afd4593 -- \
  proof/region/v1/region_proof_protocol.py \
  proof/region/v1/corpus_lane.py \
  proof/region/v1/tests/test_region_proof_protocol.py \
  proof/region/v1/tests/test_corpus_assembly.py \
  proof/region/v1/semantic/verifier.py \
  proof/region/v1/PROTOCOL.md

Length of output: 64874


The first inspection confirms the schema-derived partition and fixed-width invariant. I will make one consistency check across the decision chain and protocol text.

You are interacting with an AI system.

Правки писались через write_text(newline="\n"), из-за чего четыре файла,
хранящиеся в репозитории с CRLF, переписались целиком: diff показывал 1028 и 620
изменённых строк там, где содержательных изменений единицы. Ревьюер и человек
теряют способность увидеть настоящую правку.

Содержимое не менялось — только окончания строк возвращены к тому виду, в
котором файлы лежат в репозитории.

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

🤖 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 `@proof/region/v1/corpus_lane.py`:
- Around line 58-63: Make the address unpacking explicit in lane_comparator_v1
by passing the keys from contents to protocol.ComparatorManifestV2 rather than
relying on implicit dictionary iteration. Preserve the generator-defined
ordering and keep the existing admit call unchanged.

In `@proof/region/v1/PROTOCOL.md`:
- Around line 488-491: Удалите из документации временные benchmark-данные:
идентификаторы прогонов, значение «96 байт» и конкретные детали наблюдений из
описанного фрагмента. Замените их кратким нормативным правилом о том, что build
observations могут различаться, но не изменяют source_identity.

In `@proof/region/v1/tests/test_region_proof_protocol.py`:
- Around line 105-115: В тесте test_the_split_covers_the_manifest_exactly
добавьте проверки, что observation не пуст и строго меньше полного набора
coordinates. Сохраните существующие проверки покрытия и непересечения без
изменений.
🪄 Autofix

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: fab2d975-130b-472c-9b30-5040745894db

📥 Commits

Reviewing files that changed from the base of the PR and between 96e00c8 and 5c5b071.

📒 Files selected for processing (14)
  • proof/region/v1/PROTOCOL.md
  • proof/region/v1/arb/tests/full_domain_receipt.py
  • proof/region/v1/corpus_lane.py
  • proof/region/v1/mpfi/tests/full_domain_receipt.py
  • proof/region/v1/region_proof_protocol.py
  • proof/region/v1/semantic/verifier.py
  • proof/region/v1/tests/test_build.py
  • proof/region/v1/tests/test_corpus_assembly.py
  • proof/region/v1/tests/test_corpus_lanes.py
  • proof/region/v1/tests/test_dual_proof.py
  • proof/region/v1/tests/test_region_proof_protocol.py
  • proof/region/v1/tests/test_semantic_replay.py
  • proof/region/v1/tests/test_verification_assembly.py
  • proof/region/v1/tests/test_verification_evidence.py

Comment thread proof/region/v1/corpus_lane.py
Comment thread proof/region/v1/PROTOCOL.md Outdated
Comment on lines +488 to +491
`31089986150`/`31090010890`: `binary_identity` равны; внутри `build_identity`
различаются ровно 96 байт (32 docker capability + 2×32 наблюдения процессов), и
отдельно различается `test_observation` у Arb, который сворачивает те же
наблюдения процессов.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Удалите временный benchmark dump из протокольной документации.

Идентификаторы прогонов 31089986150/31090010890 и измерение 96 байт не являются стабильным протокольным контрактом. Оставьте краткое нормативное правило: build observations могут различаться, но не меняют source_identity.

As per coding guidelines: «временный benchmark dump … не коммитятся».

🤖 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 `@proof/region/v1/PROTOCOL.md` around lines 488 - 491, Удалите из документации
временные benchmark-данные: идентификаторы прогонов, значение «96 байт» и
конкретные детали наблюдений из описанного фрагмента. Замените их кратким
нормативным правилом о том, что build observations могут различаться, но не
изменяют source_identity.

Source: Coding guidelines

Comment thread proof/region/v1/tests/test_region_proof_protocol.py
Claude Code added 2 commits August 6, 2026 17:58
… preimage

Находки двух независимых линз ревью. Линза корректности дала PASS, линза
правдивости — FAIL по одному пункту; все закрыты.

1. Раздел «Две идентичности компаратора» утверждал, что build_identity и
   test_observation сворачивают наблюдение сборки. Неполно: они сворачивают И
   воспроизводимые входы (байты build recipe, build_input_identity,
   formula_support_identity, pipeline_policy_identity, sha и длину бинаря).
   Исключены они потому, что СОДЕРЖАТ наблюдение, а не потому что состоят
   только из него.

   Отсюда цена разделения, которую документ обязан назвать прямо: решающая цепь
   перестаёт различать компараторы, отличающиеся ТОЛЬКО воспроизводимыми входами
   сборки — тот же исходник, собранный другим рецептом или давший другой бинарь.
   Математику это не задевает (пять source-координат остаются), а полная identity
   с бинарём и рецептом по-прежнему связывается квитанцией и дуальным клеймом.

2. Замер «ровно 96 байт» был Arb-специфичен. У MPFI координата несёт вложенный
   digest, состав дельты иной, а test_observation между прогонами совпал. Общее
   для обоих движков — только невоспроизводимость build_identity. Скоуп проставлен.

3. Reference заявлял source_identity, но не задавал её преимидж, а общая формула
   identity покрывает только top-level artifacts с magic. Независимая реализация
   не воспроизвела бы координату, которую теперь несёт каждый транскрипт и run
   claim. Преимидж выписан точно.

4. Accounting-грамматика в разделе верификатора говорила «job/domain/policy/
   comparator identities» без уточнения, какая из двух. Имплементатор, читающий
   только этот буллет, свернул бы полную identity и получил вечный
   ACCOUNTING_REPLAY_MISMATCH. Тот же класс, что уже исправлялся в wire-разделах.

5. LANE_SCHEMA_V1 = "corpus-lane-v2": имя утверждало V1 при значении v2.
   Переименовано в LANE_SCHEMA_V2 — долг, внесённый прошлым исправлением.

6. Два grammar-теста accounting подавали полную identity — последний след старой
   цепи в тестах. Вердикт они не искажали (обе стороны сверки брали одно
   значение), но переиспользование как оракула молча разошлось бы с продакшном.

ВЫНЕСЕНО отдельным срезом, не смешиваю: у mpfi/tests нет аналога
arb/tests/test_native_gate.py, поэтому пины режимов MPFI пересчитываются только
при dispatch-запуске — тот же класс «гейт падает после часов работы». Разрыв
предсуществующий, моей правкой не задет; его закрытие сдвинет собственный
инвентарный пин быстрого гейта MPFI и требует круга CI.

Локально: 258 тестов, 7 ошибок — ровно базовые пробелы среды Windows.
Второй круг CodeRabbit, три находки, все верные.

1. Я поместил в нормативный reference историю замера — номера прогонов
   31089986150/31090010890 и величину «ровно 96 байт». AGENTS.md прямо
   запрещает коммитить временный benchmark dump, и по делу: контракт должен
   говорить, ЧТО обязано выполняться, а не как это однажды измерили. Номера
   прогонов устареют, дельта изменится с версией docker, а правило —
   `binary_identity` воспроизводится, `build_identity` нет — останется.
   История измерения живёт в PR и журнале.

2. Анти-вакуумность собственного гейта разбиения: проверки проходили и при
   ПУСТОМ BUILD_OBSERVATION_COORDINATES_V2, а тогда цикл по координатам
   наблюдения не исполнил бы ни одного subTest. Разбиение выглядело бы
   доказанным, не проверяя ничего. Непустота обоих множеств закреплена.

3. `*contents` распаковывал ключи словаря, то есть контент-адреса, но из
   вызова читалось так, будто в манифест уходит содержимое. Адреса названы
   явно.

Локально: 258 тестов, 7 ошибок — ровно базовые пробелы среды Windows.
@lemone112
lemone112 merged commit e420e21 into main Aug 6, 2026
10 checks passed
@lemone112
lemone112 deleted the v5b2d-4b-comparator-source-identity branch August 6, 2026 15:27
lemone112 added a commit that referenced this pull request Aug 7, 2026
…proof can be sealed (V5b2d-4d) (#549)

* Proof: one process that mints both receipts is the only place a dual proof can be sealed (V5b2d-4d)

`join_dual_proof_v1` needs five evidence chains, and two of them — the
engines' source-bound receipts — have no wire form by design: a receipt that
could be parsed from bytes would let foreign code mint provenance.  So the
join has no valid two-job topology at all; it can only happen inside a
process that minted both receipts itself.

This adds that process.  `tests/dual_proof_gate.py` builds and runs both
engines over the full manifest back to back, re-seals each engine's semantic
receipt from the verification lanes, and seals the dual proof.  The lane
cover comes from an earlier run and admits here only because a lane binds the
comparator's source identity (#547); the same identity sorts the cover by
engine, so nothing trusts an artifact's name.

Both engine lane modules now expose `seal_full_domain_receipt_v1`, so the
gate mints the same receipt those lanes do instead of restating the build
coordinates — two copies would drift, and the drift would surface as an
unrelated admission failure an hour into a native run.

The gate fails, never skips, on an incomplete environment: it is only ever
invoked deliberately, so a silent pass would be a proof that did not happen.

Measured, not assumed:
  - both engines' source locks pin byte-identical GMP and MPFR, so one job
    can serve both without an env collision; a future divergence exits 64
  - the native RUNs took 63 min (Arb) and 43 min (MPFI) on run 31116022208,
    so the sequential pair fits one job's envelope
  - executor cgroups are named per process with a counter and removed on
    close, so two sequential executions do not collide

`test_decorator_placement.py` closes the class of defect this change itself
introduced: extracting the helper put it between the module's env gate and
the class that gate guarded, which silently made the helper uncallable and
left the native test ungated.  Syntax checks and the fast suite both saw
nothing — the lane modules live outside the `test_*.py` inventory.  The new
gate reads the tree as source, like the arity gate beside it.

* Proof: the dual-proof job could not have run — two blockers found by review

Two independent reviews of the final state found that the job this branch
added could never have sealed anything.  Both are fixed here.

CONTAINMENT.  The controller that observes a BUILD to RUN enters the observer
group and stays there: non-test code writes `cgroup.procs` in exactly two
places — the observer placement and the executed child's attach — and neither
walks it back.  With one shared subtree at `pids.max=2`, the second engine's
BUILD forks docker into a saturated budget and dies, an hour into the run.
The single-engine lanes never met this because `arb.yml` puts the engines in
separate steps and `full-domain-run.yml` in separate jobs; one process for
both engines happens first here, and the dual proof admits no other topology.

So each engine now gets its own observer subtree, and `enter_task_cgroup_v1`
returns the controller to the unconstrained group between them — where a
fresh process would have started.  No run's containment widens: the observer
budget still governs each execution.

DELIVERY.  A lane is one dispatch, one run, one artifact, so a full-domain
cover is spread across 512 runs while the job read a single `run-id` — and
the input was declared `lane_run_ids` but read as `lane_run_id`.  Worse, lane
artifacts carried no engine in their name, so an Arb lane and its MPFI twin
claimed one name and could not coexist under one root.  The cover now spans
many runs through the shape `full-domain-corpus.yml` already uses, and the
artifact name carries the engine.  Which engine a lane serves is still
decided by the comparator source identity in its manifest, never by a name.

A cover that is short, overlapping, or single-engine is now refused in
seconds, before the first build, instead of after both native runs.

Also from review:
  - the anti-vacuity check was theatre: receipt identity seals job,
    comparator, run and transcript and never the cover, so two engines could
    not collide in it by construction.  Replaced with the property it claimed
    — the two covers are disjoint sets of lane directories.
  - the preflight listed 4 of the 10 variables the seal functions read, so a
    partial environment surfaced as a bare KeyError instead of the refusal
    this gate promises.
  - `seal_full_domain_receipt_v1` returns a union; an unchecked rejection
    became `AttributeError` two hours in, losing the reason.  Typed now.
  - the gate skipped the hostile `replay_evidence_is_well_bound_v1` check its
    single-engine lanes run, and this is the only place a full-domain receipt
    is ever sealed.  Restored, with the transcript's domain and comparator
    coordinates.
  - module resolution across the engines' five colliding stems was correct
    only by the order of two loads.  Asserted instead of assumed.

Verified: 260 local tests, 7 errors — the exact Windows baseline, unchanged.
The new `enter_task_cgroup_v1` tests cannot run here at all: `test_executor`
needs `fcntl`.  CI is the only place they execute.

* Proof: pin the workflow contract the expensive job was missing

The dispatch workflows are pinned by the coordinates they must carry, which
catches a missing coordinate but not a misspelt one — and a misspelt one is
what happened: the dual-proof input was declared `lane_run_ids` and read as
`lane_run_id`.  GitHub expands an undeclared input to the empty string, so
nothing fails until the job has run.  On this job that is two hours of native
execution before noticing the lane cover was empty.

`test_every_referenced_input_is_declared` closes the class for every workflow
at once, reading the declarations by indentation rather than parsing: the
proof tree carries no YAML dependency.  Proven red on the real drift, not a
mock — reintroducing exactly `inputs.lane_run_id` into the live workflow
makes the gate name it undeclared, and the fixed file is clean.  It covers
three workflows and eleven references today, with an anti-vacuity floor so a
gate that stopped finding anything cannot pass quietly.

`DualProofContainmentContractTests` pins the two properties whose absence made
the job unrunnable: each engine owns its observer subtree (a shared one admits
two tasks, and the second engine's BUILD forks into it), and the cover is
gathered from many runs rather than one `run-id`.  It also pins that the cover
is checked before the first build, so the cheap refusal cannot drift back
behind the expensive work.

* Proof: the containment fix shipped a red CI and a second tautology

Re-review of the final state found that the previous commit would have failed
CI deterministically and that one of its own repairs repeated the defect it
replaced.

THE RED.  `arb/tests/gate.py` pins the exact inventory of the fast suite, and
that suite includes `tests/test_executor.py` through its shared patterns.
Adding three tests there moved the hash and left the pin behind, so the first
step of `arb.yml` would have printed inventory drift and returned 1 before
building anything.  Recomputed independently with an AST replica of the gate's
own algorithm — the replica reproduces the parent commit's pinned value byte
for byte, which is what makes its answer for HEAD trustworthy: 270 tests,
b1bc948c…  None of this is observable here; the gate needs `fcntl`, so the
machine that wrote the drift could not run the check that catches it.

THE SECOND TAUTOLOGY.  Replacing the theatre assert with "the two covers are
disjoint" was theatre again: `_lane_cover_v1` partitions by comparator source
identity, and the two engines cannot share one — the sets are disjoint by
construction, and the line could not fail in any state the gate reaches.  What
is genuinely unguaranteed is that the partition consumed the whole cover: a
lane of a third identity is foreign to both engines and disappears silently.
The union of both covers must now equal every lane directory present.

Also from review:
  - the MPFI hostile check was vacuous — that receipt's constructor already
    requires the predicate, so the assert could not fail.  Only Arb's remains,
    where the constructor binds something narrower.  The previous commit
    claimed to restore both; it restored one.
  - `enter_task_cgroup_v1` took the caller's word for where it landed.  The
    observer placement is always followed by a budget probe; this one now
    verifies its own post-condition, because the path arrives from
    configuration and a wrong one silently moves where the next BUILD runs.
  - the module-collision guard treated an absent import as safety.  Absence
    retires the check instead of failing it, so it is a refusal now.
  - PROTOCOL.md still called the observer placement the only versioned
    cross-module placement operation.  There are two, and the second is
    documented with why it cannot widen a run's containment.

Verified: 265 local tests, 7 errors — the exact Windows baseline, unchanged.
The recomputed inventory now equals the pin.

* Proof: name a lane run after the coordinates it replays

A full-domain cover is 512 separate runs, and the dual proof needs their ids.
Without a name carrying the coordinates the only way to collect them is a
guess about creation times, which breaks the moment two campaigns overlap.
The run name makes the list a query — and makes a lane identifiable in the
UI, where 512 identically-named runs are otherwise indistinguishable.

* Proof: compare the entered group by inode, the way the observer probe does

The post-condition added a moment earlier compared paths by spelling, while
every other placement check in this module compares by inode — `fstat` on both
descriptors — precisely because one group can be reached by more than one
name.  A false refusal here is not a cheap failure: it aborts a job two hours
into two native runs, which is the same cost as having no check at all.

* Proof: verify on the Linux that was here all along, and fix what it showed

The last two rounds shipped a red CI each because the checks that catch
inventory drift cannot run on Windows — and WSL, a real Linux where this whole
layer imports, was available the entire time.  Everything below was measured
by running it there, not derived.

MY OWN TEST WAS RED.  The post-condition added last round refuses when the
process is not in the named group, and the test I wrote for that placement
builds a fake cgroup in a temporary directory — where no fixture can move this
process.  It now patches the current-cgroup reader the way the observer probe
test beside it already does, and the post-condition finally has a test that
proves it fires: an invariant added without one was exactly the omission the
reviews keep finding.

THE SECOND PIN.  `tests/test_build.py` keeps an independent outer oracle of
the same inventory — count, inventory digest and discovery-order digest — on
purpose, so a coordinated edit to the gate cannot hide drift.  Updating only
the gate left it stale, and the oracle did its job by failing.  All three
constants now carry values the gate actually printed on Linux: 271 tests,
inventory 86c723aa…, order fe5a3419…  The order digest is over discovery order
and cannot honestly be derived by hand; it was read off a run.

Both gates verified green there: the fast Arb gate reports 271 tests with its
exact 15-skip manifest, and the outer oracle passes.

PROTOCOL.md's new paragraph was mechanically spliced — a 200-column line, an
orphaned clause whose "this descriptor protocol" had drifted onto the wrong
subject, and wording describing a string comparison the code no longer does.
Rewritten as its own paragraph.

Verified on Linux: 419 tests, 3 failures.  All three are pre-existing and
mine only by proximity — I reproduced them on a worktree whose proof tree is
untouched `main`, where the same three fail identically.  Windows still gives
265 tests and 7 load errors; that is the platform's ceiling, not a result.

* Proof: two runs claiming one lane must not resolve by whoever downloaded last

The cover is gathered from many runs into one directory, so a re-run of the
same window overwrites the evidence already there.  The result still looks
exact — the window is present once, the arithmetic adds up — while one of two
answers silently won, and nothing records which.  Each run now lands in its
own staging directory and the move into the flat cover refuses a name that is
already taken.

Lane run ids are validated as numbers, the way `verification-lanes.yml`
already validates its own: an element starting with `-` would be read by `gh`
as a flag rather than a run.

Both are pinned by the workflow contract tests, so they cannot drift back.

Verified: 7 contract tests pass; the workflow still parses to 11 steps.

Left open and named rather than hidden: the cheap preflight proves the cover
is two exact domains, but not that those two identities belong to the engines
this job is about to build.  Closing that needs the comparator's source
identity derived from the checkout without building — the coordinates are all
static, but no such derivation is exposed today.

---------

Co-authored-by: Claude Code <daniilerosov12@gmail.com>
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