Skip to content

fix(release,guard): a short release cannot be signed; withdraw a false claim - #407

Merged
avrabe merged 1 commit into
mainfrom
fix/gate-evidence-batch1
Aug 7, 2026
Merged

fix(release,guard): a short release cannot be signed; withdraw a false claim#407
avrabe merged 1 commit into
mainfrom
fix/gate-evidence-batch1

Conversation

@avrabe

@avrabe avrabe commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Batch 1 of the multi-persona review of REQ-GUARD-GATE-EVIDENCE-002 — the three
changes that need no design debate. Refs #402, #405.

Four independent reviewers (safety assessor, adversarial verifier, defect
archaeologist, release manager) all recommended holding -002 at proposed.
This PR takes the uncontested subset; the two live obligation holes (#404, #385)
and the systemic layer (#403, #405, #406) follow separately.

1. release.yml can no longer publish a signed release with no binaries (#402)

find … -exec cp exits 0 when it matches nothing, and all six upstream
upload-artifact steps sat at the default if-no-files-found: warn. An empty or
partial artifacts/ therefore produced an empty release-assets/ in silence —
and sha256sum, the SLSA attestation, cosign, and gh release create all
succeed over nothing. The result is a signed, attested release containing no
binaries.

The gather is now counted against a floor, which is the shape the SBOM block
ten lines below already used
— the pattern was recognised once and not applied
to its five siblings. The floor is derived, not guessed: build-binaries has 5
matrix targets and build-vsix has 5, so a complete release carries at least 10
of these three extensions before wasm/compliance/evidence are counted.

All six uploads now declare if-no-files-found: error — the value
trace-fixtures.yml:453 already uses, so the repo demonstrably knew the
difference.

release.yml only runs on tags, so CI will never exercise this. The logic
was therefore exercised locally against four trees:

artifacts present exit
0 — the #402 scenario 1
4 — partial build 1
10 — complete release 0
13 — complete + extras 0

The pre-fix form exits 0 on all four.

2. ci.yml's mutants comment no longer contradicts the gate it calls

It still read "Nothing here yet fails a truncated run: that detector, and the
runner disk-pressure fault behind it, are #389"
— false since #401 merged two
commits earlier. This is precisely the "code drifted out from under its comment"
species that #385's own fix narrative identifies as its root cause, so leaving it
would be leaving the same trap re-armed.

The replacement also fixes the attribution. The disk-pressure hook was a red
herring
ACTIONS_RUNNER_HOOK_JOB_COMPLETED only runs between jobs. The
actual reaper was a tmpfiles sweep on a false premise: "a job that's still
running keeps mtime fresh"
, which does not hold for cargo-mutants, since it
copies each worker's source tree once at start and never touches it again.

3. A false universal in the requirement is withdrawn (#405)

I wrote this into REQ-GUARD-GATE-EVIDENCE-002 in #401:

"Each ships a self-test that runs BEFORE the gate it guards is allowed to judge
anything, and each was mutation-tested to show its cases are load-bearing
rather than merely green.
"

It is not true. Results exist for two of six, both manual shell runs with no
committed harness. An adversarial reviewer falsified the universal directly, and
I reproduced both mutants before writing this:

check_verification_filters.py:212   filt → filt.split('::')[-1]   13 passed → SURVIVES
check_lean_sorries.py               below-floor branch deleted     8 passed → SURVIVES

Mutant A is not cosmetic: 16 of the 60 distinct filters in
verification.yaml contain ::, and no self-test case has one, so the suite is
structurally blind to it.

The replacement text records the correlation rather than burying it: the two
tools that claimed mutation testing have the strongest suites; the two with live
holes (#404, #385) claimed nothing.
The claim was reached for where confidence
was already high, which is where it adds least. It also states what mechanising
it (#405) would not buy — mutation testing shows the cases present are
load-bearing, not that the case set covers the hazard.

Not in this PR

REQ-GUARD-GATE-EVIDENCE-002 stays proposed and keeps its human-scoped tag.
Nothing here promotes it.

🤖 Generated with Claude Code

…e claim

Batch 1 of the multi-persona review of REQ-GUARD-GATE-EVIDENCE-002. Three
changes, all of which correct something that currently renders "nothing
happened" as "it worked".

1. release.yml can no longer publish a signed release with no binaries (#402)

`find ... -exec cp` exits 0 when it matches nothing, and every one of the six
upstream upload-artifact steps sat at the default `if-no-files-found: warn`.
So an empty or partial artifacts/ produced an empty release-assets/ in
silence, and the checksum, attest, cosign and `gh release create` steps all
succeeded over nothing.

The gather is now counted against a floor, the same shape the SBOM block ten
lines below already used. The floor is derived rather than guessed:
build-binaries has 5 matrix targets and build-vsix has 5, so a complete
release carries at least 10 of these three extensions. All six uploads now
declare `if-no-files-found: error` — the value trace-fixtures.yml already
uses, so the repo knew the difference.

release.yml only runs on tags, so CI will never exercise this. The logic was
therefore exercised locally against four trees: 0 assets -> exit 1, 4 -> exit
1, 10 -> exit 0, 13 -> exit 0. The pre-fix form exits 0 on all four.

2. ci.yml's mutants comment no longer contradicts the gate it calls

It still read "Nothing here yet fails a truncated run: that detector, and the
runner disk-pressure fault behind it, are #389" — false since #401 merged two
commits earlier. This is the "code drifted out from under its comment"
species that #385's own fix narrative identifies as its root cause, so it is
worth not leaving in place. The replacement also corrects the attribution:
the disk-pressure hook was a red herring (it only runs BETWEEN jobs); the
reaper was a tmpfiles sweep resting on a false assumption about mtime.

3. A false universal in REQ-GUARD-GATE-EVIDENCE-002 is withdrawn (#405)

I wrote "each was mutation-tested to show its cases are load-bearing rather
than merely green" into the requirement in #401. It is not true. Results
exist for two of six, both manual shell runs with no committed harness, and
an adversarial review falsified the universal directly: substituting
`filt` -> `filt.split('::')[-1]` in check_verification_filters.py leaves
13/13 green while changing vacuity semantics for the 16 of 60 filters
containing `::`; deleting check_lean_sorries.py's below-floor branch leaves
8/8 green. Both reproduced before writing this.

The replacement records the correlation rather than hiding it: the two tools
that claimed mutation testing have the strongest suites, and the two with
live holes claimed nothing. It also states what mechanising it would NOT buy
— mutation testing shows the cases present are load-bearing, not that the
case set covers the hazard.

Refs #402, #405. REQ-GUARD-GATE-EVIDENCE-002.

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

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

Rivet verification gate

20/20 passed

count
Passed 20
Failed 0
Skipped (no steps) 0

Filter: (and (= type "feature") (or (has-tag "v093") (has-tag "v0100")))

Failed artifacts

(none)

Updated automatically by tools/post_verification_comment.py. Source of truth: artifacts/verification.yaml.

@codecov

codecov Bot commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@avrabe
avrabe merged commit 9120010 into main Aug 7, 2026
21 checks passed
@avrabe
avrabe deleted the fix/gate-evidence-batch1 branch August 7, 2026 13:15
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