Skip to content

FEAT-065: scry adjudicates its own oracle (verify_against), conservative by construction - #120

Draft
avrabe wants to merge 2 commits into
mainfrom
feat-065-verify-against
Draft

FEAT-065: scry adjudicates its own oracle (verify_against), conservative by construction#120
avrabe wants to merge 2 commits into
mainfrom
feat-065-verify-against

Conversation

@avrabe

@avrabe avrabe commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Closes the loop REQ-018 promised. v3.1 shipped a verification oracle as prose; this makes it a machine verdict, so an agent's edit is gated by a sound checker rather than by tests.

verify_against(before, after)discharged | still-open | regressed | moved | removed-with-code | uncertain.

Two findings forced the design (DD-021, new)

1. The id can't be the matching key. obligation_id includes the advisory code — the clean-room required it, since one i32.div_s raises two obligations at one pc. But fixing an obligation changes its code (div-by-zeroproven-safe) and therefore its id — exactly the transition we must detect. Matching on it would render every genuine discharge as "one id vanished, an unrelated one appeared."

So three keys, each with one job: obligation_id (cite), site_key (match — excludes code), group_key (the ordinal domain — the conservatism signal).

2. discharged has to be a certainty claim. ObligationId.v proves identity can alias (survivor_inherits_deleted_identity), which admits a false discharge: delete an open site, and a proven-safe same-kind sibling inherits its identity — the deleted obligation looks fixed. So discharged is withheld unless the ordinal domain's site set is unchanged; otherwise uncertain.

I verified the rule actually fires

Rather than trust a green test (the aliasing oracle accepts two outcomes), I probed the real verdict:

Uncertain  before=div-by-zero  after=Some("proven-safe")
  "looks proven safe, but this site's ordinal domain changed membership
   so identity may have aliased — discharge withheld"

Without the conservatism rule this case reports discharged for an obligation that was never fixed.

removed-with-code stays strictly distinct from discharged: deleting the code that carried an obligation proves nothing, and an agent rewarded for conflating them learns to delete rather than fix.

Honest residual (in DD-021)

A pure reordering of two same-kind operators in one region preserves the group's site set while swapping which site each key denotes — that shape can still mis-attribute a verdict. Disclosed; closing it needs content corroboration beyond the key.

Verification

4 FEAT-065 oracles (real fix → Discharged; deletion → RemovedWithCode, never Discharged; aliasing → Uncertain; lost fact → Regressed). core 106, viz 25, segment 10, poly 9, interval 8, octagon 26. clippy -D warnings clean across CI's package set; fmt clean; rivet validate PASS.

Scope: the pure adjudicator. CLI/MCP surfacing is FEAT-066/067. FEAT-065 stays proposed pending clean-room + CI.

🤖 Generated with Claude Code

…tive by construction

Closes the fix-verify loop REQ-018 promised: the v3.1 verification oracle was
PROSE; this makes it a machine verdict, so an agent's edit is gated by a sound
checker rather than by tests.

`verify_against(before, after) -> Vec<VerifyVerdict>` with outcomes
discharged / still-open / regressed / moved / removed-with-code / uncertain.

DD-021 (new) records two findings that forced the design:
1. `obligation_id` includes the advisory CODE (clean-room: one i32.div_s raises
   two obligations at one pc, so the site alone does not discriminate) — but
   FIXING an obligation changes its code (div-by-zero → proven-safe) and hence
   its id, which is exactly the transition to detect. Matching on the id would
   render every genuine discharge as two unrelated events. So THREE keys, each
   with one job: obligation_id (cite), site_key (match — excludes code),
   group_key (the ordinal domain — the conservatism signal).
2. `ObligationId.v` PROVES identity can alias (survivor_inherits_deleted_identity),
   which admits a FALSE discharge: an open site is deleted, a proven-safe
   same-kind sibling inherits its identity, and the deleted obligation looks
   fixed. So `discharged` is a CERTAINTY claim — withheld unless the ordinal
   domain's site set is unchanged; otherwise `uncertain`.

Verified the conservatism actually FIRES on that scenario rather than passing by
another route (a probe printed the real verdict before it was removed):
  Uncertain  before=div-by-zero after=Some("proven-safe")
  "looks proven safe, but this site's ordinal domain changed membership so
   identity may have aliased — discharge withheld"
Without the rule this case reports `discharged` for an obligation never fixed.

`removed-with-code` is kept strictly distinct from `discharged` — deleting the
code that carried an obligation proves nothing, and an agent rewarded for
conflating them learns to delete rather than fix.

HONEST RESIDUAL (DD-021 limitations): a pure REORDERING of two same-kind
operators in one region preserves the group's site set while swapping which site
each key denotes, so that edit shape can still mis-attribute a verdict. Disclosed;
closing it needs content corroboration beyond the key.

Tests: 4 FEAT-065 oracles (real fix → Discharged; deletion → RemovedWithCode and
never Discharged; aliasing → Uncertain; lost fact → Regressed). core 106, viz 25,
segment 10, poly 9, interval 8, octagon 26. clippy -D warnings clean across CI's
package set, fmt clean, rivet validate PASS.

Scope: the PURE adjudicator. CLI/MCP surfacing is FEAT-066/067. FEAT-065 stays
`proposed` pending clean-room + CI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@avrabe
avrabe marked this pull request as draft August 11, 2026 19:59
@avrabe

avrabe commented Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

Converting to draft — clean-room found 6 criticals, and one of them falsifies DD-021's core rationale

Adversarial review of this adjudicator found six wrong-verdict paths, four of which the design did not anticipate. Not mergeable. Recording them here; the rework is tracked separately.

The rationale sentence is false

DD-021 asserts: "aliasing by deletion or insertion NECESSARILY changes the ordinal domain's membership." That is the sentence the entire conservatism rule rests on, and it is wrong. Pair a deletion with a same-kind insertion in the same region and the group's site_key set is byte-identical:

;; before — div#0 OPEN (param divisor), div#1 proven safe
i32.const 10 local.get 0 i32.div_s    i32.const 20 i32.const 5 i32.div_s   i32.add
;; after — the OPEN one deleted, a new SAFE one appended
i32.const 20 i32.const 5 i32.div_s    i32.const 30 i32.const 6 i32.div_s   i32.add

Group set identical ⇒ domain_stablesingle verdict: Discharged, attributed to a proven fact about a different operator, with no compensating Regressed. This is precisely the delete-instead-of-fix reward hack the rule exists to prevent.

The other criticals

  1. Cross-function identity collision. body_shape_hash (the no-name/no-export fallback) is the whole function identity, so two unnamed functions with the same opcode sequence share site_key/group_key. Deleting an entire function carrying an obligation reports Discharged.
  2. Obligation laundering via a typed region. (block (result i32) …) routes through havoc_region, which emits no trap check, no gap, no advisory when the write set is empty and there's no call. A live obligation vanishes and reports RemovedWithCode. One line, no knowledge of the identity scheme required. (Pre-existing FEAT-040/046 reporting hole; this feature promotes it to a wrong verdict.)
  3. Regressed fabricated on a byte-identical module. verify_against(&m, &m) returns contradictory verdictsStillOpen and Regressed under the same obligation_id, with an invented before_code: "proven-safe". Root cause: site_key excludes the code, so an open div-by-zero and a proven signed-overflow at one i32.div_s share a site key, putting it in before_proven. Fires on the ordinary const / var division shape. My own test feat065_regression_is_reported passes vacuously — it would pass on a self-comparison too.
  4. New obligations are invisible. Zero verdicts for an obligation introduced at a site with no prior advisory. An agent that fixes function A while introducing a div-by-zero in function B passes the gate clean — blind in the direction that matters most.
  5. A genuine fix reads as RemovedWithCode in stripped modules. body_shape_hash changes on any opcode edit, so for a function with no name and no export — i.e. anything through wasm-opt --strip-debugDischarged is unreachable for the canonical fix shape.

Confirmed closed

The advisory-less-operator vector I suspected is not a hole (ordinals are per-kind and per-region, and nested regions get their own counters), and ⊤-degradation does not suppress trap checks. The module sentinel cannot collide with a real site (kind = "<module>" is unreachable from op_report_name).

Assessment

An adjudicator that can wrongly say "fixed" is worse than none, because an agent optimises straight into it. Findings 1–3 are that failure; finding 5 means it cannot see new faults; finding 4 makes the Regressed channel unusable. The pure-library scope (no CLI/WIT surface yet) means nothing shipped — it does not soften the ranking.

Adversarial review of the FEAT-065 adjudicator (PR #120, now draft) falsified
this decision's central justification. DD-021 asserted that "aliasing by
deletion or insertion NECESSARILY changes the ordinal domain's membership" — the
sentence the whole conservatism rule rested on. It is FALSE: a deletion paired
with a same-kind insertion leaves the group's site_key set byte-identical, so
the check passes and a FALSE `discharged` is produced — the delete-instead-of-fix
reward hack the rule exists to prevent.

Retracted in place (not quietly reworded) and the six reproduced findings
recorded in `limitations`, including two the design did not anticipate at all:
new obligations produce no verdict whatsoever, and `regressed` is fabricated on
a byte-identical module (contradictory verdicts under one id).

Correcting the claim, not the ledger. The implementation stays in draft until
matching uses CONTENT corroboration rather than a positional ordinal plus a
population check.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

📐 rivet artifact delta

PR: #120 Base SHA: bf445c7e

Validation

head — `rivet validate` result
  SR-11 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-12 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-13 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-2 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-3 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-4 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-5 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-6 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-7 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-8 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-9 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SYS-1 (system-req, status: accepted) — missing: sys-integration-verification
  SYS-2 (system-req, status: accepted) — missing: sys-integration-verification
  SYS-3 (system-req, status: accepted) — missing: sys-integration-verification
  SYS-4 (system-req, status: accepted) — missing: sys-integration-verification
  SYS-5 (system-req, status: accepted) — missing: sys-integration-verification
  → run `rivet validate --explain SR-1` to see which link type and source types satisfy a gap

Result: PASS (119 warnings)
Schemas: common@0.3.0 (embedded), dev@0.3.0 (embedded), research@0.1.0 (embedded), research-ext@0.1.0 (on-disk), safety-case@0.1.0 (embedded), aspice@0.2.0 (embedded)
base — `rivet validate` result (for comparison)
  SR-11 (sw-req, status: accepted) — missing: unit-verification, sw-integration-verification
  SR-12 (sw-req, status: accepted) — missing: unit-verification, sw-integration-verification
  SR-13 (sw-req, status: accepted) — missing: unit-verification, sw-integration-verification
  SR-2 (sw-req, status: accepted) — missing: unit-verification, sw-integration-verification
  SR-3 (sw-req, status: accepted) — missing: unit-verification, sw-integration-verification
  SR-4 (sw-req, status: accepted) — missing: unit-verification, sw-integration-verification
  SR-5 (sw-req, status: accepted) — missing: unit-verification, sw-integration-verification
  SR-6 (sw-req, status: accepted) — missing: unit-verification, sw-integration-verification
  SR-7 (sw-req, status: accepted) — missing: unit-verification, sw-integration-verification
  SR-8 (sw-req, status: accepted) — missing: unit-verification, sw-integration-verification
  SR-9 (sw-req, status: accepted) — missing: unit-verification, sw-integration-verification
  SYS-1 (system-req, status: accepted) — missing: sys-integration-verification
  SYS-2 (system-req, status: accepted) — missing: sys-integration-verification
  SYS-3 (system-req, status: accepted) — missing: sys-integration-verification
  SYS-4 (system-req, status: accepted) — missing: sys-integration-verification
  SYS-5 (system-req, status: accepted) — missing: sys-integration-verification
  → run `rivet validate --explain SR-1` to see which link type and source types satisfy a gap

Result: PASS (117 warnings)
Schemas: common@0.3.0 (embedded), dev@0.3.0 (embedded), research@0.1.0 (embedded), research-ext@0.1.0 (on-disk), safety-case@0.1.0 (embedded), aspice@0.2.0 (embedded)

Artifact stats

base head
Total artifacts 222 223
full stats — head
Artifact summary:
  academic-reference               24
  competitive-analysis             11
  design-decision                  21
  feature                          71
  market-finding                    7
  requirement                      20
  safety-context                    3
  safety-goal                       5
  safety-justification              3
  safety-solution                   6
  safety-strategy                   1
  stakeholder-req                   3
  sw-req                           13
  sw-verification                  13
  sys-verification                  5
  system-req                        5
  technology-evaluation            12
  TOTAL                           223

Orphan artifacts (no links): 11
  CA-001
  CA-002
  CA-003
  CA-004
  CA-005
  CA-006
  CA-007
  CA-008
  CA-009
  CA-010
  CA-011

Diagnostics: 0 error(s), 119 warning(s), 18 info(s)

Diff (base → head)

+ DD-021  v3.3 — Adjudicating a NON-INJECTIVE identity: three keys, and `discharged` as a certainty claim

1 added, 0 removed, 0 modified, 222 unchanged

~ NEW    WARN: [DD-021] prose mentions 'FEAT-064' but no typed link to it; add a link in `links:` or remove the mention
~ NEW    WARN: [DD-021] prose mentions 'FEAT-040' but no typed link to it; add a link in `links:` or remove the mention
0 new errors, 0 resolved errors, 2 new warnings, 0 resolved warnings

AADL model — head

spar/scry.aadl: OK

Posted by the rivet-delta workflow. Informational only — does not gate the PR.

avrabe added a commit that referenced this pull request Aug 20, 2026
…and disclose

Caught in review before publication. The claim shipped in the previous commit —
"a singleton ordinal domain provably cannot alias" — is wrong, and it is wrong in
exactly the way DD-021 was wrong. It reasons from the one theorem that happens to
be proven rather than from what aliasing is, and a proof of ONE sufficient
condition does not enumerate them.

The counterexample, now pinned as
`feat072_region_shift_is_not_certified_as_identity_held`:

  group_key hashes the region PATH, and a path is a sibling index at its depth.
  Delete a whole region and its later siblings renumber. A surviving region moves
  into the deleted region's path, and its sole operator inherits the ENTIRE key —
  while both domains remain singletons, so the ordinal check sees nothing.

Measured on the fixture: an unproven `div_s` deleted with its block, the
proven-safe `div_s` from the next block inheriting its identity, reported
ordinal_stable=1 and counted as an attributable change. From the keys alone, an
obligation removed by deleting its region is indistinguishable from one that was
fixed. That is survivor_inherits_deleted_identity one level up the key — the same
failure class that got #120 withdrawn.

Changes:
  * `AliasFree` → `OrdinalStable`, `alias_free()` → `ordinal_stable()`,
    `attributable_changes()` → `ordinal_stable_changes()`. The name now states
    the scope: sibling-ORDINAL donation is excluded, and nothing else is.
  * The page says plainly that this is NOT a certificate that identity held, and
    explains the region-path shift that defeats it.
  * The grep gate now asserts the DISCLOSURE is present, not only that the
    over-claim is absent. A number a reader cannot calibrate is worse than none.
  * The limitation is pinned by a characterization test rather than hidden: if a
    future change makes that count 0, the test fails so the page's disclosure is
    updated in the same commit instead of warning about a hazard that is gone.

Also corrected a public claim I had just introduced. The obligation anchor's
tooltip read "stable obligation id — survives edits elsewhere in the module" —
precisely what scry#123 falsified for Rust-produced modules, rendered into the
published dashboard. Now scoped, and it cites #123. Swept README, docs/ and
render_scope for other identity-stability wording: none found (FEAT-064 has no
public documentation yet).

DD-022 records this as a PATTERN, which is the durable part: every attempt to
certify identity from the KEYS ALONE has now failed at three different levels —
the ordinal (DD-021), the region path (here), and the function ident (scry#123).
The keys are a good address and a bad proof. Corroboration has to come from
CONTENT.

136 tests pass; clippy -D warnings clean; rivet validate PASS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkNzkNYzPh7366DkNijeNc
avrabe added a commit that referenced this pull request Aug 21, 2026
…easured (#124)

* plan: split REQ-020, unblock v3.3.0, and add the dogfood + visualization features

v3.3.0 was uncuttable for a TRACEABILITY reason, not a code reason. REQ-020
is written as a conjunction — "stable obligation identity + a machine-checkable
oracle" — and the oracle half (FEAT-065) was refuted in review before it
shipped (six wrong-verdict paths, scry#122; DD-021's rationale retracted in
place). A conjunctive requirement cannot be half-discharged, so the identity
half was being held hostage to a design defect it does not share.

Split rather than reword. Rewording REQ-020 to hide its second half would be
the artifact-level version of the four vacuous oracles written this month.

  REQ-020 (v3.3.0)  identity an agent can CITE — verifiable on its own
  REQ-021 (v3.4.0)  the GATE, with an adversarial acceptance bar drawn
                    directly from scry#122: self-comparison yields only
                    still-open; deletion never yields discharged; a
                    cardinality-preserving delete+insert yields uncertain;
                    a newly introduced fault always yields a verdict.
                    Each case must be shown RED against the pre-rework
                    adjudicator before it is fixed.

FEAT-065 follows REQ-021 to v3.4.0. FEAT-066's `verify` MCP tool follows it —
exposing a refuted adjudicator over MCP would put a wrong verdict directly
into an agent's tool loop, so the v3.3.0 server omits the tool rather than
documenting a caveat.

Two new v3.3.0 features, neither depending on the adjudicator:

  FEAT-072  Obligation anchors + the delta view. FEAT-064 currently ships a
            key that nothing displays and no consumer can cite; the dashboard
            still hands out (func_index, pc) references it knows will break.
            Anchors ship unconditionally. The delta view renders the
            ADJUDICABLE FRACTION and suppresses the discharge count while
            scry#122 is open, enforced by a test that greps the page.

  FEAT-073  Self-adjudication harness — scry adjudicates scry across its own
            history, in observation mode. Purpose is evidence: scry#122 lists
            nine work items and nothing says which fire on real code. The
            harness exits 0 unconditionally and stamps scry#122 into every
            artifact, so observation mode is a property of the code rather
            than a convention a later edit can drop.

DD-022 records all three decisions with the alternatives rejected, and states
the separation the plan depends on: scale does not substitute for adversarial
reading. The six findings came from reading, not volume, and a
6,191-obligation run producing plausible numbers has exactly the shape of a
vacuous oracle. FEAT-073 measures WHICH failures occur; REQ-021's bar
establishes THAT each is fixed. Neither is evidence for the other.

DD-021 lands here in its already-retracted form: a refuted design decision
belongs in the design record whether or not its code ever merges.

rivet validate: PASS. v3.3.0 is now free of the adjudicator; v3.4.0 carries
REQ-021 + FEAT-065 + FEAT-057.

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

* FEAT-068/072/073: make obligation identity visible, citable, and measured

Three surfaces that make FEAT-064's identity real, none of which depends on
the adjudicator refuted in scry#122.

FEAT-068 — guidance.json v2. The feed shipped un-versioned and carried no
identity at all, so an agent could not tell an OLD PRODUCER from a module with
no such finding, and identity that existed only in Rust structs could not cross
a process boundary. Adds `guidance_schema: 2` plus `obligation_id`, `site_key`
and `group_key` on every advisory.

`site_key` / `group_key` are lifted onto main from the withdrawn FEAT-065
branch. Their DERIVATION was never what the review refuted — the refuted part
was the inference "group set unchanged, therefore safe to claim a discharge".
The keys are shipped here as data, with the docs saying plainly that nothing in
this release may conclude a discharge from them.

FEAT-072 — anchors + the delta view.
  * Every advisory row is now `id="ob-<obligation_id>"` with a permalink. Until
    now the only handle a consumer could quote was `fn{index}:{pc}` — precisely
    the key that shifts on the next edit. The dashboard was handing out
    references it knew would break.
  * `scry-viz delta <before.wasm> <after.wasm>` compares two analyses BY
    IDENTITY. It takes modules rather than feeds so the comparison cannot drift
    from the producer's key derivation.

The delta view makes exactly one soundness claim, and it is a new one:
ALIAS-FREE. `ObligationId.v` proves aliasing needs a deleted same-kind sibling
to inherit an ordinal from (survivor_inherits_deleted_identity), so an ordinal
domain holding one member in BOTH runs has nothing to inherit from. That is
checkable, and it is the honest headline — not "K adjudicable" but "K provably
alias-free, the rest not excluded". Note this is strictly stronger than DD-021's
refuted rule: a singleton domain rules aliasing out; an unchanged domain does not.

No discharge count is published, enforced by a test that greps the rendered page
rather than by reviewer discipline (DD-022).

FEAT-073 — scripts/self-history.sh, the self-adjudication harness. Builds
scry_mcdc.wasm at the last N commits and compares consecutive pairs. Exits 0
unconditionally; every artifact names scry#122. Observation mode is a property
of the code, not a convention a later edit can drop. Carries a self-comparison
control so a clean run can never silently be an empty one.

Test discipline: the four new oracles were mutation-checked, not just run.
Forcing `alias` to AliasFree kills only the alias test; deleting the anchor
kills only the two anchor tests. Both vacuity directions are covered — a
self-comparison must show no change AND a known-different pair must show one,
so "no changes" can never be the silent result of a broken match.

134 tests pass; clippy -D warnings clean across the CI package set.

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

* FEAT-072/073: publish the delta view, wire the harness into CI, record scry#123

CI (observation mode). The harness runs on every build over the last 3 commits
and uploads its report. It never gates: the script exits 0 by construction and
`continue-on-error` is belt to that braces. `if-no-files-found: warn`, not
`error` — an observation that produced nothing is a fact to report, and `error`
would turn a non-gating harness into a gate through the back door.

Pages. The self-analysis page now carries obligation anchors automatically, and
release.yml renders a release-over-release delta against the previous tag,
linked from the landing page when present. `continue-on-error`: an unbuildable
previous tag is a fact about the history, not a reason to block a release.

Fixed a defect in my own page before it shipped. The moved-sites table filtered
on `changed()` — true for gone and new rows too — while the summary counted
in-place changes only, so the page could announce "7633 changed" directly above
a summary saying "0 changed". Conflating vanished / new / changed-in-place is
precisely how a delta misleads, so the table now carries an explicit `fate`
column and a reconciliation test asserts moved == gone + new + changed with the
three buckets disjoint.

MEASURED — scry#123, and it reorders the rework. The harness's first run found a
defect on NEITHER scry#122's nine items nor DD-020's limitations: 43-45% of
function identities churn per build. Rust legacy mangling ends in `17h<16 hex>E`,
a symbol disambiguator derived from crate metadata and instantiation rather than
from the function body, and FEAT-064 prefers the name-section name. Across
582cfb0→524b3e0b, 334 of 744 functions carrying advisories lost EVERY obligation
identity, 410 kept every one, and ZERO were partial.

That is upstream of scry#122: content-corroborating `site_key` cannot help while
`func_ident` — hashed in first — churns. Both repairs fail in opposite
directions (raw: 1.2% collision / 43% churn; stripped: 0% churn / 36% collision,
max 36 functions per name), so #123 states the measured trade and deliberately
recommends nothing.

FEAT-064's AC#1 — "an edit in an unrelated function leaves every obligation ID
outside it unchanged" — is recorded as FALSIFIED IN PRACTICE on Rust-produced
modules. It passes the hand-written WAT fixture and fails on real toolchain
output. Third FEAT-064 claim refuted after being written, and the first the
fixture suite structurally could not have caught, because the defect is in the
producer's output rather than in scry's logic. This blocks calling REQ-020
verified; it does not affect what has landed.

The harness also KILLED one of my own explanations before it was published: I
expected ordinal shifts inside surviving functions to masquerade as gone+new.
The discriminating probe found zero partial-overlap functions, so ordinal shift
contributed nothing. "A fix cannot present as `changed`" is therefore recorded
as an OPEN HYPOTHESIS with a named probe, not as a finding — across 7 pairs zero
shared sites changed, but that may be a fact about the sample rather than a
structural one.

DD-022 and FEAT-072 corrected from "adjudicable fraction" to "alias-free
fraction": nothing here certifies adjudicability, but a singleton ordinal domain
provably cannot alias. Strictly stronger than DD-021's refuted rule.

135 tests pass; clippy -D warnings clean; rivet validate PASS; both workflows parse.

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

* FEAT-072: the alias-free claim was unsound — rename to OrdinalStable and disclose

Caught in review before publication. The claim shipped in the previous commit —
"a singleton ordinal domain provably cannot alias" — is wrong, and it is wrong in
exactly the way DD-021 was wrong. It reasons from the one theorem that happens to
be proven rather than from what aliasing is, and a proof of ONE sufficient
condition does not enumerate them.

The counterexample, now pinned as
`feat072_region_shift_is_not_certified_as_identity_held`:

  group_key hashes the region PATH, and a path is a sibling index at its depth.
  Delete a whole region and its later siblings renumber. A surviving region moves
  into the deleted region's path, and its sole operator inherits the ENTIRE key —
  while both domains remain singletons, so the ordinal check sees nothing.

Measured on the fixture: an unproven `div_s` deleted with its block, the
proven-safe `div_s` from the next block inheriting its identity, reported
ordinal_stable=1 and counted as an attributable change. From the keys alone, an
obligation removed by deleting its region is indistinguishable from one that was
fixed. That is survivor_inherits_deleted_identity one level up the key — the same
failure class that got #120 withdrawn.

Changes:
  * `AliasFree` → `OrdinalStable`, `alias_free()` → `ordinal_stable()`,
    `attributable_changes()` → `ordinal_stable_changes()`. The name now states
    the scope: sibling-ORDINAL donation is excluded, and nothing else is.
  * The page says plainly that this is NOT a certificate that identity held, and
    explains the region-path shift that defeats it.
  * The grep gate now asserts the DISCLOSURE is present, not only that the
    over-claim is absent. A number a reader cannot calibrate is worse than none.
  * The limitation is pinned by a characterization test rather than hidden: if a
    future change makes that count 0, the test fails so the page's disclosure is
    updated in the same commit instead of warning about a hazard that is gone.

Also corrected a public claim I had just introduced. The obligation anchor's
tooltip read "stable obligation id — survives edits elsewhere in the module" —
precisely what scry#123 falsified for Rust-produced modules, rendered into the
published dashboard. Now scoped, and it cites #123. Swept README, docs/ and
render_scope for other identity-stability wording: none found (FEAT-064 has no
public documentation yet).

DD-022 records this as a PATTERN, which is the durable part: every attempt to
certify identity from the KEYS ALONE has now failed at three different levels —
the ordinal (DD-021), the region path (here), and the function ident (scry#123).
The keys are a good address and a bad proof. Corroboration has to come from
CONTENT.

136 tests pass; clippy -D warnings clean; rivet validate PASS.

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

* rivet: give FEAT-073 a typed link to DD-022

CI's artifact validation flagged a trace gap this branch introduced: FEAT-073's
prose names DD-022 as the decision that makes it observation-only, but nothing
in `links:` said so. Prose is not traceability — a mention the graph cannot see
does not re-open the right decision when the feature regresses.

Also tried a reverse `verified-by` edge from REQ-021 and reverted it: the type
is not in the schema, and FEAT-073 already declares `traces-to: REQ-021`, which
is the direction the schema models. Inventing vocabulary to silence a nag would
have recorded a relation the tooling cannot check.

rivet validate: PASS, 130 warnings (was 131).

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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.

1 participant