docs(P-012): record loop-invariant-call mining packet as a research seed - #329
Conversation
…ch seed The merged invariant-cost note measured a 1.0x-1092x spread over four hand-picked shapes and closed by saying the honest first step is mining, not a rule. This records what that scan should collect, so the measurement design does not evaporate — it survives even if a future checker turns out to be a completely different thing, because it describes an observation, not an architecture. The packet keeps three independent label axes -- input stability, call repeatability, placement safety -- with the flat bucket derived from them and never recorded instead of them. Collapsing early is what would waste the scan: inputs-proven/repeatability-proven/placement-unknown and inputs-proven/repeatability-unknown/placement-proven both flatten to UNKNOWN, and the product question of which axis is the bottleneck stops being answerable from the data. Two fields exist specifically because the presence of a control transfer is not discriminating on its own: `var x = F(); if (c) break;` and `if (c) break; var x = F();` record identical control-transfer syntax and have entirely different placement stories, so call_control_context and call_reachability_per_iteration keep a difference the scan is not required to resolve but must not lose. Two guards are stated in the text because a column list invites being turned into a predicate: no single field, receiver immutability included, is evidence that hoisting preserves semantics, and the pass must preserve unknown rather than infer safety from the absence of a recognised hazard. Pure addition inside the stage-2 material, marked "Research seed only; does not schedule or specify a checker." Status, dependencies, non-goals, the ROADMAP and the proposal index are untouched; no issue is filed; no attribute, receiver whitelist, lattice or fix-arm design is introduced -- those are conclusions from numbers that do not exist yet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PbyzyVi7fibuSKLweuXgea
📝 WalkthroughWalkthroughThe proposal adds a stage-2 syntactic scan for calls inside loops. It defines candidate metadata, independent stability, repeatability, and placement labels, derived classification buckets, and safeguards against treating observations as a loop-hoisting specification. ChangesLoop-call mining
Estimated code review effort: 1 (Trivial) | ~3 minutes 🚥 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 |
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-012-bug-corpus-mining.md`:
- Line 221: Remove the standalone blank blockquote line in the quoted section of
P-012, keeping the surrounding guards as one continuous blockquote or list so
markdownlint MD028 passes.
- Around line 174-180: Update the observation schema table so its field count
matches the documented 15-field objective: explicitly define capture_count and
capture_types as one composite field, or revise the declared total to 16 if they
remain separate fields. Ensure the schema description and listed identifiers
consistently communicate the chosen representation.
- Around line 206-208: Define explicit deterministic precedence rules for
deriving the bucket from the three source labels, including conflicts among
multiple reasoning-required states and any disproven state combined with a
reasoning-required state. Update the derived-bucket specification near
CHEAP_PROVABLE so every source-label tuple maps to exactly one bucket without
recording the derived value as a replacement for the source labels.
- Line 189: Clarify the call_control_context schema in the proposal by making it
a set of applicable context values, so overlapping classifications such as
conditional, short-circuit-operand, and nested-lambda-or-local-function are
retained. If it must remain single-valued, define explicit precedence that
preserves all relevant facts.
🪄 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: 230a5ec9-bebf-49d6-9650-d33a57da1a54
📒 Files selected for processing (1)
docs/proposals/P-012-bug-corpus-mining.md
Four review findings, all checked against the text and all valid; none changes what the packet says, each removes a way to read it two ways. capture_count and capture_types shared one table row, so the table had one fewer row than it had identifiers and a miner could reasonably emit either shape. They are now one row each: 19 rows, 19 identifiers, no composite left implicit. call_control_context was written as a single value while its values genuinely overlap -- `c && F()` is both conditional and short-circuit-operand, and a call in a lambda under an if is also nested-lambda-or-local-function. Forcing one value would discard exactly the distinctions the field exists to keep, so it is now explicitly a set, with the overlap spelled out. The derived bucket named five outcomes without saying how a label tuple maps to one, and two tuples matched two buckets: needs-effect-reasoning together with needs-cfg-reasoning, and disproven together with any reasoning-required axis. It now derives by an ordered, total rule. A settled negative takes precedence over an open axis; the order of the two reasoning rungs is a reporting convention rather than a claim about which reasoning dominates, and it costs nothing because the three axis labels are always kept, so a candidate needing both is recoverable from the data whichever bucket it rolls into. The two guards were two blockquotes separated by a blank line, which trips markdownlint MD028; they are one blockquote with two bullets now. Verified: MD028 count is 0. The MD013 line-length reports that remain on the new table rows match this document's existing tables, which already run past 100 characters, so they are its convention and are left alone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PbyzyVi7fibuSKLweuXgea
Что и зачем
Добавляет в P-012 одну секцию внутри материала stage 2 — схему майнинга для кандидатов «инвариантный вызов внутри цикла». Это описание измерения, а не архитектура, и три вещи стоит подчеркнуть прямо:
Research seed only; does not schedule or specify a checker.Никакого дизайна правила, атрибутов, вайтлистов приёмников, решётки или fix-арма внутрь не протащено — всё это выводы из чисел, которых пока нет.UNKNOWN, и продуктовый вопрос перестанет быть отвечаемым из данных.docs/notes/invariant-cost-static-vs-runtime.md(PR docs(notes): measured .NET optimization limits and GC/LOH observability #327) намерил разброс 1.0×–1092× на четырёх выбранных вручную формах и закончился тем, что честный первый шаг — майнинг, а не правило. Секция фиксирует этот шаг, и только его.Содержательно: 16 наблюдаемых полей; три оси меток (
input_stability/call_repeatability/placement_safety); производный бакет, который никогда не записывается вместо трёх исходных и выводится упорядоченным тотальным правилом.Два поля —
call_control_contextиcall_reachability_per_iteration— существуют потому, что наличие control transfer само по себе ничего не различает:var x = F(); if (c) break;иif (c) break; var x = F();дают одинаковый флаг и совершенно разную placement-историю. Скан не обязан эту разницу разрешать, но не должен её потерять.call_control_context— множество, а не одно значение, потому что его значения реально пересекаются (c && F()одновременноconditionalиshort-circuit-operand).Два guard-абзаца закрывают дверь перед тем, чтобы список колонок через полгода превратился в предикат вида
if immutable_receiver and not contains_throw: report(): ни одно поле по отдельности, включая иммутабельность приёмника, не является доказательством того, что вынос сохраняет семантику, и проход обязан сохранятьunknown, а не выводить безопасность из отсутствия распознанной опасности.Тип изменения
Как проверено
Поведения нет — дифф целиком документационный, вставка в один файл, поэтому тесты репозитория не применимы и не гонялись.
Проверено то, что относится к самому изменению:
+78 / -0, один файл, ноль удалённых строк.Status, зависимости, non-goals,docs/ROADMAP.mdи индекс предложений не тронуты; секция вставлена после материала stage 2 / detectability, перед## Open questions.OwnPure,ImmutableArray,lattice,autofix,whitelist,LLVM,speculatable.MD028= 0. ОстающиесяMD013/line-lengthна строках таблиц соответствуют уже существующим таблицам этого файла (они идут до 108 символов), то есть это его конвенция, а не привнесённое здесь.Связанные issue
Нет, и намеренно. Секция ничего не планирует в работу — по дисциплине
research-landscape-2026.mdзаметки и предложения фиксируют, планирует ROADMAP. Заводить issue под research seed значило бы создать вторую поверхность статуса для работы, которая сознательно не в очереди.Чеклист
ROADMAP.mdи индекс предложений намеренно не трогались, чтобы research seed не выглядел запланированной работойfeat:,fix:,docs:…)Про упоминание промышленного прецедента: в тексте одна фраза о том, что компиляторы держат repeatability и placement legality разными атрибутами — как подтверждение декомпозиции, с явной оговоркой «не готовое соответствие для C#». Имён атрибутов и таблицы соответствия нет намеренно.
Имена значений осей записаны прозой через дефис (
cheap-proven), а не как идентификаторы: это предложение о том, что измерять, а не wire-схема будущего сканера — машинные имена должен определить его собственный контракт.