feat(bridge): wire the lowered module through the core analyses (#259 cp4) - #336
Merged
Conversation
The OwnIR bridge maps a core verdict back to a fact handle through the diagnostic's structured `subject` (`name#line`) and nothing else (BR-V3), and `own-analysis` emitted `(code, line)` only. `Emit::push_at` now stamps the symbol's `origin` at exactly the sites where `analysis.py` passes `subject=sym.origin` — the state problems, OWN001 through the RID's minting symbol, release, overspan, the return path, the buffer escape codes — and `lifetimes.py`'s `source#line` on OWN014. The loan/permission codes stay subject-less, as in Python. `resource_kind`, messages and evidence on the `.own` path are deliberately not touched: nothing here asserts them. Pinned through the production `check_module` surface in `tests/subject.rs` (leak, param, use-after-release, double release, return path, origin inherited across a move, subject-less OWN006, the buffer origin with its column, the OWN014 source identity). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016vuEiNoEXaooMDRu6tXrZ6
…cp4) `own_bridge::check_facts` is `ownlang/ownir.py::check_facts` as spec/Bridge.md §5 writes it: lower, rebuild the Layer 2 document as the core AST (`ast.rs` — composing the byte-exact cp2 surface instead of lowering a second time), `own_analysis::check_module`, then map the ERROR-severity verdicts through the closed BR-V2 skip list and the verdict's `subject` to a fact handle (or refuse with the reference's map-or-raise text), anchor them per BR-V5, grade them per BR-V4/V6, append the DI and effect findings from the `own-analysis` finders (BR-P1/P2), the OWN050/051/052 side paths, dedup (BR-V7) and sort (BR-V8). The bridge prepares inputs and maps outputs; every verdict and every anchor is the analysis's own. `lower_full` now returns beside the Layer 2 document the Python-shaped handle records (`column` included — it is a dedup-key member), the OWN051 tuples with their owned-local gate, and the solve-failure reason for OWN052; `lower()` is unchanged and the Layer 2 goldens stay byte-exact. Declared, not hidden: a document declaring an obligation protocol is refused (the OBL analysis is not ported; an incomplete verdict list is worse than a loud no); a coordinate outside the core's `u32` line domain is refused, never clamped (a site or binding line whose only reader guards on `>= 1` folds a negative value to 0, which is exact on every path); the map-or-raise text interpolates the core message, which is still a title. The DAG widens by the four core crates the wiring needs, and a named test asserts no core crate depends on the bridge. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016vuEiNoEXaooMDRu6tXrZ6
…ro Python
`ownlang/verdicts.py` is the authoritative emitter of the normalized verdict
list (every `Finding` member in declaration order, the bridge's own
ordering, a refusal as `{"error": ...}`; `VERDICTS_VERSION = 1`), and
`tests/test_verdict_fixtures.py` its verify/--write harness over a frozen
ledger: the swept `ownir`/`lowered`/`summaries` corpora plus 19 synthetic
controls (EFF001 and DI001-005 through real blocks, the DI004 call-site and
DI005 store-site anchors, duplicate-site last-wins with the line-0 and
negative-line fallbacks, multi-file ordering, tiering and suppression, the
OWN051 gates, the pooled view anchor, the closed BR-V2 skip list, one record
pair per dedup-key member, and the declared-boundary controls). 77 goldens,
all through the tolerant door.
`own-bridge/tests/verdicts.rs` replays them through `check_facts` and
compares identity, anchor, kind and tiering — every member but `message`,
`related` and `flow`, which the goldens already carry for checkpoint 5:
69 cases (5 refusals, 127 findings), 0/0/0. The other 8 are an executable
exclusion ledger — two protocol documents, four coordinate-boundary
controls, two OD-1 door controls — each run and asserted to be refused
exactly as declared, so an exclusion cannot rot into a coverage hole.
Mutation campaign (--no-fail-fast, restore from copies): 30 mutations, 29
caught, one declared survivor (the ERROR-only filter — no warning-tier core
verdict exists to filter). Round one's three survivors became controls.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016vuEiNoEXaooMDRu6tXrZ6
Status surfaces changed together, per the status-drift rule: the P-022 checkpoint table (cp4 complete at the checkpoint-4 surface with the unmeasured set named; cp5 unblocked against the same goldens; the preferred queue now starts at cp5), the proposals index row, spec/Bridge.md §6 (Layer 3 built; OD-1 measured) and the behavior matrix's fixture summary, and the rust/README crate table that still claimed two crates of eight. The checkpoint note carries the census, the three declared boundaries and the two decisions they owe, the dedup-key argument, and the mutation campaign — including the round that found three coverage holes and one mutation that did not mutate. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016vuEiNoEXaooMDRu6tXrZ6
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Team Run ID: 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 |
The mutation campaign left one declared survivor: removing the ERROR-only filter changed nothing, because no facts producer reaches the one core pass that grades below ERROR, so the corpus can never fail the rule. A normative BR-V1 behavior that is permanently unprovable is the wrong resting place. The filter is now one pure predicate (`is_mapped`: BR-V1's severity rule and BR-V2's closed artifact list together), and a synthetic WARNING is driven through `map_core` itself — same diagnostic, same handle, ERROR maps to one finding, WARNING to none, a subject-less BR-V2 artifact is dropped before map-or-raise. The production path is unchanged; the mutation is now caught (30/30), at the unit level and saying so. The replay's log label names the surface it compares — "cp4 verdict surface (identity/anchor/kind/tiering)" — rather than "verdict parity", so the phrase a roadmap status is made of never appears ahead of cp5. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016vuEiNoEXaooMDRu6tXrZ6
…ection Per the cp4 review: the obligation-protocol analysis is a checkpoint of its own (row 4b) rather than a fourth job for cp5 — it does not block cp5, and #259's final acceptance needs it; the coordinate-domain divergence carries the owner's stated direction (a Python-first tightening, landed as its own contract change with parity evidence, never "the port is u32, so the reference is wrong"); the `subject` tail is checked rather than assumed (no Rust output surface serializes it); M19's closure is recorded; and cp5 gets its comparison matrix over the same frozen goldens — every "deferred" row becomes "prove", every "proven" row a regression guard, and no golden is regenerated beside the implementation. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016vuEiNoEXaooMDRu6tXrZ6
The checkpoint-4 row of the P-022 status table and the P-022 line of the proposals index still carried the round-1 census (76 goldens, 18 synthetic controls, 68 cases replayed, 111 findings) after round 2 added the M01/M04/M06 controls; the checkpoint note and the PR packet already read 77 / 19 / 69 / 127. Derived again from the tree (manifest.json + the *.verdicts.json goldens): 77 goldens = 58 swept + 19 synthetic, 8 excluded, 69 replayed, 5 refusals, 127 findings. The cp4 row's coordinate-boundary sentence now names the same direction the cp5 row and the checkpoint note record (Python-first tightening; the contract decision itself still owed) instead of an open "Python-first bound or Rust widening". Status surfaces change together; the census numbers are still hand-copied across four documents, and an evidence-derived projection with a --check is the follow-up on its own branch. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016vuEiNoEXaooMDRu6tXrZ6
PhysShell
pushed a commit
that referenced
this pull request
Sep 5, 2026
The campaign's provenance gate accepts a result only while the commit it was taken on is an ancestor of HEAD. Rebasing this branch onto main after #336 merged left the recorded run describing a history the tree no longer contains, and `tests/test_checkpoint_status.py` said so — the signal working, not a breakage. Re-run on a clean tree at a529193 (this commit's parent), definition unchanged (sha256 6f858c6bf6a2, no edit to docs/evidence/p022-cp4-mutations.json). Outcome as the runner printed it: p022-cp4 @ a529193: 30/30 caught, 0 survived, 0 compile-error, 0 invalid, 0 runner-error; expected catchers missed: none M00, the honesty control, survived — the unmutated tree passes, so the run measured something. Against the previous result only provenance and per-mutation elapsed times differ: no outcome and no catcher moved. docs/generated/p022-cp4-mutations.md is regenerated by scripts/render_checkpoint_status.py, never edited by hand; the census fragment is unchanged, since it does not depend on the run. ruff, mypy and tests/run_tests.py are green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0167aQj9xTAF5r7K1nTCtDQ5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Что и зачем
#259 checkpoint 4 — analysis wiring.
own_bridge::check_factsпрогоняет реальные OwnIR-факты через портированные анализы (check_module: ownership/lifetime/buffer policy; DI и effect finders изown-analysis) и маппит вердикты обратно на C#-анкеры по spec/Bridge.md §5. Сравнение — на поверхности cp4 (identity, anchor, kind, tiering), голдены несут всё, cp5 дожимает сравнение без регенерации.Одно изменение в ядре:
own-analysisтеперь проставляетsubject(name#line) ровно там, где это делаетanalysis.py/lifetimes.py— без него BR-V3 map-or-raise невозможен.Тип изменения
Как проверено
python tests/run_tests.py(в т.ч. новыйtests/test_verdict_fixtures.py: 77 кейсов),ruff check .,mypy(31 файл)cargo fmt --check+cargo clippy --workspace --all-targets(0) +cargo test --workspace --no-fail-fast(36 таргетов, 0 падений)Python не менялся в производственном коде: он oracle. Добавлен только наблюдатель
ownlang/verdicts.py(какlowered.py) и харнесс.Связанные issue
Refs #250, #259. Ничего не закрывает — cp4 из пяти checkpoint'ов. Строки cp4/cp5 в #250/#259 — отразить при merge (как делалось для cp1); плюс новая строка 4b — protocol analysis (см. ниже).
Чеклист
docs/notes/p022-bridge-verdict-checkpoint4.md)Пакет #250 / #259
над измеренным множеством: 69 кейсов replay (5 refusal'ов, 127 вердиктов) из 77 голденов — 127 вердиктов × cp4-проекция сравнения, не «verdict parity complete».
Checkpoint packet (#259)
Что осталось за измеренным множеством — и почему это записано, а не спрятано
obligations.pyне портирован. Мост отказывает документу с протоколами вместо неполного списка вердиктов:protocol_isloaded_cleanиначе «совпал бы» вакуумно. В checkpoint-листе P-022 step 6b: implement Rust own-bridge with layered OwnIR parity #259 протокольного анализа нет вовсе, в финальном acceptance — есть. По ревью: отдельный явный checkpoint — строка 4b в таблице P-022, не в cp5 (cp5 — messages/evidence/rendering; целое analysis family сверху сделало бы последний checkpoint мешком). 4b не блокирует cp5; final acceptance P-022 step 6b: implement Rust own-bridge with layered OwnIR parity #259 — блокирует.u32; строгая дверь принимает любой signed-64 (§4.2), толерантная — всё, что проходит_as_int. Координата вне0..=u32::MAX→ отказ, никогда не clamp. Референс такие документы анализирует (OWN001 вB.cs:-1, DI001 вreg.cs:-5). Это паттерн cp1: семейство расхождения вне измеренного множества, записанное для решения. Направление по ревью: Python-first tightening — нормативно определить домен координат (отрицательная строка исходника бессмысленна), научить референс отказывать так же, затем снять exclusion; отдельным сознательным изменением контракта с parity evidence, а не «Rust уже u32, значит Python неправ».OwnIr, поэтому skip-not-coerce (BR-D2) и игнор неизвестного lifetime до моста не доходят. Порт правил есть и закреплён на уровне сырого документа.Одна граница сравнения: текст map-or-raise интерполирует сообщение диагностики ядра, а в ядре это пока title — три
hoist_neg_*refusal'а сравниваются до членаmessage=, обеими сторонами одной функцией.Мутации
Раунд 1 (24): три настоящих выживших — M01 (в корпусе не было ни одного OWN033/034/035/041: «закрытый» skip-list без контроля на член), M04/M06 (
columnиeventв ключе дедупа были избыточны на том корпусе — их прикрывалиhandlerи имена) — и одна мутация, которая не мутировала (M10). Все четыре стали контролями/исправлены до раунда 2. Плюс баг харнесса: атрибуция catcher'ов терялась из-за раздельного захвата stdout/stderr.Раунд 2: 30 мутаций, 29 пойманы, один объявленный выживший — M19 (фильтр ERROR-only: в ядре нет warning-tier вердиктов, корпус не может его убить).
Раунд 3 (по ревью): M19 закрыт — 30/30. BR-V1 вынесен в чистый предикат
is_mapped(BR-V1 + BR-V2 в одном месте), синтетический WARNING прогнан через самmap_core: тот же диагностик, тот же handle — ERROR → один finding, WARNING → ни одного, subject-less артефакт BR-V2 отбрасывается до map-or-raise. Production path не изменён; мутация ловится на unit-уровне, и тест это про себя говорит. Таблица — вdocs/notes/p022-bridge-verdict-checkpoint4.md, там же матрица cp4→cp5 (proven→regression / deferred→prove) над теми же замороженными голденами — никакой регенерации рядом с реализацией cp5.Наблюдения по дереву (не правил, только фиксирую)
rust/README.mdутверждал «two of eight planned crates exist» при семи существующих — таблица исправлена в этом PR (статусные поверхности меняются вместе).cargo docдаёт два предсуществующих warning'а вown-cfg(ссылки на приватныйresolve) — не трогал.--checkв существующих Python-воротах идёт отдельной веткой после merge.🤖 Generated with Claude Code
https://claude.ai/code/session_016vuEiNoEXaooMDRu6tXrZ6