Skip to content

feat(ownir): bound source coordinates to int32 and mirror it in the bridge (#259 final acceptance) - #341

Merged
PhysShell merged 4 commits into
mainfrom
claude/coordinate-domain-contract-ie8jmb
Sep 7, 2026
Merged

feat(ownir): bound source coordinates to int32 and mirror it in the bridge (#259 final acceptance)#341
PhysShell merged 4 commits into
mainfrom
claude/coordinate-domain-contract-ie8jmb

Conversation

@PhysShell

Copy link
Copy Markdown
Owner

Что и зачем

#259's final acceptance was one contract decision, not one more port: spec/OwnIR.md §4.2 bounded a source coordinate at signed 64 bits while the core's AST holds a line as u32, and four verdict_boundary_* controls sat in the Layer 3 exclusion ledger because of the gap. This lands the decision Python-first — every line is now in [0, 2147483647] and every column in [1, 2147483647], the int32 domain every consumer this project feeds actually has — mirrors it in the Rust door and bridge, and promotes the four controls out of the exclusion ledger, which leaves only the two #294 OD-1 door controls there.

Тип изменения

  • feat — новая возможность
  • fix — исправление бага
  • docs — документация
  • refactor / chore / test / ci — без изменения поведения

Как проверено

  • python tests/run_tests.py
  • ruff check . и mypy
  • cd rust && cargo fmt --check && cargo clippy --workspace --all-targets && cargo test --workspace --no-fail-fast
  • python tests/test_ownir_validation_fixtures.py / test_ownir_defensive_limits.py / test_verdict_fixtures.py / test_repro_fixtures.py
  • twelve mutation campaigns re-run on a clean tree at this branch's head (python scripts/mutate_campaign.py --campaign docs/evidence/<c>.json --run) — 276 mutations, zero survivors, every expected catcher named per mutation and hit; counts in p022-coord-mutations.md and the other campaign fragments

Связанные issue

Refs #259, #250, #326, #294.

The owner applies the issue-body updates at merge (the exact text is the last section of this body) and marks #259 by hand. This PR marks nothing.

Чеклист

  • изменение покрыто тестом/селфтестом
  • README/docs обновлены при необходимости
  • коммиты в conventional-commit стиле

Checkpoint packet (#250)

Scope. The coordinate-domain contract and its port. spec/OwnIR.md §4.1/§4.2 and spec/ownir.schema.json; the strict door's domain rule on every line-bearing field, including the two §4.2 recorded as validated nowhere (components[].subscriptions[].line and a flow op's line, at every nesting shape); the tolerant door's degrade-to-0; the Rust mirror in own-ir (strict.rs, protocol.rs) and own-bridge (ast.rs, lower.rs, verdict.rs, render.rs); the promotion of the four verdict_boundary_* controls; two mutation campaigns and a re-run of the other ten; the status surfaces.

Explicit non-goals. #294 OD-1 (the two remaining exclusions). The DI004/DI005 rendered-surface case. The BR-V5 protocol-path sentence (cp4b note §6.7). own-shadow's Layer 3 boundary — the reducer still refuses the verdict layer and that is #260's. No severity, message-matrix or analysis change. No new tolerant rule for the protocol family beyond the coordinate domain.

Python source of truth. ownlang/ownir.py (_check_representable, _check_line_domain, _check_column, _check_flow_coordinates, _as_line, _as_col) and ownlang/obligations.py (_opt_line, the Door split). The reference changed first and the port followed; that ordering is #326's precedent and it is load-bearing, because a census taken against a moving reference measures nothing.

Frozen fixture. tests/fixtures/ownir_validation.json (the cp1 strict-door ledger), tests/fixtures/verdicts/ under manifest.json (Layer 3), tests/fixtures/verdict_renders/ (BR-V9), tests/fixtures/repro/digests.json (the shadow digest ledger).

Fixture regeneration command.

python tests/test_ownir_validation_fixtures.py --write
python tests/test_verdict_fixtures.py --write
python tests/test_repro_fixtures.py --write
python scripts/render_checkpoint_status.py

Steady-state test command.

python tests/run_tests.py
ruff check . && mypy
cd rust && cargo fmt --check && cargo clippy --workspace --all-targets && cargo test --workspace --no-fail-fast

Production dependency changes. None. No crate gained a dependency; no Python module gained an import.

Behaviour changes — NOT "none" this time. This is the one PR in the #259 series where Python production code changes, and it is Python-first by design:

  1. load() rejects a line outside [0, 2147483647] and a column outside [1, 2147483647] (category Location — a representable coordinate violating its domain rule). The representable-form rule (signed 64 bits, Shape) is unchanged and still checked first; keeping the two axes apart is what stops a domain violation being filed as a shape one, which is the exact defect feat(ownir): bound source coordinates and nesting depth (Python-first) #326's census had to discover in column.
  2. load() validates components[].subscriptions[].line and a flow op's line — for type as well as domain, at every nesting shape. §4.2 recorded these as an open contract question ("should a coordinate no rule reads still be well-formed?"). The answer is yes, because the tolerant door does read it and anchors findings on it.
  3. check_facts() degrades an out-of-domain coordinate: a line to 0 ("unknown / file-level", the value an absent line already reads as), a column to absent. Degrade, never clamp2^31 does not become 2^31 - 1 and -1 does not become 1, because a clamp moves a finding onto a real line the producer never named.
  4. The protocol grammar takes a Door: type and representability stay grammar and still skip a malformed entry whole (cp4b's rule, untouched); only the domain follows the door. Measured: a raising _opt_line on the tolerant path drops the whole method, so one impossible coordinate would have taken a real violation with it.

Consequence: four Layer 3 goldens move (the finding's line, nothing else), and the four boundary controls leave rust_replay_excluded.

Acceptance changes. The Layer 3 exclusion ledger shrinks from six entries to two, and the pinned set in own-bridge/tests/verdicts.rs with it. Both remaining entries are #294 OD-1. The replay additionally asserts the §4.2 domain at Layer 2 over every replayed case.

Local commands. As above, plus python scripts/mutate_campaign.py --campaign docs/evidence/p022-coord-1.json --run and --campaign docs/evidence/p022-coord-2.json --run on a clean tree.

GitHub Actions links. Filled in by CI on this PR — the workflows are the repository's own (pr-issue-validation, the Python gate, the Rust gate).

Known deferred cases.

Migration counters (asserted by the replays, not counted by hand)

counter value asserted by
Python-only 0 own-bridge/tests/verdicts.rs (every Finding member, every refusal in full, no fail-fast)
Rust-only 0 the same replay, plus own-ir/tests/validation_replay.rs over the cp1 ledger
Changed 0 both replays
Ordering-only 0 own-bridge/tests/verdicts.rs (BR-V8 stable sort compared in order)
Unexplained 0 both replays fail on any divergence, so a green build is the claim

Every count in this packet is a link into docs/generated/:
cp1 census ·
Layer 3 census ·
coordinate census ·
cp5 surface inventory ·
final-acceptance campaigns ·
cp4 ·
cp4b ·
cp5 ·
shadow.

Read the note first: docs/notes/p022-bridge-verdict-final-acceptance.md.


Issue text for the owner to apply at merge

I do not edit issues. Below is the exact replacement text.

Replacement for #259 § Final acceptance

Final acceptance

#259 final acceptance reached: Layer 3 parity over the full #258 family inventory at the full Finding and the rendered surfaces; declared boundary: the two OD-1 door controls (#294), measured, not open work.

Not "shadow mode", not "P-022 done", not "Rust is the default". Counts live in docs/generated/; the note is docs/notes/p022-bridge-verdict-final-acceptance.md.

Replacement for #250's #259 entry

| 6b | Rust own-bridge, layered OwnIR parity | #259 | final acceptance reached — cp1–cp5 and 4b complete, the coordinate-domain contract landed Python-first and mirrored, the four boundary controls promoted; the exclusion ledger names only the two OD-1 door controls (#294). Not shadow mode: that is #260's acceptance. |

Replacement for #250's queue line

Preferred queue: #259 final acceptance reached → #260 acceptance / #261.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JA26kL7GDHKPxkYhmpzn9W


Generated by Claude Code

…final.0)

#259's final acceptance is one contract decision, not one more port: §4.2 bounds
a source coordinate at signed 64 bits, the core's AST holds a line as u32, and
the four verdict_boundary_* controls sit in rust_replay_excluded because of the
gap between those two numbers.

#326 established the order for a change of this shape — Python-first, with the
census taken against the tree rather than against the change — so this commit
measures before anything moves and contains no production code.

The note records the owner's decision (D1 domain [0, 2^31-1] with 0 as the
absent sentinel, D2 the strict door rejecting out-of-domain as Location for
every line field including the two §4.2 records as unchecked, D3 the tolerant
door degrading to 0), the full coordinate inventory on both doors in both
languages, the churn budget, and the cp1 controls that flip with the `why` each
one's decision replaced quoted rather than deleted.

tests/coordinate_census.py walks every JSON file under tests/fixtures/ and
classifies every line/ctor_line/column slot at any depth. Three properties of it
are load-bearing. It is WIDER than the door, because "0 stays a legal line" is a
claim about the goldens and a census reading only the inputs cannot see one of
them. Its value classes follow the taxonomy's axis — outside-int64 has no
representable integer form (Shape), negative and above-int32 are representable
coordinates violating the domain rule (Location) — because folding them would
reproduce the exact defect #326's census had to discover. And the slot inventory
is asserted as a SET: a declared slot no fixture reaches is reported as a
phantom, the rule the schema's binding map already lives under.

Two measurements the note records because they went differently than expected:

* the Rust COLUMN path has no u32 anywhere. Measured through the crate rather
  than read: a document with "column": 2^40 is accepted by the Rust strict door,
  carried as Some(1099511627776) on Finding::column, and emitted to SARIF as
  startColumn 1099511627776. There is no second undeclared boundary; the
  tolerant readers gain D1's upper bound so the tolerant door cannot emit a
  column the strict door would refuse, not to close a divergence.
* the churn budget is four verdict goldens and nothing else. tests/fixtures/repro
  does NOT churn, digests.json included: the digest ledger pins the canonical
  hash of each FACTS document and no facts document changes, and the curated
  artifact set — the only place a layer envelope's produced/refused status is
  committed — contains none of the four boundary documents. Measured by probe
  (the tolerant line reader replaced in-process with the degrade) across every
  fixture family before writing it down.

The census renders to docs/generated/p022-coord-census.md through the existing
fragment gate, so no count is typed in the note.

Refs #250, #259, #326.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JA26kL7GDHKPxkYhmpzn9W
…ield (Python-first) (#259 final.1)

The reference is the oracle, so the coordinate-domain decision lands here first
and the port follows in the next commit. This is #326's order, and it was
load-bearing there for the same reason it is here: a change to what the
reference ACCEPTS cannot be merged beside the port that mirrors it, or the
census that judges the pair is taken against a moving reference.

spec/OwnIR.md §4.2 now bounds coordinates TWICE, on two axes it keeps apart.

The FORM is unchanged: every coordinate integer fits a signed 64-bit integer
(#326). The DOMAIN is new and sits inside it: every line lies in
[0, 2147483647], every column in [1, 2147483647] or absent or null. 2147483647
is int32, and int32 is the line type of every consumer this project feeds —
Roslyn's LinePosition.Line, LSP's uinteger, .NET diagnostics — so a line wider
than that cannot reach the place it points at, whatever it can be stored in.

0 is legal and means "unknown / file-level". It is the reference's OWN default
for an absent line, and the corpus carries it in the goldens as well as the
inputs, so bounding at 0 rather than at 1 reads the reference rather than
tightening it. A negative line is refused: no producer emits one — the Roslyn
extractor writes StartLinePosition.Line + 1 off a 0-based position — and
nothing downstream can point at it.

Keeping form and domain apart is not tidiness. The #259 cp1 taxonomy classifies
a rejection by its MECHANISM, and this change is the first case where one value
can violate one axis and satisfy the other: i64::MAX as a line was accepted and
is now `location`, because it still HAS a signed-64 form and violates only the
rule about what that form may mean; i64::MAX + 1 was and stays `shape`, because
it has no such form at all. Folding them would have re-created the exact defect
#326's census had to discover — one violation classified two ways.

§4.2's recorded exception is closed rather than carried. components[].
subscriptions[].line and a flow op's line were validated NOWHERE, not even for
type, and §4.2 recorded that as an open contract question rather than a parity
gap because both implementations agreed about it. The answer is yes, a
coordinate no rule reads must still be well-formed — because the TOLERANT door
does read it and anchors findings on it. Both are validated now, for type and
for domain, the flow-op line recursing through then/else/body exactly as the
column walk already did. The schema's binding map moves with them: ten untyped
"line": {"type": "integer"} sites become $refs, and UNBOUND is now empty, with
the machinery kept so the next unclassified path is still red.

The tolerant door degrades, and the degrade is the half that had to be designed
rather than transcribed:

  _as_int is gone. Every one of its call sites read a line, so it becomes
  _as_line and carries the domain: an out-of-domain line reads as 0 — the value
  an absent line already reads as. DEGRADE, NEVER CLAMP: 2^31 does not become
  2^31 - 1 and -1 does not become 1, because a clamped anchor points at a REAL
  line the producer did not mean, which is worse than saying nothing. _as_col
  gains the same upper bound so the tolerant door cannot emit a column the
  strict door would refuse.

  The protocol family needed a door split rather than a rule. obligations.
  _opt_line sits behind BOTH doors through one parser, and measured, a raising
  _opt_line makes check_facts drop the whole method — so an impossible
  coordinate would have taken a real violation with it. Type and
  representability stay grammar and still fail loud (the entry is skipped whole,
  cp4b's rule, untouched); the DOMAIN is the door's, threaded as `strict`
  through parse_method/parse_events. load() refuses; check_facts degrades to 0.

Two pins, because the two-doors contract is only true if they hold. The
tolerant degrade is asserted on three paths with clamp controls at both ends.
And the strict door must never REACH the degrade — a document load() accepts
must carry no out-of-domain coordinate at all — asserted over every document
the cp1 ledger records as accepted, not stated in prose. That assertion found
the four controls this decision flips before the ledger did.

Ledger: 216 -> 294 controls, insertion-stable (78 added, 0 removed, 0 reordered,
5 rewritten). The five rewritten are the four flips plus accept-zero-line, whose
`why` now says zero is the bottom of the domain rather than an edge case that
survived. The flips keep their `accept-` names so the change reads as one
re-measured control rather than a new control beside a deleted one; each `why`
states the position it replaces, and the note quotes the old wording in full.
One control closes a gap the flips exposed rather than created: a column below
the representable form is `shape` by mechanism even though the reference's
message names the 1-based rule, because it tests `v < 1` before the form.

Churn is exactly the four verdict_boundary_* goldens (the finding's `line`,
nothing else), as the budget written in final.0 predicted. The DI001 evidence
slice does not move: its captor step was already dropped by the >= 1 guard at
-5 and is still dropped at 0. Nothing in repro/, summaries/, lowered/,
verdict_renders/ or either fact-parity ledger changes.

The Rust half is knowingly red at this commit — the port has no domain rule yet
and the four boundary exclusions still declare a refusal that is about to stop
happening. That is the same deliberate red 934213c recorded for #326: a fix
landing beside its own evidence cannot be checked against it.

Refs #250, #259, #326.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JA26kL7GDHKPxkYhmpzn9W
…ary controls (#259 final.2)

The port follows the reference. #259's last exclusion family is promoted, and
it is promoted because the CONTRACT moved in the previous commit — never
because the core happens to hold a line as u32.

own-ir, the strict door. defaulted_int becomes defaulted_line and carries the
§4.2 domain after the representable form, answering Location where the form
answers Shape; column gains the same upper bound, on the same axis as its
1-based rule; flow_columns becomes flow_coordinates and checks the op's line
before its column; subscriptions gain their line between the resource-kind
check and the column, which is the reference's order and observable through
BR-D1. The site arrays get a second pass rather than a widened `all(...)`,
because the reference puts the shape check in one folded predicate and the
coordinate rule in a separate loop right after it — one pass answering both
would report a domain violation as the record's shape.

The typed model stays Option<i64> and now says why in a doc comment: the strict
door guarantees the domain, and narrowing the field would put a SECOND door
rule in serde — one answering Shape for a domain violation, in serde's field
order rather than BR-D1's, firing on the tolerant path where the reference
degrades. A typed field wider than the domain is not a hole; it is the shape
that leaves exactly one implementation of the rule.

The protocol grammar gets a Door parameter, not a second parser. parse_method /
events / event_line take Door::{Strict, Tolerant}, mirroring the reference's
own split: type and representability stay grammar and fail loud either way (the
entry is skipped whole on the tolerant path, cp4b's rule, untouched), while the
domain is the door's. Two readings of one grammar is exactly what 4b collapsed
into one, so this is a parameter rather than a fork.

own-bridge. ast::core_line degrades instead of refusing, and its BridgeError
text is gone; it is infallible now and the signature says so, because keeping
the Result would leave a refusal path the ledger can no longer reach and no
test could ever exercise. verdict::anchor_line and guarded_line collapse into
one core_line: guarded_line existed to fold a NEGATIVE site line to 0 while
leaving the upper end fail-loud, and the domain makes both ends one rule.

Removing the refusal exposed the divergences it was hiding, which is the cp5
lesson arriving on schedule — a comparison surface that gains a member can lose
controls. Three of them, all in the same shape: a reader that took the fact's
value where the reference takes `_as_line`'s or `_as_col`'s.

* the bridge's raw line readers (as_int in lower.rs and verdict.rs) returned
  the coordinate unchanged. Unobservable only because the AST build refused any
  document carrying an out-of-domain one before a finding could be built from
  it. Both become as_line with the domain — the same rename the reference took.
  Measured: without it, a slice whose acquire line the reference drops would
  have been kept and anchored where nothing is.
* lower::as_col had no upper bound at all, so the previous commit's Python
  change would have left the tolerant column readers disagreeing. Bounded to
  the same domain, for the reference's stated reason: the tolerant door must
  not emit a column the strict door would refuse.
* render::code_flows silently DROPPED a step it could not convert — a rule the
  reference does not have, unreachable behind the refusal and a silent
  divergence the moment it went. It is a total conversion with a debug_assert
  as the tripwire, so a future builder that skips as_line says so instead of
  losing a step.

The promotion: rust_replay_excluded 6 -> 2, the pinned set in verdicts.rs with
it, and the two that remain are both #294 OD-1. Neither golden was regenerated
to reach it — the four moved in final.1, with the reference.

Four controls land with the code they protect, because the campaigns in final.3
are only evidence if the layers that must catch a mutation exist. Three were
written for rules the promotion made reachable; the fourth exists because a
campaign proved a rule was NOT reachable:

* own-ir/tests/validation_replay.rs pins the PORT's own coordinate diagnostics
  — the axis each rule answers on and the domain each message names.
  Deliberately not the cross-language comparison the rest of that file makes
  (kind, never text): it stops this port's diagnostics rotting, which matters
  more now that the door has two coordinate rules on two axes.
* own-bridge/tests/verdicts.rs asserts the §4.2 domain at LAYER 2, over every
  replayed case. Nothing else pins it: ast::core_line degrades on the way in,
  so a port carrying a raw coordinate through the lowering would look identical
  at Layer 3 and disagree with the reference at the seam cp2's evidence is
  taken from.
* tests/fixtures/verdicts/verdict_domain_tolerant_readers is one synthetic
  Layer 3 case for the two tolerant readers nothing else reaches. The four
  promoted controls all pass through the AST build, which degrades a second
  time, so a port that kept the raw value still produced the right anchor —
  measured, as three survived mutations. A DI registration line an escape
  slice's source hop reads is guarded on `>= 1` and never narrowed again, and
  a subscription column is read by the column reader alone: at 2^31 the hop
  must be DROPPED and the column ABSENT, and a pass-through keeps both at
  coordinates nothing can point at. Its in-domain twin sits beside it so the
  control cannot pass by refusing everything. Insertion-stable: +1 golden, +1
  digest record, zero existing records rewritten.
* own-bridge/src/ast.rs gains a unit test on core_line — and its doc comment
  says why a direct test rather than an end-to-end one, because a unit test on
  the function under test is normally the weak evidence this project refuses.
  `lower` degrades every coordinate first, so core_line is a second line of
  defence over an i64 field wider than the domain: real, and unobservable end
  to end. The campaign proved it — clamping there, or accepting the whole u32
  range again, changed no golden.

Two deliberate changes beyond the code, both declared:

* the four boundary fixtures' own _doc blocks said "the Rust core refuses the
  document rather than clamp the coordinate", which this commit makes false. A
  control whose documentation contradicts the behaviour it pins is worse than
  the four digest records it costs to fix, so they are rewritten and
  tests/fixtures/repro/digests.json is regenerated: exactly four records
  changed, one added (the new control), zero removed, zero rewritten
  elsewhere. No artifact, trace or reduction moves, and the own-shadow reducer
  still refuses Layer 3.
* an amendment to final.0's churn budget, which predicted zero repro churn.
  The prediction was right about what the CHANGE forces; the five records are
  the price of an editorial fix and a control added on purpose, and saying so
  is the point of having written the budget down first.

The campaign definitions land here rather than in final.3, and the reason is
mechanical: a recorded run must name a commit that ALREADY contains the
definition it ran, because the runner requires a clean tree and the gate
requires the recorded commit to be an ancestor of HEAD. It is the same shape
the tree already has — cp4b.2's recorded run names the commit before the one
that records it. So docs/evidence/p022-coord-1.json (the strict door, 26
mutations) and p022-coord-2.json (the tolerant degrade, 15), their renderer
registration, and the re-anchoring every campaign needed are here; the recorded
runs are final.3's.

Six anchors were re-anchored, never deleted, and two of them are findings
rather than bookkeeping:

* cp4's M15/M20/M21 and cp4b.2's M23/M28 pointed at core_line, as_col's bound,
  guarded_line, parse_method's call shape and the manifest's first exclusion —
  all surfaces this commit moved.
* M15's CATCHER moved with it. The Layer 3 replay caught it while core_line
  refused; the domain makes it unreachable from outside, so the control is the
  direct one above.
* shadow-cp4's M61 expected four catchers and two of them stopped being
  catchers ON MAIN, before this work: `the_reducer_is_silent_on_unchanged_data`
  and `two_engines_that_both_refused_a_layer_agree` only failed while some
  committed artifact still had ASYMMETRIC verdict-layer statuses, and 4b's
  promotion removed the last of them. Measured at 834f295 with the mutation
  applied, in a worktree, rather than inferred from this change. The two that
  remain are the load-bearing ones. That is precisely why every campaign is
  re-run rather than trusted: a recorded result stays valid for the commit it
  names, and says nothing about this tree.

Two harnesses learn to name their failures (`FAIL[<check>]:`, the house pattern
the repro and verdict families already use), so a campaign's expected catcher
can be the rule it attacks rather than "this file exited non-zero".

Knowingly red at this commit: no campaign result matches its definition's sha
any more, and the two new campaigns have no recorded run at all. Both are
final.3's, and re-running is the only honest fix — the same deliberate red
934213c recorded for #326. Everything else is green: cargo fmt --check, cargo
clippy --workspace --all-targets with zero warnings, cargo test --workspace
--no-fail-fast across 44 targets, ruff and mypy.

Refs #250, #259, #294.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JA26kL7GDHKPxkYhmpzn9W
…259 final.3)

Twelve mutation campaigns, re-run on a clean tree at this branch's head, and
the surfaces that describe what they measured.

Two are new. They are split by DOOR rather than by sub-checkpoint, because the
two doors fail differently — the strict one refuses an out-of-domain coordinate
and the tolerant one degrades it — and a campaign measuring them together could
not say which half a survivor belonged to. Every rule is mutated on BOTH sides,
in the reference and in its Rust mirror, since a domain only one implementation
enforces is a divergence rather than a rule. Both ends of both bounds, and
off-by-one at each: a domain probed only far outside cannot tell a correct
limit from a narrow one. Two mutations attack the CATEGORY and the message
rather than the verdict, because a domain violation filed as `shape` leaves
every accept/reject green and is the exact defect #326's census found in
`column`.

Ten are re-runs, and the re-running is the point rather than the ceremony. A
recorded result stays valid for the commit it names and says nothing about this
tree; discipline 3's lesson is that a comparison surface which gains a member
can lose controls. Every campaign now names this branch's head, the tree was
clean before and after each, no mutation survived, and every expected catcher
fired.

What the first pass of the tolerant campaign found is the part worth keeping,
and it is recorded in the note's §5 rather than summarized away. Seven of
fifteen mutations survived, with one cause: the promotion made rules reachable
in principle that the corpus still could not observe.

* `ast::core_line` cannot be reached from outside any more — `lower` degrades
  every coordinate one layer earlier — so clamping there, or accepting the
  whole u32 range again, changed no golden. It is a real second line of defence
  over an i64 field wider than the domain, and it is not an end-to-end control;
  the direct test that now pins it says exactly that, so it cannot be misread
  as one.
* The four promoted controls all pass through the AST build, which degrades a
  second time, so a port that kept the raw fact value still produced the right
  anchor. Two readers escape that: a DI registration line an escape slice's
  source hop reads, and the subscription column. One synthetic Layer 3 case
  closes both.
* One equivalent mutant, proven and re-anchored rather than annotated:
  `render::code_flows` used to drop a step it could not convert, and §4.2 makes
  every flow-step line convertible, so re-introducing the drop drops nothing.
  The mutation moved onto the conversion itself, which the byte-exact rendered
  replay catches.

Two mutations were rewritten because each was caught by a TRACEBACK rather than
by a named check, and a catcher recorded as "non-zero exit with no reported
failure" is evidence of a crash, not of the rule. Both now attack the same rule
where it is legible.

The surfaces. A generated cp1 census (`tests/validation_census.py` →
`docs/generated/p022-cp1-census.md`) counts the strict-door ledger from the
ledger itself: controls by section, by verdict and by category, plus the
coordinate family on its own. The three numbers that used to be typed on the
P-022 cp1 row — `216 controls`, `matrix 35/181`, `48 mutations` — leave it and
stay in the issue bodies as history; the row says "four censuses" and links the
fragment. cp4's row records the coordinate boundary as promoted rather than
owed; cp5's replaces the `u32` sentence with the decision itself, in the terms
the decision was taken in — int32 is the line type of every consumer this
project feeds, never "Rust is u32, so the reference is wrong".

`spec/Bridge.md` and `spec/BridgeBehaviorMatrix.md` follow: BR-D1 gains the
subscription coordinate in its check order, BR-D2 and BR-L11 name `_as_line`
and the domain it now carries, OD-3's entry says what "everywhere" means today,
and the matrix gains two rows — one per door — rather than a sentence saying
the boundary is owed. Three doc comments in the bridge still said `_as_int`
after the rename; they say `_as_line` now.

The queue reads "#259 final acceptance reached → #260 acceptance / #261", and
the line under the checkpoint table states what that means and, at the same
length, what it does not: not shadow mode (that is #260's acceptance, blocked
on its own two decisions), not "P-022 done", not "Rust is the default".

The churn against the branch base is in the note's §3.2, checked against the
budget §2.2 wrote before anything moved. Four verdict goldens moved, in the
finding's `line` and nothing else. Nothing under `summaries/`, `lowered/`,
`verdict_renders/` or either fact-parity ledger moved at all — the goldens
anchored at line 0, dozens in the summaries family alone, came through exactly
as the decision requires. Five records under `repro/` moved and the budget said
zero: four because the boundary fixtures' own documentation contradicted the
behaviour they pin, one because a campaign found a blind spot and the fix was a
control. Both are deliberate, and saying so is the point of having written the
budget down first.

Green: `python tests/run_tests.py`, `ruff check .`, `mypy`, and in `rust/`
`cargo fmt --check`, `cargo clippy --workspace --all-targets` with zero
warnings, `cargo test --workspace --no-fail-fast` across 44 targets.

Refs #250, #259, #294, #326.

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

coderabbitai Bot commented Sep 7, 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: a7fa1ffa-a019-4f3a-8aaa-711bf1e6bea2


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 21fb0c3 into main Sep 7, 2026
45 checks passed
PhysShell pushed a commit that referenced this pull request Sep 8, 2026
Refs #261, #345, #262, #250.

The owner ratified #261's decision packet (C-1..C-5 with the exit-code
rulings) on 2026-09-08. The tree half of that move: P-022 rows 7b and 8,
the preferred queue, the crate-DAG line for own-cli, the proposals index
row and the own-cli row of rust/README.md now say what was decided —
the oracle split by surface and behaviour class, `report` struck, the
production OwnIR executable separated from the residual .own/dev CLI
(#345), engine selection kept outside the executable, the production
seam alone as #262's CLI prerequisite — and say explicitly that a
ratified decision is not completed code.

Two stale sentences on the same surfaces corrected in the same move:
rust/README.md's own-bridge row still read "in progress (cp5 open)"
after #259's final acceptance (PR #341), and the sentence above the
P-022 table still said #260's acceptance was blocked on its decisions.

No count is typed on any surface; no evidence, code or generated
fragment changes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016vuEiNoEXaooMDRu6tXrZ6
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