docs(P-022): parity-work discipline + status reconciliation at fdcb222 - #322
Conversation
Two halves of one change, deliberately together: the rules that say how to stop status drift, and the reconciliation that clears the drift those rules were learned from. ## Parity-work discipline (new section) Four rules, each paid for by a real defect during step 5a (#255, PRs #319/#320/#321), written wider than this port so they outlive P-022: 1. Oracle over reviewer prose — a finding is a hypothesis until reproduced against the reference; keep the observed behaviour, not the reviewer's explanation. (A review argued the word-boundary rule from single-ended probes; the reference builds a both-ended pattern. Right conclusion, wrong reason — implementing the reason would have been wrong.) 2. Mutation over plausible tests — a regression test is not evidence until the matching mutation fails it THROUGH the production surface it claims to protect. (The ordering replay sorted a parallel vector with a copy of the key; it would have passed against sort_unstable_by, the exact defect it existed for.) 3. No fail-fast during mutation campaigns — expose all catching layers, not the first failing target. (cargo test halts on the first target; with --no-fail-fast the same mutation showed three catchers, and a second mutation was caught only at the replay layer.) 4. Insertion-stable generated goldens — vocabulary-derived shape must depend on stable item identity, never ordinal position. Rule 4's law is the INVARIANT, not the mechanism: insert one synthetic vocabulary member existing-record churn == 0 new-record delta == 1 A content hash is today's way of satisfying it, not the requirement; any stable mapping conforms and swapping it is not a violation. Writing the hash into the norm would turn an implementation detail into scripture. Single home by design — not duplicated into AGENTS.execution-surfaces.md, because two copies of one law drift, which is what the status-drift rule exists to prevent. ## Status reconciliation Written fresh against the tree at fdcb222, NOT carried over from the earlier unmerged reconciliation: a stale block patched with a stale fix stays stale. The old branch is abandoned rather than cherry-picked. What was wrong on main: - #258 still described as "land with PR #297, in independent review; not on main yet" — closed completed, both spec documents on main. - own-lowered and own-bridge absent from the workspace list; both are members. - 5a listed as a future step — #255 closed completed today. - own-diagnostics described as the data-only layer — it now carries the full normalized contract. What it says now: checkpoint-level status with each open step separating its normative blocker from #250's preferred sequencing, #259 broken out per its own five checkpoints (2 and 3 complete with counts, 1 partial, 4 not started, 5 unblocked-but-not-done), and #260/#269 keeping the sliceable-vs-declarable distinction. Preferred queue recorded: #256 → #259 remaining → #260/#269. The proposals index row is corrected in the same change — a third status surface for the same fact, and leaving it stale would repeat the defect. #250 is a GitHub issue and cannot ride in a git commit; its body is synchronised in the same move, per its own rule that both surfaces change together. Docs only. No code, fixtures or acceptance touched. The executable guard for rule 4 currently implements `churn == 0`; extending it to `delta == 1`, plus three review nitpicks from #321, lands in the follow-up test PR. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CJF7MBi1ijU5m9cJVWgQsM
📝 WalkthroughWalkthroughThe P-022 documentation now contains reconciled migration statuses, detailed ChangesP-022 migration documentation
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai review |
|
@coderabbitai review Повторный триггер: автоматический nudge отработал в 09:11Z, а лимит сбрасывался ~09:19Z — на восемь минут раньше, поэтому команда прошла впустую. Сейчас квота доступна. CI зелёный, combined status Generated by Claude Code |
|
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 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 `@docs/proposals/P-022-rust-core-migration.md`:
- Line 49: Update the Markdown lines containing issue references `#258` and `#259`
so each reference is prefixed with normal prose rather than beginning the line.
Apply the same adjustment to the corresponding reference at the later reported
location, preserving the surrounding wording and meaning while eliminating MD018
violations.
- Around line 660-672: Update the “Normative acceptance” section to explicitly
mark the new-record delta == 1 assertion as pending/not yet enforced, while
retaining churn == 0 as the currently executable guard. Ensure the documentation
does not imply that both acceptance criteria are already covered by tests.
- Line 9: Update the “Implementation status” heading to use the expected
Markdown heading hierarchy by changing it from level three to level two, unless
a valid missing parent heading is intentionally added.
In `@docs/proposals/README.md`:
- Line 44: Update the P-022 status in the proposals index to use the documented
“in progress” value instead of “in execution,” preserving the rest of the status
description unchanged.
🪄 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: CHILL
Plan: Pro Plus
Run ID: 2ee760d9-8660-4847-a42d-3f6835e36c9c
📒 Files selected for processing (2)
docs/proposals/P-022-rust-core-migration.mddocs/proposals/README.md
Three of four CodeRabbit findings on 2cb3822, each checked against the tree before acting (rule 1 of the section this PR adds). - Rule 4 declared `churn == 0` AND `delta == 1` as normative acceptance while `_insertion_churn()` gates only the first. A generator that dropped the new record entirely would pass every executable check. The norm stays as written; the gap is now stated rather than implied, which is the whole point of the status-drift rule this PR introduces. - `## Implementation status`: every other top-level section in the file is `##`; this one was the only `###` hanging directly off the h1. - Reflowed two paragraphs so `#258` and `#319` no longer begin a line. Renders the same on GFM (ATX needs a space), but the pattern is fragile on non-CommonMark renderers and trips MD018. The fourth finding is rejected in the PR thread: "in execution" is pre-existing on main, matches the proposal's own Status line, and changing only the index row would manufacture the exact drift this PR removes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CJF7MBi1ijU5m9cJVWgQsM
…itpicks (#323) P-022 rule 4 states two lines as normative acceptance; the executable guard implemented only the first. This brings the guard up to the norm accepted in #322, and no further. _insertion_effect() returns churn / added / removed. Each of the three mutations is caught by exactly one number, with the other two structurally blind to it: seed = sorted index churn=42 added=1 removed=0 iterate the golden's codes churn=0 added=0 removed=0 cap ledger at previous size churn=0 added=1 removed=1 (baseline) churn=0 added=1 removed=0 The third number came from a Codex finding during review: a generator capped at its previous size emits the probe AND drops an existing code. The dropped record never enters the churn comparison and the addition still counts one, so both original numbers read clean while the ledger had not grown at all. This does not widen rule 4 — a delta is a gain, and counting only additions was measuring the stated norm wrong. The churn gate sits behind the staleness check, so it was additionally verified after regenerating under the mutation, where it fires with its own message. Three nitpicks from the #321 review, also mutation-checked: * analyzer_corpus read with expect, matching every other accessor in the file; dropping the key from one case now fails naming it, where unwrap_or(false) would have silently under-counted coverage. * changed == unexplained asserted explicitly, since they move together by construction and an added explanation channel must split them deliberately. Recorded honestly: on a green tree this is 0 == 0 and proves nothing. Shown to have teeth by a compound mutation (corrupt one recorded text so a divergence exists, drop the unexplained increment) — it fires first, naming the counter split rather than the divergence. * every_optional_field_shape_is_still_exercised guards the nine arms the generator rotates. Collapsing the two-evidence arm and regenerating fails only this test: the divergence check still passes 47/47, because it asks whether each case matches its own recorded text, never whether a shape disappeared. Test-only; no production code touched on either side. Refs #255, #250.
Что и зачем
Две половины одного изменения, намеренно вместе: правила, как перестать допускать status drift, и сверка, убирающая тот drift, на котором эти правила и выучены.
Разводить их было бы третьим заходом на те же грабли — чинить статус отдельным PR от правила «обе поверхности меняются одним изменением».
Тип изменения
Как проверено
python tests/run_tests.pyruff check .иmypypython scripts/<...>.py --selftest)Изменение docs-only; гейты прогнаны, чтобы подтвердить отсутствие побочных эффектов. Все факты сверены прогоном против дерева на
fdcb222, а не по памяти — первое же правило нового раздела.Связанные issue
Refs #250, #255, #256, #258, #259. Ничего не закрывает.
Чеклист
feat:,fix:,docs:…)Раздел Parity-work discipline
Четыре правила, каждое оплачено настоящим дефектом на шаге 5a (#255, PR #319/#320/#321). Формулировки намеренно шире этого порта — ни одно не зависит от Rust, Python или слоя диагностик, поэтому они переживут P-022 и пригодятся следующей миграции, которая прикалывает одну реализацию к другой.
\b-foo), тогда как эталон строит двусторонний шаблон. Вывод верен, обоснование — нет; реализовать обоснование значило бы ошибитьсяsort_unstable_by, ровно того дефекта, ради которого написанcargo testостанавливается на первом таргете; с--no-fail-fastта же мутация дала три ловца, а вторая ловилась только на replay-слоеИнвариант нормативен, хеш — нет
Для четвёртого правила закон записан как acceptance, а не как рецепт:
Content hash — сегодняшний способ это выполнить, а не требование. Любой стабильный маппинг, держащий обе строки, соответствует норме, и его замена не нарушение. Иначе через два года корректная замена выглядела бы ересью при правильном поведении.
Единственная оговорка по механизму: он обязан быть воспроизводим между процессами —
hash()в Python рандомизирует хеширование строк и не годится.Один дом
Правила не дублируются в
AGENTS.execution-surfaces.md. Они родились как доказательная дисциплина конкретной parity-миграции, а не как общая инструкция агенту; две копии одного закона разъезжаются — ровно то, что призвано предотвращать правило про status drift.Сверка статуса
Написана заново против дерева на
fdcb222. Предыдущая (неслитая) сверка не переносилась: протухший блок, залатанный протухшим фиксом, остаётся протухшим — она сама успела устареть, объявляя #255 «ready». Старая ветка брошена, а не cherry-pick'нута.Что было неверно на
main:#258описан как «land with PR spec(bridge): #258 — executable own-bridge contract (Bridge.md + behavior matrix) #297, in independent review; not onmainyet» — закрыт completed, оба spec-документа наmain;own-loweredиown-bridgeотсутствовали в списке воркспейса, хотя оба его члены;5aзначился будущим —#255закрыт completed сегодня;own-diagnosticsописан как data-only слой — он несёт полный нормализованный контракт.Что теперь: checkpoint-level статус, где у каждого открытого шага различены нормативный блокер и предпочтительная последовательность из #250.
#259разложен по его собственным пяти checkpoint'ам — cp2 и cp3 complete с числами (27/27 lowering, 35 summaries goldens), cp1 partial, cp4 not started, cp5 разблокирован #255, но не сделан: его набор сравнения теперь есть, а сравнивать пока нечего без wiring из cp4. У#260/#269сохранено различие sliceable-инфраструктуры и недостижимого пока final acceptance.Зафиксирована очередь: #256 → остаток #259 (cp1 → cp4 → cp5) → #260/#269.
Строка P-022 в индексе предложений исправлена тем же изменением — это третья статусная поверхность того же факта, и оставить её протухшей значило бы повторить дефект прямо в PR, который его лечит.
Про #250
Это GitHub issue, он физически не едет в git-коммите. Его body синхронизируется тем же ходом — по его же правилу, что обе поверхности меняются вместе. Там:
#255отмечен Done,#256— Ready с удовлетворённым блокером, у#259cp5 явно перечислен остаток acceptance, allocation переставлен на#256→ остаток#259, и из status-drift правила дана ссылка на новый раздел.Что НЕ входит
Исполняемый guard для правила №4 сейчас реализует только
churn == 0. Доведение доdelta == 1плюс три nitpick'а из ревью #321 — следующим отдельным test-PR, и он обязан мутационно доказать обе половины acceptance.Раунд ревью (
267e38c)CodeRabbit дал 4 находки на
2cb3822. Каждая проверена против дерева до правки — это первое правило раздела, который тот же PR и вводит, так что применить его именно здесь было принципиально.delta == 1не гарантируется тестомImplementation status#258/#319в начале строкиin execution→in progressв индексеПро
delta == 1. Проверено в дереве:_insertion_churn()возвращает только счётчик churn,run()гейтит поif churn:— delta не вычисляется нигде. То есть генератор, который вовсе выбросил бы новую запись, прошёл бы все исполняемые проверки, пока документ читался так, будто закрыты обе строки. Ровно тот разрыв документа и дерева, ради которого этот PR и существует. Теперь разрыв назван явно; сама норма не менялась — по тексту правила №4 законом является инвариант, а не текущая обвязка.Про отклонённую находку.
in executionуже был наmainв этой же ячейке (diff менял только текст после статуса), аP-022-rust-core-migration.md:3содержитStatus: **in execution**на строке, которую PR не трогает. Переписать только строку индекса значило бы оставить индекс противоречащим тому предложению, которое он индексирует, — то есть изготовить тот самый drift. Плюс словарь применён избирательно: в той же таблице живутv0 built,first slice built,draft (stub),accepted (design; impl post-cutover, #304).Обе committable-подсказки ревьюера были к тому же синтаксически битые —
(`#214/`#249)и`#319/`#320/#321с непарными бэктиками. Ещё одна причина не принимать подсказки на веру.MD018, для точности: на CommonMark это ложное срабатывание — ATX-заголовок требует пробела после
#, поэтому#258на GitHub рендерится текстом. Переформатировано всё равно: вне CommonMark паттерн ломается, а цена — два переноса строки. Отдельно: markdownlint в этом репозитории не настроен и не запускается в CI, это собственный линтер ревьюера, не гейт.