Skip to content

FEAT-087 (scry#122): surviving your own edit is a second, independent identity bit - #162

Merged
avrabe merged 3 commits into
mainfrom
feat-087-survives-own-edit
Aug 27, 2026
Merged

FEAT-087 (scry#122): surviving your own edit is a second, independent identity bit#162
avrabe merged 3 commits into
mainfrom
feat-087-survives-own-edit

Conversation

@avrabe

@avrabe avrabe commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Closes work item 2 of #122.

The finding

id_build_local is a one-bit answer to a three-way question. It correctly
answers "are these keys comparable across builds at all?" — and cannot answer
"does this identity survive an edit to the very function being fixed?"

tier ident comparable? survives own edit?
1 unique stripped name stripped name yes yes
2 shared stripped → raw raw name NO (marked) yes
3 name, no disambiguator raw name yes yes
4 unnamed body_shape_hash yes NO

Tier 4 carried the same false as tiers 1/3 while failing the property
adjudication depends on.

Measured

On a stripped build of scry's own scry_mcdc.wasm (wasm-tools strip -a,
8.2MB → 484KB, 853 functions / 32 exported):

  • 766 of 766 obligation-carrying functions land on the body-shape-hash tier
  • all 10,520 obligations were advertised as a stable identity

Stripping is standard in release builds, so this is the common production shape,
not a corner case. Minimal fixture: one unexported function, one appended
local.get 0 / i32.addsite_key 8b27dcd0 → 0df9e454, survival 0 of 1.

What this is NOT

Marking tier 4 id_build_local: true. A body-shape hash genuinely is
cross-build comparable — same body, same hash — so that bit is correct as it
stands. Flipping it suppresses cross-build citation for 100% of every stripped
module to fix a different property, gutting REQ-020 for the common release case.

The fix

A second, independent bit ident_survives_own_edit, named after what it
licenses rather than the mechanism that produced it (DD-022: consumers read
mechanism names as stability rankings and get it wrong).

Consumer rule — the conjunction: treat a missing key in a later build as
evidence the site is gone only when !id_build_local && ident_survives_own_edit.
Otherwise the honest verdict is uncertain, never discharged (REQ-021).

Surfaced end-to-end: Advisory, guidance feed v4, and an id moves on own edit HTML badge. After the change: schema 4, 10,519/10,520 marked false; the
single true is the module-scoped unbounded-stack, ident <module>, which
genuinely is body-independent.

Honest scope

This fixes the contract before the consumer lands. FEAT-065
(verify_against) is still proposed, #120 is parked, and nothing on main turns
a missing key into discharged — so this is not a shipped false-discharge. The
reason to do it now is that the flag is already public in feed v3, so every v3
consumer inherits the ambiguity.

The 100% is scoped to stripped modules, measured on scry's own binary. Whether
avrabe's 468-module corpus (#126) is stripped is a separate measurement and is
not claimed.

NOT COVERED, stated rather than implied: the FEAT-072 delta view does not
yet carry the new bit — and that page is exactly where a consumer reads "this
site vanished". Next slice; recorded in the artifact residual.

Oracles

Red-first: seeded true at all five construction sites → failed at
lib.rs:10823 on the tier-4 assertion, with both advisories located (so the red
was the contract, not a missing fixture).

Mutation-checked both directions:

mutant kills survives
field ≡ true (pre-fix) discrimination only 109
field ≡ false discrimination + independence 108
baseline 110 ✓

The strict superset is the signature of two genuinely independent bits — a
rename of id_build_local would kill identical sets. The six pre-existing
assert!(!a.id_build_local) assertions stay green under both mutants; they gate
nothing here.

Also de-tautologised FEAT-068's schema assertion: the bump turned
contains("guidance_schema":3) red, and the obvious repair (compare against
GUIDANCE_SCHEMA_VERSION) is vacuous because the feed is emitted from that
constant — the class that shipped scry 0.0.0 (#152). It now parses the declared
integer structurally and floors it at 2, mutation-checked: renaming the key kills
it, regressing the version to 1 kills it.

tests=0 clippy=0 fmt=0 rivet=0 claim-check=0 gate-coverage=0 — 303 tests.

⚠️ Per #130 this repo has no required status checks; gh pr checks must be
verified by hand before merge.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KkNzkNYzPh7366DkNijeNc

scry#122 item 2 asks that an adjudicator never claim `discharged` when the
function identity came from `body_shape_hash`. The obvious repair — mark that
tier `id_build_local: true` — is wrong in the other direction, and expensively:
a body-shape hash IS cross-build comparable (same body, same hash), so marking
it build-local would suppress cross-build citation for 100% of every stripped
module to fix a different property, gutting REQ-020 for the common release case.

`id_build_local` is a one-bit answer to a three-way question. Two properties:

  tier                          comparable?  survives own edit?
  1 unique stripped name        yes          yes
  2 shared stripped -> raw      NO (marked)  yes  (name is body-independent)
  3 name, no disambiguator      yes          yes
  4 unnamed -> body_shape_hash  yes          NO

Only the first was encoded. This adds the second, `ident_survives_own_edit`,
named after what it LICENSES rather than the mechanism that produced it —
DD-022's standing lesson is that consumers read mechanism names as rankings.
The consumer rule is the conjunction: read a missing key as evidence a site is
gone only when `!id_build_local && ident_survives_own_edit`; else `uncertain`.

MEASURED, on a stripped build of scry's own scry_mcdc.wasm (wasm-tools strip -a,
8.2MB -> 484KB, 853 funcs / 32 exported): 766 of 766 obligation-carrying
functions and all 10,520 obligations land on the body-shape-hash tier, every one
previously advertised as a stable identity. Stripping is standard in release
builds, so this is the common production shape. After: schema 4, 10,519 of
10,520 marked false; the single `true` is the module-scoped `unbounded-stack`,
whose ident is the literal `<module>` and IS body-independent.

HONEST SCOPE: this fixes the CONTRACT BEFORE THE CONSUMER LANDS. FEAT-065
(`verify_against`) is still `proposed`, PR #120 is parked, and nothing on main
turns a missing key into `discharged`. The reason to do it now is that the flag
is already public in feed v3, so every v3 consumer inherits the ambiguity.
The 100% is scoped to stripped modules on scry's own binary; whether avrabe's
468-module corpus (scry#126) is stripped is NOT claimed here.

NOT COVERED, stated rather than implied: the FEAT-072 delta view still does not
carry the new bit, and that page is exactly where a consumer reads "this site
vanished". Next slice.

Red-first: seeded `true` at all five construction sites, failed at lib.rs:10823
on the tier-4 assertion with both advisories located. Mutation-checked both
ways: field forced `true` kills only the discrimination test (109 pass); forced
`false` kills discrimination AND tier-2 independence (108); baseline 110. The
strict superset is the signature of two independent bits. The six pre-existing
`assert!(!a.id_build_local)` assertions stay green under both — they gate
nothing here.

Also de-tautologised FEAT-068's schema assertion. The version bump turned
`contains("guidance_schema":3)` red; repairing it by comparing against
GUIDANCE_SCHEMA_VERSION would be vacuous, since the feed is emitted FROM that
constant (the class that shipped `scry 0.0.0`, scry#152). It now parses the
declared integer and floors it at 2, mutation-checked: renaming the key kills
it, regressing the version to 1 kills it.

tests=0 clippy=0 fmt=0 rivet=0 claim-check=0 gate-coverage=0.

Claude-Session: https://claude.ai/code/session_01KkNzkNYzPh7366DkNijeNc

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
avrabe and others added 2 commits August 27, 2026 02:45
Review catch. The one advisory reporting `ident_survives_own_edit: true` on the
stripped module is the module-scoped `unbounded-stack`, and I explained it as
"`<module>` is body-independent, so true is correct". The value was right and the
reasoning was right, but the CODE had not decided it: the module-scoped loop in
stamp_obligation_ids sets the three identity keys and never touched this field,
so the observed `true` was simply the construction-site default I seeded.

An artifact AC then asserted the field "discriminates even within one module" on
the strength of a default. That is a claim about a decision that was not made.

Now assigned deliberately in that loop, and pinned by
feat087_a_module_scoped_ident_survives_any_edit. Mutation-checked: forcing the
new line false kills exactly that test (2 pass, 1 fail) and restoring it is
green. The AC records the correction rather than quietly reading correct.

tests=0 clippy=0 fmt=0 rivet=0 claim-check=0 gate-coverage=0.

Claude-Session: https://claude.ai/code/session_01KkNzkNYzPh7366DkNijeNc

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…FEAT-077

Bumping the guidance feed 3 -> 4 makes two written claims literally false. Both
are in roadmap-3.0.yaml and neither would have been caught by a gate — rivet
validates structure, not whether an AC still describes reality.

  FEAT-068 AC: "VERIFIED on a real feed: guidance_schema=3, ..."
  FEAT-077 AC: "Then `guidance_schema` is 3 and every advisory carries ..."

FEAT-068's is a MEASUREMENT RECORD being read as a live claim. The requirement
was always that the feed DECLARES a version, not that the number equals 3; the
number was the observed value. Reworded to say so, and to record that its oracle
parses the declared integer structurally rather than comparing it against
GUIDANCE_SCHEMA_VERSION (which is tautological — the feed is emitted from that
constant).

FEAT-077's is an AC that would now FAIL. Reworded to "at least 3 — exactly 3
when this shipped, 4 since FEAT-087".

Also promotes FEAT-077 implemented -> accepted. `implemented` counts as
NOT-yet-verified in the release gate, so a shipped, green feature left there
blocks its release indefinitely — v3.4.0 has been carrying it. Evidence: all six
FEAT-077 oracles green now (unique-stripped-ident survival, the two collision
cases, exact-disambiguator-shape, and both byte-identical v3.2.5 regression
pins), plus the five mutants recorded in the artifact. v3.4.0 not-ready 4 -> 3
of the pre-existing set.

tests=0 clippy=0 fmt=0 rivet=0 claim-check=0 gate-coverage=0.

Claude-Session: https://claude.ai/code/session_01KkNzkNYzPh7366DkNijeNc

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

📐 rivet artifact delta

PR: #162 Base SHA: ed1f99b7

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 (146 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: 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 (142 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 255 257
full stats — head
Artifact summary:
  academic-reference               24
  competitive-analysis             11
  design-decision                  22
  feature                          87
  market-finding                    7
  requirement                      21
  safety-context                    3
  safety-goal                       5
  safety-justification              4
  safety-solution                   6
  safety-strategy                   1
  stakeholder-req                   3
  sw-req                           13
  sw-verification                  13
  sys-verification                  5
  system-req                        5
  technology-evaluation            12
  verification                     15
  TOTAL                           257

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

Diagnostics: 0 error(s), 146 warning(s), 20 info(s)

Diff (base → head)

+ FEAT-086  v3.2.7 — Clippy covers every crate, and the coverage guard covers BOTH gates (scry#157)
+ FEAT-087  v3.4 — Identity that survives an edit to its OWN body is a second, independent bit (scry#122 item 2)
~ FEAT-068
  field changed: acceptance-criteria
~ FEAT-077
  status: - implemented -> + accepted
  field changed: acceptance-criteria
~ FEAT-083
  status: - proposed -> + accepted

2 added, 0 removed, 3 modified, 252 unchanged

~ NEW    WARN: [FEAT-087] prose mentions 'FEAT-077' but no typed link to it; add a link in `links:` or remove the mention
~ NEW    WARN: [FEAT-087] prose mentions 'DD-022' but no typed link to it; add a link in `links:` or remove the mention
~ NEW    WARN: [FEAT-087] prose mentions 'FEAT-065' but no typed link to it; add a link in `links:` or remove the mention
~ NEW    WARN: [FEAT-087] prose mentions 'FEAT-072' but no typed link to it; add a link in `links:` or remove the mention
0 new errors, 0 resolved errors, 4 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
avrabe merged commit 265725e into main Aug 27, 2026
11 checks passed
@avrabe
avrabe deleted the feat-087-survives-own-edit branch August 27, 2026 03:22
avrabe added a commit that referenced this pull request Aug 27, 2026
… 7 -> 4

`implemented`/`proposed` count as NOT-yet-verified in the release gate, so a
shipped, green feature left there blocks its release indefinitely. These three
have shipped AND been verified, so leaving them proposed understates the release
just as promoting them early would overstate it.

Evidence for each, grounded rather than remembered:

  FEAT-087 (#162, 11/11 CI green) -- 3 oracles re-run on main just now:
    the tier-discrimination test, the tier-2 independence pin, and the
    module-scoped decision. Mutation-checked both directions when landed.
  FEAT-088 (#163, 11/11 CI green) -- the gate's --self-test PASSES and the
    real check PASSES on main, and it is wired into ci.yml (self-test before
    the real check). Mutation-checked against the real repo when landed.
  FEAT-090 (#166, 11/11 CI green) -- 5 oracles re-run on main just now,
    including the polarity test that dies only under the `&=` -> `|=` mutant.

NOT promoted, deliberately:
  FEAT-089 -- FILED, not built. The non-zero-fact work does not exist yet; its
    own AC#1 requires a red test that is still red by design.
  FEAT-064 -- AC3 closed by measurement in #164, but AC1 remains FALSIFIED
    (repaired by FEAT-077 only for uniquely-named functions). Closing one AC
    does not clear the artifact, and REQ-020 stays blocked behind it.
  FEAT-057, FEAT-065, REQ-021 -- unbuilt.

rivet=0 claim-check=0 fmt=0.

Refs: FEAT-087

Claude-Session: https://claude.ai/code/session_01KkNzkNYzPh7366DkNijeNc

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
avrabe added a commit that referenced this pull request Aug 27, 2026
… 7 -> 4

`implemented`/`proposed` count as NOT-yet-verified in the release gate, so a
shipped, green feature left there blocks its release indefinitely. These three
have shipped AND been verified, so leaving them proposed understates the release
just as promoting them early would overstate it.

Evidence for each, grounded rather than remembered:

  FEAT-087 (#162, 11/11 CI green) -- 3 oracles re-run on main just now:
    the tier-discrimination test, the tier-2 independence pin, and the
    module-scoped decision. Mutation-checked both directions when landed.
  FEAT-088 (#163, 11/11 CI green) -- the gate's --self-test PASSES and the
    real check PASSES on main, and it is wired into ci.yml (self-test before
    the real check). Mutation-checked against the real repo when landed.
  FEAT-090 (#166, 11/11 CI green) -- 5 oracles re-run on main just now,
    including the polarity test that dies only under the `&=` -> `|=` mutant.

NOT promoted, deliberately:
  FEAT-089 -- FILED, not built. The non-zero-fact work does not exist yet; its
    own AC#1 requires a red test that is still red by design.
  FEAT-064 -- AC3 closed by measurement in #164, but AC1 remains FALSIFIED
    (repaired by FEAT-077 only for uniquely-named functions). Closing one AC
    does not clear the artifact, and REQ-020 stays blocked behind it.
  FEAT-057, FEAT-065, REQ-021 -- unbuilt.

rivet=0 claim-check=0 fmt=0.

Refs: FEAT-087

Claude-Session: https://claude.ai/code/session_01KkNzkNYzPh7366DkNijeNc

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
avrabe added a commit that referenced this pull request Aug 27, 2026
… 7 -> 4

`implemented`/`proposed` count as NOT-yet-verified in the release gate, so a
shipped, green feature left there blocks its release indefinitely. These three
have shipped AND been verified, so leaving them proposed understates the release
just as promoting them early would overstate it.

Evidence for each, grounded rather than remembered:

  FEAT-087 (#162, 11/11 CI green) -- 3 oracles re-run on main just now:
    the tier-discrimination test, the tier-2 independence pin, and the
    module-scoped decision. Mutation-checked both directions when landed.
  FEAT-088 (#163, 11/11 CI green) -- the gate's --self-test PASSES and the
    real check PASSES on main, and it is wired into ci.yml (self-test before
    the real check). Mutation-checked against the real repo when landed.
  FEAT-090 (#166, 11/11 CI green) -- 5 oracles re-run on main just now,
    including the polarity test that dies only under the `&=` -> `|=` mutant.

NOT promoted, deliberately:
  FEAT-089 -- FILED, not built. The non-zero-fact work does not exist yet; its
    own AC#1 requires a red test that is still red by design.
  FEAT-064 -- AC3 closed by measurement in #164, but AC1 remains FALSIFIED
    (repaired by FEAT-077 only for uniquely-named functions). Closing one AC
    does not clear the artifact, and REQ-020 stays blocked behind it.
  FEAT-057, FEAT-065, REQ-021 -- unbuilt.

rivet=0 claim-check=0 fmt=0.

Refs: FEAT-087

Claude-Session: https://claude.ai/code/session_01KkNzkNYzPh7366DkNijeNc

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
avrabe added a commit that referenced this pull request Aug 27, 2026
… 7 -> 4 (#173)

`implemented`/`proposed` count as NOT-yet-verified in the release gate, so a
shipped, green feature left there blocks its release indefinitely. These three
have shipped AND been verified, so leaving them proposed understates the release
just as promoting them early would overstate it.

Evidence for each, grounded rather than remembered:

  FEAT-087 (#162, 11/11 CI green) -- 3 oracles re-run on main just now:
    the tier-discrimination test, the tier-2 independence pin, and the
    module-scoped decision. Mutation-checked both directions when landed.
  FEAT-088 (#163, 11/11 CI green) -- the gate's --self-test PASSES and the
    real check PASSES on main, and it is wired into ci.yml (self-test before
    the real check). Mutation-checked against the real repo when landed.
  FEAT-090 (#166, 11/11 CI green) -- 5 oracles re-run on main just now,
    including the polarity test that dies only under the `&=` -> `|=` mutant.

NOT promoted, deliberately:
  FEAT-089 -- FILED, not built. The non-zero-fact work does not exist yet; its
    own AC#1 requires a red test that is still red by design.
  FEAT-064 -- AC3 closed by measurement in #164, but AC1 remains FALSIFIED
    (repaired by FEAT-077 only for uniquely-named functions). Closing one AC
    does not clear the artifact, and REQ-020 stays blocked behind it.
  FEAT-057, FEAT-065, REQ-021 -- unbuilt.

rivet=0 claim-check=0 fmt=0.

Refs: FEAT-087

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