fix(bridge+extractor): #294 tolerant-door contract + #305 teardown-predicate soundness (audit → red → green) - #306
Conversation
… line coercion The bridge's tolerant door (direct `check_facts`/`to_module`/`to_own`, `load()` bypassed) diverged from the strict door in two accidental ways tracked by #294: - OD-2: a present-but-unknown `resource` kind was silently routed as `subscription` (`_RESOURCES.get(rkind, default)`), contradicting IR4 and the unknown-flow-op guard that already fail loud on both doors. New `_route_resource` raises the same `OwnIRError` text as `load()`; an absent `resource` still defaults to `subscription`. - OD-3: finding construction used strict `int(sub.get("line", 0))` on the token/capture anchor paths but `_as_int` elsewhere, so a non-int `line` on the tolerant door crashed one path and degraded the other. All paths now use `_as_int` (degrade to 0); `load()` still validates `line` on the strict door. Rust `own-bridge` already failed loud on an unknown kind, so OD-2 landing lets `tolerant_unknown_kind` become a shared `rust_replay` case: the Rust error text is aligned to Python's authoritative message and the fixture's `Rejected` golden now pins both sides byte-for-byte. No `rust_replay: false` snapshots remain. Tests: red→green in tests/test_ownir.py (tolerant-door unknown-kind raise + non-int line degradation, with absent-kind and valid-int controls); Rust replay/round-trip guards updated to assert the empty Python-only set. Docs: spec/Bridge.md §9 OD-2/OD-3 marked resolved, BridgeBehaviorMatrix + own-lowered doc refreshed. OD-1 (whether Rust exposes the tolerant door) stays for #259. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015de4MezSeUnZBoWq1fFU5M
…cates Adversarial code reading of the landed teardown-context/guard predicates (enumerate the implicit axioms, attack each, verify against Program.cs). Full attack matrix — six confirmed hole families, eleven survived attacks, doctrine assessment, bounded fix directions — in docs/notes/teardown-predicate-adversarial-audit.md. Two P1 holes (silently swallowed leaks, the #238 doctrine violation) are pinned as red corpus fixtures; the CI corpus benchmark is the empirical arbiter (before.cs MISSED expected — recall floor is an absolute count; after.cs reuses proven-silent shapes): - subscription-teardown-early-return-guard: the SectorTS flag guard rewritten from `if (!flag) { -= }` to `if (flag) return; -=` — semantically identical, invisible to IsParamGuardedRelease (ancestors-only walk), while the symbol closure credits the helper regardless of argument values. The C# twin of the bridge's D7/INF-S3 defect. - subscription-disposing-else-branch-release: a `-=` in the ELSE of the canonical `if (disposing)` is credited (the exception classifies the parameter's use in the condition, never the branch holding the site) — yet it runs only on the finalizer path the extractor's own doctrine declares unreachable while the subscription is live. Both .own reductions are caught by the branch-sensitive core (wpf corpus 26/26) — extractor gaps, not core gaps. cfg/diag parity fixtures regenerated (additive); Rust parity green on the grown corpus. WPF002 Stop() shares the predicate, so the holes apply verbatim — timer twins land with the fix. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015de4MezSeUnZBoWq1fFU5M
… canonical disposing is branch-aware The green half of the adversarial audit (previous commit was the red): - IsParamGuardedByEarlyReturn (attack A): a `return` lexically preceding the release site inside the enclosing callable, guarded by a condition that references a parameter, demotes the release — the caller-controlled skip spelled as a sibling instead of an ancestor. The one exemption is the canonical INVERTED disposing exit (`if (!disposing) return;`), which guarantees the site runs on every Dispose() call. Nested lambdas / local functions are not scanned (their returns do not exit the frame); unguarded and field/local-guarded returns keep today's crediting. - SiteOnNegativeBranch (attack B): the canonical positive-`disposing` exemption now covers only a site on the POSITIVE side of the guard; a `-=` or `.Stop()` in the ELSE of `if (disposing)` (or the when-false arm of a ternary) demotes like any parameter guard — that branch is the finalizer path the predicate's own doctrine declares unreachable while the subscription/timer pins the owner. Both rules are folded into the shared IsParamGuardedRelease, so `-=` and timer `.Stop()` inherit them together (one predicate, one context model). IsCanonicalDisposingGuardUse is split into IsCanonicalDisposingParam + IsNegatedGuardUse so the enclosing-guard and early-return rules can invert the canonical polarity without duplicating the use-position analysis. Corpus: the two red subscription fixtures flip to caught; timer twins added (timer-stop-early-return-guard, timer-stop-disposing-else-branch). wpf corpus 28/28 on the .own side; cfg/diag parity regenerated (additive); Rust parity green. The canonical THEN-branch shapes (WinFormsDisposalSample, subscription-param-guarded-unregister after.cs family) keep their crediting — no sample carries the demoted shapes, so extractor goldens stay put; the CI corpus benchmark is the empirical arbiter for the C# side. Worst case of both rules is a kept warning, never a swallowed leak (#238). The general argument-value-aware answer remains #304; this is the bounded pre-cutover floor. Closes #305. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015de4MezSeUnZBoWq1fFU5M
|
Warning Review limit reached
Next review available in: 41 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (34)
✨ 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 |
…inus the dominator tree The runtime-witness half of the flagship demo, resurrected as the minimal core rather than a rebase of the geological layer: Heap.cs (mark-from-roots census, sampled ranked retainers with the field named at every hop, stack/finalizer vs real retention) + Program.cs (census / roots, --pid|--dump, --type, --sample/--max-hops limits, --out runtime.json) + the csproj. Deliberately NOT taken: Dominators.cs (444 lines of separate proof surface the hero demo does not need), the pre-#278/#305/#306 corpus slice, and the old runtime-contract docs. Adapted to the A3 contract: - verdict vocabulary — RETAINED (a root path was established, exit 1) | OBSERVED_ONLY (instances exist, no path established — never called a proven leak, exit 0) | ABSENT; usage documents it; - the JSON artifact carries verdict + collector identity (mode attach/dump, target, runtime, OS — no timestamps: identical heaps must yield identical artifacts); - a failed read stays exit 2, distinct from clean(0)/retained(1); - net8.0 cross-platform (ClrMD 3.x live-attach works on Linux and Windows; legacy net472 heaps are dump-only from here — the PR #280 live-attach build can be resurrected the day a live legacy target matters). Gate A builds the project and smokes the honest-failure surface on both platforms: bare usage exits 2 with the verdict vocabulary documented, and a failed attach exits 2 — never a clean scan. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015de4MezSeUnZBoWq1fFU5M
Что и зачем
Две закрытые soundness-линии одной ветки. #294 (мост, Python-first): толерантная дверь (
check_factsв обходload()) молча маршрутизировала неизвестныйresourcekind какsubscription(OD-2) и падала на не-intlineна части путей (OD-3) — теперь IR4-everywhere fail-loud (_route_resource, текст ошибки байт-в-байт сload()) и_as_intна всех путях построения находок; фикстураtolerant_unknown_kindстала общимrust_replay-кейсом (Python-only снимков не осталось). #305 (экстрактор): адверсариальный аудит teardown-предикатов #293/#302 (матрица аксиом AX1–AX6, 6 семейств дыр, 11 выдержанных атак —docs/notes/teardown-predicate-adversarial-audit.md) нашёл две P1-дыры: параметрический guard, записанный раннимreturn, кредитовался (близнец D7/INF-S3 моста), и-=/.Stop()в else-ветке каноническогоif (disposing)кредитовался вопреки собственной финализаторной доктрине. Фикс:IsParamGuardedByEarlyReturn+ branch-aware каноническое исключение (SiteOnNegativeBranch), свёрнуты в общийIsParamGuardedRelease— подписки и таймеры наследуют вместе; 4 новых корпусных кейса (2 subscription + 2 timer twins) red→caught.Тип изменения
Как проверено
python tests/run_tests.py(exit 0; wpf-корпус 28/28)ruff check .иmypypython scripts/benchmark.py --selftest; cfg/diag parity регенерированы (аддитивно),cargo testвесь workspace зелёный (Rust-паритет на выросшем корпусе)f6bf31c(run #1594, push): 18/18 jobs success; corpus benchmark — все 4 новыхbefore.cs→caught: OWN001, всеafter.cs→clean, итог 61/62 caught · 62/62 fixes clean · 0 FP (единственный missviewmodel-escapes-to-app— пре-существующий recall-floor элемент; фикс только добавляет условия демоции и по построению не может создать новый miss)f6bf31cСвязанные issue
Closes #294. Closes #305. Refs #293, #302, #304 (общий post-cutover ответ), P-036.
Чеклист
test_ownir.py; 4 red→green корпусных кейса; guard-и replay-тестов Rust обновлены)spec/Bridge.md§9 OD-2/OD-3 resolved,BridgeBehaviorMatrix.md, аудиторская нота, notes.md кейсов)feat:,fix:,docs:…)🤖 Generated with Claude Code
https://claude.ai/code/session_015de4MezSeUnZBoWq1fFU5M
Generated by Claude Code