Skip to content

v0.86 NOTESPOP3 (#1259): the notes guard was potent only where it never runs — bound the head extract - #1528

Closed
avrabe wants to merge 2 commits into
mainfrom
v086-notespop3
Closed

avrabe wants to merge 2 commits into
mainfrom
v086-notespop3

Conversation

@avrabe

@avrabe avrabe commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

Paired controls first — and they found the guard inert in both environments

The artifact's refuting command required running the generator with and without the foreign-artifact cache, with paired controls. The result is sharper than the claim the lane was built on. The anti-vacuity guard fires only on a totally empty added set, and against v0.85.0:

environment added guard
synced checkout, foreign cache present 3219 silent
clean checkout, no foreign cache 18 silent

Only 18 of those 3219 are this repository's. The other 3201 are the federated sibling checkouts under .rivet/repos — gale, jess, kiln, loom, meld, scry, sigil.

The positive control is what makes it a potency finding

Simulating a total breakage of our own extract with --base HEAD, so that nothing of this repository's own is added:

environment rc added guard
clean checkout 1 0 FIRES
synced checkout 0 3201 — and zero of them ours SILENT

Same breakage, opposite verdicts. The guard is demonstrably potent in an environment where the generator never runs, and demonstrably inert in the only environment where it does — and the inert case does not merely fail to warn, it emits a notes section claiming 3201 added artifacts, none of which are ours. No workflow invokes the generator; CI runs its unit test alone.

Root cause: an asymmetry, not a missing check

The base side is a git archive of the configured source roots, so it can never contain .rivet/repos. The head side was the whole working tree. Every foreign artifact therefore read as "added".

The fix

Bound the head side the same way — from the working tree rather than from HEAD, because notes are written before the release commit exists and must describe the tree being cut. Each side gets its own directory, so neither can read the other's siblings.

That single change delivers all three parts of the derived remedy: bound the extract, separate the directories, and make the guard reachable.

after rc added guard
synced / v0.85.0 0 18 silent (correct: 18 shipped)
clean / v0.85.0 0 18 silent (correct)
synced / HEAD (breakage) 1 0 FIRES ← was silent at 3201
clean / HEAD (breakage) 1 0 FIRES (unchanged)

Both environments now emit identical added lists.

Negative control, executed

Restoring the head side to the raw repo root reds the new test with its own message, and reverts the live generator to exit 0 / 3201 added / silent on the breakage case. Restored — both green again.

The test asserts the property, not the count: the path handed to rivet diff --head is not the repo root, and what it contains is the source roots without the sibling cache. It records the head's contents at call time, because the generator builds its extracts in a TemporaryDirectory — a test that looked afterwards would pass vacuously. Suite 11 → 12 tests, green, already CI-wired.

Refs #1259

🤖 Generated with Claude Code

https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L

avrabe and others added 2 commits October 14, 2026 07:42
…er runs - bound the head extract

PAIRED CONTROLS FIRST, as the artifact requires, and they found the guard inert
in BOTH environments - which is sharper than the claim the lane was built on.

The anti-vacuity guard fires only on a totally empty `added` set. Measured
against v0.85.0:

  synced checkout, foreign cache present   3219 added   guard silent
  clean checkout,  no foreign cache          18 added   guard silent

Only 18 of those 3219 are this repository's. The other 3201 are the federated
sibling checkouts under .rivet/repos - gale, jess, kiln, loom, meld, scry, sigil.

THE POSITIVE CONTROL IS WHAT MAKES IT A POTENCY FINDING. Simulating a total
breakage of our own extract with `--base HEAD`, so that NOTHING of this
repository's own is added:

  clean checkout    guard FIRES, exit 1, 0 added
  synced checkout   guard SILENT, exit 0, 3201 added - and ZERO of them ours

Same breakage, opposite verdicts. So the guard is demonstrably potent in an
environment where the generator NEVER runs, and demonstrably inert in the only
environment where it does - and the inert case does not merely fail to warn, it
EMITS a notes section claiming 3201 added artifacts, none of which are ours.
No workflow invokes the generator; CI runs its unit test alone.

ROOT CAUSE, and it is an asymmetry rather than a missing check. The base side is
a `git archive` of the configured source roots, so it can never contain
.rivet/repos. The head side was the whole working tree. Every foreign artifact
therefore read as "added".

THE FIX is to bound the head side the same way, from the WORKING TREE rather
than from HEAD, because notes are written before the release commit exists and
must describe the tree being cut. Each side gets its own directory, so neither
can read the other's siblings. That single change delivers all three parts of
the derived remedy: bound the extract, separate the directories, and make the
guard reachable.

AFTER, the full matrix:

  synced / v0.85.0            exit 0   18 added   silent   (correct: 18 shipped)
  clean  / v0.85.0            exit 0   18 added   silent   (correct)
  synced / HEAD   breakage    exit 1    0 added   FIRES    was silent at 3201
  clean  / HEAD   breakage    exit 1    0 added   FIRES    unchanged

Both environments now emit IDENTICAL added lists.

NEGATIVE CONTROL on the new test, executed: restoring the head side to the raw
repo root REDS the test with its own message, and reverts the live generator to
exit 0 / 3201 added / silent on the breakage case. Restored, both green again.

The test asserts the PROPERTY, not the count: the path handed to `rivet diff
--head` is not the repo root, and what it contains is the source roots WITHOUT
the sibling cache. It records the head's contents at CALL time, because the
generator builds its extracts in a TemporaryDirectory - a test that looked
afterwards would pass vacuously. Suite 11 -> 12 tests, green, and already
CI-wired.

Refs #1259

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

avrabe commented Oct 14, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #1537, which carries this branch's content in full.

Why: a dependabot auto-merge moved main and put every lane PR behind=1. The merge gate's currency check (rev-list --count head..base must be zero) means merging any one lane PR re-stales the rest, so the six lanes were forced sequential — six rebases and six full CI cycles, with three more auto-merge-enabled dependabot PRs still queued to move main again.

The consolidation was verified by simulation before being chosen: the whole wave applied to a detached worktree off origin/main, the single ci.yml conflict resolved with both steps kept, and gated there at no CI cost — status_evidence 0 failures, claim_check 75/75, verdict_prose clean, and the window anti-vacuity guard untripped.

Nothing was dropped. The artifact records are byte-identical apart from the PR number they name, plus one correction the simulation surfaced: SPELLSCAN now carries issue-scope: outlives, because #1515's own subject is the SELFTEST2 vacuity and that lane is partial.

@avrabe avrabe closed this Oct 14, 2026
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