Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9f6dfe3
P-037 A1.1-a1 (tooling): a corpus verdict snapshot, so "same verdicts…
claude Sep 18, 2026
17e7285
P-037 A1.1-a1 (tooling): the snapshot asked for a severity that does …
claude Sep 18, 2026
cca0f44
P-037 A1.1-a1: the pre-change corpus baseline, both engines, 137 files
claude Sep 18, 2026
2720b9a
P-037 A1.1-a1: parameter-bearing methods reach OwnIR with params[]; n…
claude Sep 18, 2026
2de574a
P-037 A1.1-a1: a returned PARAMETER is a bare CFG exit, not a fresh-f…
claude Sep 18, 2026
464308b
P-037 A1.1-a1: zero semantic cut, proved on two independent sources
claude Sep 18, 2026
80af136
P-037 A1.1: the fact-shape census, and both P-037 checkers wired into CI
claude Sep 18, 2026
aff77cd
docs(p037): freeze A2 execution governance
PhysShell Sep 18, 2026
57510ee
P-037 A2.0: add evidence provenance closure
PhysShell Sep 18, 2026
efb0d55
P-037 A2.0: add whole-MOS differential gate
PhysShell Sep 18, 2026
bb11b56
P-037 A2.0: wire provenance controls into evidence gates
PhysShell Sep 18, 2026
f968696
P-037 A2.0: bind MOS source set to committed inputs
PhysShell Sep 18, 2026
7366ff9
P-037 A2.0: satisfy lint and launcher census
PhysShell Sep 18, 2026
5eaab59
P-037 A2.0: normalize MOS snapshot imports
PhysShell Sep 18, 2026
6ed8f3f
P-037 A2.0: preserve repository compilation in MOS gate
PhysShell Sep 18, 2026
f09ca0c
P-037 A2.0: make provenance closure fail on phantom paths
PhysShell Sep 18, 2026
724ce19
P-037 A2.0: expose provenance control to the regression suite
PhysShell Sep 18, 2026
dad79e3
P-037 A2.0: close evidence integrity gaps before baselines
PhysShell Sep 18, 2026
82b7449
P-037 A2.0: make snapshot integrity narrowing explicit
PhysShell Sep 18, 2026
1113188
P-037 A2.0: harden the evidence harness into a T-candidate contract
claude Sep 18, 2026
4a8e658
P-037 A2.0: seal the executed Rust artifacts per take and attest them…
claude Sep 18, 2026
5fd6bfa
docs(p037): record A2 baseline evidence at terminal-green T
PhysShell Sep 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2998,6 +2998,47 @@ jobs:
[ "$rc" -eq 1 ] || { echo "FAIL: expected exit 1 (findings), got $rc"; exit 1; }
case "$out" in *OWN001*) ;; *) echo "FAIL: the Rust core found no OWN001"; exit 1 ;; esac
echo "OK: Rust-default dogfood on ${{ matrix.os }} (own-check.sh)"
# P-037 A1.1 (#304). Both censuses run here because this is the one job that
# already has all three things they need — a .NET SDK for the extractor, a
# built Rust candidate, and a Python for the reference engine.
#
# Linux only, deliberately: both censuses are about the EXTRACTOR's output and
# the two engines' reading of it, neither of which is platform-shaped. The
# Windows leg of this job exists for own-check.ps1, which is a separate
# launcher implementation and is the thing Windows actually tests.
#
# NOT here: the full P-037 verdict/MOS snapshots. The verdict sweep is
# 137 files times two engines and the MOS gate additionally measures the
# independent repository C# tree. They are per-STEP evidence recorded under
# docs/evidence/, not per-push work. The cheap provenance and MOS-reader
# controls below ARE per-push so forgetting or hollowing the heavy gate is red.
- name: P-037 conformance controls + fact-shape census
if: matrix.os == 'ubuntu-latest'
run: |
export OWEN_RUST_CORE="$PWD/rust/target/release/own-cli"
# The four G-V4 / class-3 controls, on both engines against one record.
# Today's record must match; --post-a1 is expected to FAIL until the
# semantic cut lands and is therefore NOT run as a gate here.
python scripts/p037_controls.py --engine both
# The eleven fact shapes. This is the census that exists because the
# 137-file VERDICT corpus reported UNCHANGED for a version of a1 that
# turned six jobs in this very workflow red: verdict coverage is not
# syntax/fact-shape coverage, and one fixture per shape fails by NAME.
python scripts/p037_fact_shapes.py check --engine both
# A2.0: a recorded run remains evidence only while its measured
# implementation closure and exact input blob set stay unchanged.
python tests/test_p037_evidence.py
# Level 2 reads the whole MOS document through the existing common
# P-022 capture envelope. This cheap control pins that returns and
# unresolved fields cannot disappear behind a transfer-only projection.
python scripts/p037_mos_snapshot.py --selftest
# A2.0: the machinery a baseline run stands on, exercised where cargo,
# git and dotnet are real. Both Rust executables are built qualified
# (path from cargo's own compiler-artifact message, digest verified),
# the frozen repo population is derived, materialized from blobs,
# re-hashed and removed again. No baseline is taken here.
python scripts/p037_evidence.py artifacts
python scripts/p037_evidence.py population --source repo --materialize --cleanup
# The Windows launcher is a separate implementation, not a wrapper around
# the shell one, so the Windows leg has to go through it to mean anything.
- name: Own.NET's own tree, through own-check.ps1 on the Rust core
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ __pycache__/
*.py[cod]
*.egg-info/
.pytest_cache/
.mypy_cache/
# P-037 A2.0: frozen evidence populations, materialized from git blobs
# (scripts/p037_evidence.py); never tracked, never dirty.
.p037-population/
*.ownreport.json

# .NET build output (golden_arraypool demo)
Expand Down
34 changes: 34 additions & 0 deletions corpus/p037-shapes/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# `corpus/p037-shapes` — the FACT-SHAPE census for P-037 A1.1

A small, deliberate census of the C# shapes whose **emitted facts** the A1.1
steps must preserve or must change — not a second verdict corpus.

## Why this exists

A1.1-a1 taught the lesson the hard way. The 137-file verdict corpus reported
`UNCHANGED` for a version of a1 that turned six CI jobs red, because it contains
no method that returns its own disposable parameter and the repository's own
tree does. The corpus is labelled for **verdicts**; the A1.1 steps change the
**fact surface**.

> **verdict coverage ≠ syntax / fact-shape coverage**

Two hundred more random C# files would not have closed that gap. One fixture per
shape does. Each case here pins what the extractor emits for a shape it is known
to be sensitive to, so a step that changes a shape it did not mean to touch is
caught by name rather than by a CI job failing somewhere downstream.

## Layout

<shape>/case.cs the minimal program
<shape>/expected.json what the facts must look like, and the verdict

`expected.json` records `facts` (asserted structure of the emitted
`functions[]` record) and `verdict` (the finding codes at warning severity).
A case whose shape belongs to a step that has not landed carries
`"status": "pending_a2"` with the contract it will be held to, and asserts
today's facts meanwhile — so the diff a2 produces is visible per shape instead
of aggregated into a number.

Checked by `scripts/p037_fact_shapes.py`, which names its engine explicitly
(#262 Stage 3) and runs one extractor invocation per case.
28 changes: 28 additions & 0 deletions corpus/p037-shapes/call-expression-statement/case.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// A statement-level call. The C# lowering emits a `call` op in exactly ONE
// place and only for `var x = Foo(...)` — a call whose RESULT binds to a local.
// `Inner(s, true);` as a statement produces no `call` node at all, so there is
// nothing for a summary layer to specialise. Without this, F3 cannot be carried
// through the existing summary layer at all.
//
// This is also where a2's zero-semantic-cut risk lives: a real `call` op is
// already semantically active and MOS reads it, so emitting one here can move
// an existing summary before the guarded kernel is anywhere near. The MOS
// collapsed-view diff is what catches that.
using System.IO;

static class ShapeStatementCall
{
static void Inner(Stream s, bool keep)
{
if (!keep)
{
s.Dispose();
}
}

static void Caller(string path)
{
var r = File.OpenRead(path);
Inner(r, true);
}
}
33 changes: 33 additions & 0 deletions corpus/p037-shapes/call-expression-statement/expected.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"schema": "p037-fact-shape/1",
"shape": "call-expression-statement",
"status": "pending_a2",
"why": "The heart of the a2 gap. The C# lowering emits a `call` op in exactly ONE place and only for `var x = Foo(...)` — a call whose RESULT binds to a local. `Inner(r, true);` as a STATEMENT produces no `call` node, which the recorded facts show: `Caller`'s body is `acquire:r`, `release:r` — the fabricated release — and no call at all. So F3 cannot be carried through the existing summary layer today: there is no node for a guarded summary to specialise.",
"a2_contract": "a2 emits a real `call` op for relevant first-party statement-level calls. This is also where a2's zero-semantic-cut risk lives, and it must be measured rather than hoped: a real `call` op is ALREADY semantically active and MOS reads it, so emitting one here can move an existing summary long before the guarded kernel is anywhere near. A verdict that happens to stay the same is not a pass — an unintended MOS collapsed-view change means a2 began the semantic cut early, and the answer is to isolate it or restage, never to note that the tests are green.",
"measured_at": "464308b",
"facts": {
"ShapeStatementCall.Inner": {
"params": [
{
"name": "s",
"line": 15
}
],
"body": [
"if:None@17",
"then:release:s@19"
]
},
"ShapeStatementCall.Caller": {
"params": null,
"body": [
"acquire:r@25",
"release:r@26"
]
}
},
"verdict": {
"rust": [],
"python": []
}
}
25 changes: 25 additions & 0 deletions corpus/p037-shapes/extension-receiver/case.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// A reduced EXTENSION method. Roslyn's reduced symbol drops the `this`
// parameter from `Parameters`, but the summary lives on the UNREDUCED
// declaration where the receiver is parameter 0. a2 must synthesise
// receiver -> declared parameter 0
// timeout -> declared parameter 1
// or `CallReleasesReceiver` can never be honestly retired: the guarded
// summaries would be complete except for one old tunnel under the fence.
using System.IO;

static class ShapeExtensionReceiver
{
static void WaitForDispose(this Stream self, int timeout)
{
if (timeout >= 0)
{
self.Dispose();
}
}

static void Caller(string path)
{
var s = File.OpenRead(path);
s.WaitForDispose(5);
}
}
37 changes: 37 additions & 0 deletions corpus/p037-shapes/extension-receiver/expected.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"schema": "p037-fact-shape/1",
"shape": "extension-receiver",
"status": "pending_a2",
"why": "A reduced extension method. Roslyn's reduced symbol drops `this` from Parameters while the summary lives on the UNREDUCED declaration, where the receiver is parameter 0.\n\nTwo things measured here, and the second was a surprise. (1) `CallReleasesReceiver` — the `ConsumesParam` consumer that handles this shape — is reached ONLY from two sites gated on `ThisFieldName(...)`: it is part of the FIELD disposal detector and never runs on a local. (2) Consequently `var s = ...; s.WaitForDispose(5);` is charged OWN001 even when the extension disposes its receiver UNCONDITIONALLY — verified with an unguarded probe, so it is not the guard doing it. That is a pre-existing false positive of exactly the family P-037 addresses (a first-party callee that releases, unrecognised), arriving through the RECEIVER rather than the argument. It is not A1's to fix and is recorded here so it is not mistaken for something A1 caused.",
"a2_contract": "a2 synthesises the receiver binding against the UNREDUCED declaration: receiver -> declared parameter 0, `timeout` -> declared parameter 1. Without it `CallReleasesReceiver` can never be honestly retired — the guarded summaries would be complete except for one old tunnel under the fence, on the field path where nobody is looking.",
"measured_at": "464308b",
"facts": {
"ShapeExtensionReceiver.WaitForDispose": {
"params": [
{
"name": "self",
"line": 12
}
],
"body": [
"if:None@14",
"then:release:self@16"
]
},
"ShapeExtensionReceiver.Caller": {
"params": null,
"body": [
"acquire:s@22",
"use:s@23"
]
}
},
"verdict": {
"rust": [
"OWN001:warning@22"
],
"python": [
"OWN001:warning@22"
]
}
}
21 changes: 21 additions & 0 deletions corpus/p037-shapes/guard-bool-const-false/case.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// The same call with `false`. The two constants must be distinguishable in the
// facts: a single "is a constant" bit would collapse the exact distinction the
// cell selection turns on.
using System.IO;

static class ShapeConstFalse
{
static void Inner(Stream s, bool keep)
{
if (!keep)
{
s.Dispose();
}
}

static void Caller(string path)
{
var r = File.OpenRead(path);
Inner(r, false);
}
}
33 changes: 33 additions & 0 deletions corpus/p037-shapes/guard-bool-const-false/expected.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"schema": "p037-fact-shape/1",
"shape": "guard-bool-const-false",
"status": "pending_a2",
"why": "The same call with `false`. Present so the two constants are distinguishable in the facts: a single `is a constant` bit would collapse the exact distinction cell selection turns on, and would do it invisibly.",
"a2_contract": "{\"param\":1,\"kind\":\"bool_const\",\"value\":false}; Rust derives `const-neg`.",
"measured_at": "464308b",
"facts": {
"ShapeConstFalse.Inner": {
"params": [
{
"name": "s",
"line": 8
}
],
"body": [
"if:None@10",
"then:release:s@12"
]
},
"ShapeConstFalse.Caller": {
"params": null,
"body": [
"acquire:r@18",
"release:r@19"
]
}
},
"verdict": {
"rust": [],
"python": []
}
}
22 changes: 22 additions & 0 deletions corpus/p037-shapes/guard-bool-const-true/case.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// `Inner(s, true)`: a BOOL CONSTANT argument. a2 must record it as
// {"param":1,"kind":"bool_const","value":true}; Rust derives `const-pos`.
// The frontend never names const-pos itself — that is an interpretation
// relative to the callee's elected guard, and it belongs to Rust.
using System.IO;

static class ShapeConstTrue
{
static void Inner(Stream s, bool keep)
{
if (!keep)
{
s.Dispose();
}
}

static void Caller(string path)
{
var r = File.OpenRead(path);
Inner(r, true);
}
}
33 changes: 33 additions & 0 deletions corpus/p037-shapes/guard-bool-const-true/expected.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"schema": "p037-fact-shape/1",
"shape": "guard-bool-const-true",
"status": "pending_a2",
"why": "`Inner(r, true)`: the constant that decides the callee's branch. Today it reaches the facts as nothing at all — the statement-level call emits no `call` op, and the fabricated `release` from ConsumesParam stands in its place.",
"a2_contract": "{\"param\":1,\"kind\":\"bool_const\",\"value\":true}. The frontend never writes `const-pos`: that is an interpretation relative to the callee's ELECTED guard and belongs to Rust.",
"measured_at": "464308b",
"facts": {
"ShapeConstTrue.Inner": {
"params": [
{
"name": "s",
"line": 9
}
],
"body": [
"if:None@11",
"then:release:s@13"
]
},
"ShapeConstTrue.Caller": {
"params": null,
"body": [
"acquire:r@19",
"release:r@20"
]
}
},
"verdict": {
"rust": [],
"python": []
}
}
19 changes: 19 additions & 0 deletions corpus/p037-shapes/guard-forward-bare/case.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// A wrapper FORWARDS its own flag unchanged: `Inner(s, keep)`. a2 records
// {"param":1,"kind":"param","source_param":1}; Rust reads the `id` edge.
using System.IO;

static class ShapeForwardBare
{
static void Inner(Stream s, bool keep)
{
if (!keep)
{
s.Dispose();
}
}

static void Outer(Stream s, bool keep)
{
Inner(s, keep);
}
}
37 changes: 37 additions & 0 deletions corpus/p037-shapes/guard-forward-bare/expected.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"schema": "p037-fact-shape/1",
"shape": "guard-forward-bare",
"status": "pending_a2",
"why": "A wrapper forwarding its own flag unchanged. Today `Outer`'s body shows a fabricated `release` where the forward belongs, so the wrapper enters the summary layer as an unconditional consumer — the may-as-must flattening, now visible INSIDE the summaries rather than only at the call site.",
"a2_contract": "{\"param\":1,\"kind\":\"param\",\"source_param\":1}; Rust reads the `id` edge.",
"measured_at": "464308b",
"facts": {
"ShapeForwardBare.Inner": {
"params": [
{
"name": "s",
"line": 7
}
],
"body": [
"if:None@9",
"then:release:s@11"
]
},
"ShapeForwardBare.Outer": {
"params": [
{
"name": "s",
"line": 15
}
],
"body": [
"release:s@17"
]
}
},
"verdict": {
"rust": [],
"python": []
}
}
Loading
Loading