Skip to content

P-037 A2.1: guarded-fact sidecar (corrected), richer facts with zero MOS/verdict movement - #363

Merged
PhysShell merged 3 commits into
mainfrom
claude/p037-a2.1-relevance-fix
Sep 19, 2026
Merged

PhysShell merged 3 commits into
mainfrom
claude/p037-a2.1-relevance-fix

Conversation

@PhysShell

Copy link
Copy Markdown
Owner

Summary

  • dce26ed: the A2.1 treatment — the Roslyn extractor emits a validated
    guarded-fact sidecar; the legacy body is unchanged and both consumption
    doors ignore it (demonstrably inert).
  • 5a0de07: fix, found by independent review of the original evidence
    (was PR P-037 A2.1: guarded-fact sidecar, richer facts with zero MOS/verdict movement #362, closed/superseded). BuildGuardedFacts coupled call
    RELEVANCE to argument REPRESENTABILITY: a handle passed via params,
    ref/out, or as an unstable owned parameter had its relevance bit
    hardcoded to false whenever the slot had to be encoded opaque, so
    the entire call record silently vanished from guarded_facts instead
    of surviving with an opaque slot — contradicting spec/OwnIR.md §5.2
    ("an unstable parameter argument becomes opaque, not absent"). Adds
    three regression shapes (corpus/p037-shapes/sidecar-relevance-*),
    each verified against the pre-fix code (reproduced the bug: the call
    record was entirely absent) and the fix (call record now present with
    an opaque slot).
  • cd7e020: evidence-only commit recording the M1 after-run comparison
    against the T-baseline (R, already in main via P-037 A2.0: evidence harness hardening + terminal-green baseline (T, R) #361), taken at the
    corrected commit 5a0de07.

Test plan (all done pre-merge on this VPS)

  • Full 14-shape fact-census (11 pre-existing + 3 new): all pass
  • Sidecar inertness control, both engines, all shapes + repo dogfood
    samples: emitted == stripped == contradictory
  • Dedicated sidecar vocabulary test: all pass
  • ruff check . / mypy --strict: clean
  • dotnet build (Release): succeeds, only a pre-existing unrelated
    warning
  • MOS repo: UNCHANGED, facts_moved=1, zero MOS movement on both engines
  • MOS corpus: UNCHANGED, facts_moved=36, zero MOS movement on both engines
  • verdict/python: UNCHANGED, no verdict moved (137 files)
  • verdict/rust: UNCHANGED, no verdict moved (137 files); own-cli
    qualified sha == executed sha (unchanged from T — fix is C#-only)
  • Execution profile byte-identical to the T-baseline capture

facts_moved counts (1, 36) are unchanged from the pre-fix run: neither
population happens to contain the params/ref/unstable-owned-param edge
cases the fix addresses, so the correctness fix is visible on the three
new regression shapes, not on this measured population.

Known pre-existing observation (not fixed here, tracked in the evidence
manifest): scope_cache_sites[].file carries an absolute path rather
than repo-relative — present since T, out of scope for this PR.

Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_011wKL9pEApQTK4dE3eXv8g8

🤖 Generated with Claude Code

claude and others added 3 commits September 19, 2026 03:59
…validated and inert

Treatment-only step of the A2 staging (docs/notes/p037-formal-kernel.md §10.3):
the extractor now writes `functions[].guarded_facts`, the raw record of what
flows into each relevant call and what each eligible `if` tests, keyed by
declared parameter ordinal. Neither engine reads it; the legacy `body` stays
authoritative; no path under the A2.0 instrument freeze is touched, so the
after-takes on M1 compare directly against the R baselines with
population_commit = T.

Producer (frontend/roslyn/OwnSharp.Extractor/Program.cs):
- calls[]: one per call a disposable candidate local or an owned parameter
  flows into (argument or reduced-extension receiver); site {line, column} of
  the invocation plus statement_line as the join identity with the legacy ops;
  form statement|initializer|expression; callee/sig or null when unresolved;
  first_party; args bound by declared ordinal (named args resolved, extension
  receiver = 0, params/ref/out = opaque, unresolved callee = source position).
- arg kinds (raw facts only, §10.2): var{name}, param{source_param[,negated]},
  bool_const{value}, null_literal, object_creation, call_result{callee,sig},
  opaque. No const-pos/id/neg and no fresh_owned are ever written.
- guards[]: one per eligible `if` on a stable by-value parameter — truth on a
  bool, is_null/not_null on a reference or Nullable<T>; G-V4 stability is
  whole-body write exposure (assignment, ++/--, ref/out, ref alias, address).
  Ineligible guards get no entry: absence is the fail-closed signal.
- Self-check: a sidecar that violates its own vocabulary makes the run refuse
  to write facts at all (exit 2), so a producer defect never reaches a door.

Contract (spec/ownir.schema.json, spec/OwnIR.md §5.2, §4.2):
- closed $defs guardedFacts/guardedCall/guardedArg/guardedGuard/sourceSite;
  the sidecar is optional and additive, OWNIR_VERSION stays 0.
- sourceSite is deliberately UNBOUND (type only): the doors do not read the
  sidecar in A2.1, and §4.2 forbids a schema narrower than the door on a
  coordinate the door does not check. tests/test_ownir_defensive_limits.py
  classifies it as the one UNBOUND entry; the instrument step that registers
  the sidecar at the doors moves it to BOUND.

Controls:
- corpus/p037-shapes: all 11 shapes re-recorded with the sidecar (body ops,
  params and both engines' verdicts byte-identical to before), status
  pending_a2 -> anchored, and a2_expect contract checks per shape that fail by
  name (scripts/p037_fact_shapes.py check).
- tests/test_p037_sidecar.py pins the schema enums against the producer's
  vocabularies and the raw-fact boundary.
- scripts/p037_sidecar_inertness.py (CI dogfood step): for every census shape
  and the samples tree, the emitted, stripped and contradictory sidecars yield
  identical lowered/summaries/verdicts layers on both engines, python == rust,
  and the control is non-vacuous (39 sidecars over 12 documents locally).

Known A2.1 boundary, owed by A2.2 (honest emission for every relevant call):
object-creation arguments (`new X(handle, leaveOpen: true)`) are not yet a
call fact; A2.1 covers invocation expressions.

Refs #304

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QfeUtLRm6KFRWcTGQZxQQb
BuildGuardedFacts coupled two independent questions: whether a call is
RELEVANT (a tracked/owned handle syntactically flows into it) and how
precisely an argument slot can be REPRESENTED (param/var/opaque). Both
the params/ref-or-out branch and the unstable-owned-parameter branch in
ArgFact hardcoded the handle bit to `false` whenever the slot had to be
opaque, so a handle passed through a `params` array, by `ref`/`out`, or
as an unstable owned parameter never set `relevant`, and the entire call
record silently vanished from guarded_facts instead of surviving with an
opaque slot for that argument — contradicting spec/OwnIR.md §5.2, which
says an unrepresentable argument becomes opaque, not absent.

Fix: evaluate the argument's handle-ness once, independent of how the
slot ends up encoded, and thread that value through both branches.

Adds three regression shapes to corpus/p037-shapes (a params sink, a
ref sink, and an unstable owned parameter flowing to a normal argument),
each asserting the call record exists with an opaque slot, not merely
that the slot's kind is opaque. Verified against the pre-fix code first
(all three reproduced the bug: guarded_facts entirely absent for the
method), then against the fix (all three now carry the call record).
Full existing 11-shape census, the dedicated sidecar vocabulary test,
and the sidecar inertness control (both engines) all still pass
unchanged: the sidecar remains semantically inert.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011wKL9pEApQTK4dE3eXv8g8
Supersedes 2cbd6c6 (PR #362), whose source_commit (dce26ed) had the
call-relevance bug fixed by the parent commit. Four after-takes (MOS
repo, MOS corpus, verdict/python, verdict/rust) at A2.1 treatment commit
5a0de07, population held at
terminal-green T (4a8e658), in the same
preserved M1 measurement environment used for the T baseline. All four
compare UNCHANGED against the R-recorded baseline: MOS repo
facts_moved=1, MOS corpus facts_moved=36 (both counts unchanged from the
pre-fix run — the fix's edge cases do not appear in this population),
both verdict engines report no verdict moved. Evidence-only: no
instrument, treatment, spec, or CI file touched; built directly on A',
not rebased.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011wKL9pEApQTK4dE3eXv8g8
@coderabbitai

coderabbitai Bot commented Sep 19, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Repository: PhysShell/Own.NET/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: bd1436c8-0891-4452-b41a-df687f180170


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@PhysShell
PhysShell merged commit 23e3203 into main Sep 19, 2026
75 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants