fix(extractor): --fix-candidates no longer drops orphaned_awaitables (OWN053) - #387
Merged
Merged
Conversation
…(OWN053)
`--fix-candidates` is additive: it adds fix metadata and changes nothing else.
It removed a section. The same input, the same build:
--flow-locals orphaned_awaitables: 5 sites
--flow-locals --fix-candidates orphaned_awaitables: absent
so every OWN053 advisory disappeared from a run that asked for fix candidates
(corpus/ownership-lab/h29/fx/Orphan.cs: 5 advisories without the flag, 0 with
it, on both engines).
Root cause: the extractor built its envelope in three places, picked by a nested
conditional — `--fix-candidates` ? A : orphans ? B : C. The `--fix-candidates`
envelope was written before `orphaned_awaitables` existed and was never given
it. Each additive section had to be remembered in every envelope, and one was
not.
The fix is the mechanism, not the missing line: ONE envelope, built once, in the
key order the facts have always had; a section that is not always there is one
conditional line. There is no second envelope for the next section to be
forgotten in. Output without the flag is unchanged byte for byte, and so is
every flag-on output that had nothing to lose: 256 outputs compared with main's
build (every C# input CI scans and the two OWN053 fixtures, with and without
--flow-locals, flag on and off) — 254 byte-identical, 2 repaired (they gain
exactly the section they used to drop), 0 other.
Why the S0 control did not see it. The invariant it holds was already the right
one — flag-on minus the S0 fields == flag-off, the whole document. The document
was the problem: FixCandidatesSample.cs has subscriptions and no other section,
and CI scans it without --flow-locals, so nothing the flag could drop was ever
there. Now:
* tests/fixtures/fix_candidates/AdditiveSections.cs makes every top-level
section the extractor can write non-empty under --flow-locals (both OWN053
families included, with no reference directory);
* `check_fix_candidates_facts.py --additive-sections` holds the equality on
it, reads the section list off the extractor's own envelope
(`facts["<key>"] = ...`) and refuses a pair that leaves a section out — so a
section added to the extractor is red until the fixture exercises it — and
requires the reference to give the same findings for both documents;
* the CI step "S0 fix-candidates — extractor metadata (Part A)" runs it.
Negative controls: on main's extractor the new check fails (`orphaned_awaitables`
is in the flag-off facts and MISSING from the flag-on facts; 3 findings with the
flag, 5 without); with the section dropped under the flag in this tree it fails
the same way and passes again once restored; an envelope key the fixture does
not exercise fails it by name; and the old sample, given to the new mode, is
refused as not exercising `services`, `functions` and `orphaned_awaitables`.
No OwnIR vocabulary or version change (OWNIR_VERSION 1, LOWERED_VERSION 2), no
change to the OWN053 rule, to the state-protocol lowering or to the measurement
instrument. spec/OwnIR.md §2 states the producer-side rule.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.
Что и зачем
--fix-candidatesаддитивен: он добавляет fix-метаданные и ничего больше не меняет. На деле он удалял секцию: с флагом из фактов пропадалorphaned_awaitables, и вместе с ним каждый advisory OWN053.Причина: экстрактор строил конверт фактов в трёх местах, выбирая по вложенному условию (
--fix-candidates? A : orphans ? B : C). Конверт под--fix-candidatesнаписан до появленияorphaned_awaitablesи его так и не получил.Исправлен механизм, а не пропущенная строка: конверт один, строится один раз, в прежнем порядке ключей; необязательная секция — одна условная строка. Второго конверта, в котором можно забыть следующую секцию, больше нет.
Контроль S0 этого не видел не потому, что инвариант был слабым (он уже был «flag-on минус S0-поля == flag-off, весь документ»), а потому, что слабым был документ: в
FixCandidatesSample.csесть подписки и больше ни одной секции, и CI сканирует его без--flow-locals. Теперь:tests/fixtures/fix_candidates/AdditiveSections.csделает непустой каждую top-level секцию, которую экстрактор умеет писать (оба семейства OWN053 — без внешних ссылок);check_fix_candidates_facts.py --additive-sectionsдержит на нём равенство, читает список секций из самого конверта экстрактора (facts["<key>"] = ...) и отклоняет пару, в которой секция не задействована — новая секция в экстракторе будет красной, пока фикстура её не покрывает; дополнительно требует, чтобы референс давал одинаковые находки для обоих документов;Словарь и версия OwnIR не меняются (
OWNIR_VERSION1,LOWERED_VERSION2). Правило OWN053, lowering state-протоколов, T0 иscripts/perf_baseline.pyне тронуты. Вspec/OwnIR.md§2 добавлено правило аддитивности на стороне производителя.Тип изменения
Как проверено
python tests/run_tests.pyruff check .иmypypython scripts/<...>.py --selftest)На закоммиченной ветке, свежий Linux-клон (
dotnet8.0.422 и 9.0.315,ruff 0.15.8,mypy 1.19.1), и на Windows.main(каждый C#-вход, который сканирует CI, и две фикстуры OWN053; с--flow-localsи без; флаг включён и выключен) — 254 побайтово идентичны, 2 исправлены (получили ровно ту секцию, которую теряли), 0 прочих.main— 1, ключомorphaned_awaitables).orphaned_awaitablesодинаков с флагом и без (2 сайта на новой фикстуре, 5 наOrphan.cs); Python и Rust печатают одинаковый вывод для обоих документов и совпадают между собой (код выхода, stdout, stderr).C# leak extractor (Roslyn) -> OwnIR -> core, шаги как написаны вci.yml: все exit 0 (включая S0 A/B, S1, S2 rewriter, patch bundle, self-gate, stale preimage).python tests/run_tests.py— exit 0; 15 реестров эталонов в синхроне;cargo fmt --check,cargo clippy --all-targets— exit 0;cargo test --no-fail-fast— 288/0; паритет CLI наtypestate_*— 37/37;protocol_gate.py --rust— 0 провалов.1a26aa63fd5f),mergegate_ci.pyна симулированном merge — allowed.Отрицательные контроли:
mainновая проверка красная: «orphaned_awaitablesis in the flag-off facts and MISSING from the flag-on facts», 3 находки с флагом против 5 без;services,functions,orphaned_awaitables.Не проверено: остальные job CI локально не гонялись (job
C# leak extractorсуществует только на ubuntu; на Windows экстрактор и новая проверка запускались вручную).Связанные issue
Отдельного issue нет. Дефект замечен при сведении #385 с #386.
Чеклист
feat:,fix:,docs:…)🤖 Generated with Claude Code