Found during a multi-persona review of REQ-GUARD-GATE-EVIDENCE-002. Verified at 618b29df.
This is the same defect family as #381/#384/#385/#388 — an error path that yields the ideal reading — but it sits on the release path rather than the merge gate, so its blast radius is a published artifact rather than a green check.
The chain
.github/workflows/release.yml:456-459 gathers release assets:
find artifacts -type f \
\( -name "*.tar.gz" -o -name "*.zip" -o -name "*.vsix" \) \
-exec cp {} release-assets/ \;
find exits 0 when it matches nothing. Confirmed:
$ mkdir -p /tmp/rz && find /tmp/rz -type f -name '*.tar.gz' -exec cp {} /tmp/rz \; ; echo $?
0
Upstream, every one of the six upload-artifact steps leaves if-no-files-found at its default (warn):
| job |
if-no-files-found |
build-binaries |
(default: warn) |
build-compliance |
(default: warn) |
build-test-evidence |
(default: warn) |
build-wasm |
(default: warn) |
build-vsix |
(default: warn) |
build-sbom |
(default: warn) |
So an empty or partial build produces no upload error, find copies nothing, and sha256sum ./* (:481), the cosign signing, and gh release create ... release-assets/* (:559-562) all proceed over whatever survived — up to and including nothing.
The repo already knows the difference, which is what makes this a gap rather than a policy: the SBOM step immediately below (:464-470) is explicitly guarded with if [ -n "$SBOM_SRC" ] ... else ::error:: ... exit 1, and trace-fixtures.yml:453 sets if-no-files-found: error. Five sibling globs were not given the same treatment.
Second instance in the same workflow
release.yml:213-220:
set +e
cargo run --release -- analyze --root Analysis_Pkg::Full_System.Impl \
test-data/analysis_test.aadl > test-evidence/validation/analyze-output.txt 2>&1
rc=$?
set -e
echo "exit_code=${rc}" >> test-evidence/validation/analyze-output.txt
exit_code= is written and never read — a grep of the whole repo finds line 220 as its only occurrence. A panic, or a --root selector that stops matching after a model rename, yields a green step, a green build-test-evidence job, a satisfied needs, and a released evidence bundle whose "validation" content is a crash trace.
Suggested fix
if-no-files-found: error on all six uploads (the value the repo already uses elsewhere).
- Replace the bare
find with a gathered-and-counted form that fails on zero, and assert an expected minimum asset count before signing — signing is the step that converts "we built nothing" into "we attested nothing".
- Either assert on
rc in the evidence step, or stop writing a value nothing consumes.
Acceptance: a release run with an artificially emptied artifacts/ must fail before gh release create, not after.
NOT claimed: that any published release is currently empty. This is about what the pipeline permits.
🤖 Generated with Claude Code
Found during a multi-persona review of REQ-GUARD-GATE-EVIDENCE-002. Verified at
618b29df.This is the same defect family as #381/#384/#385/#388 — an error path that yields the ideal reading — but it sits on the release path rather than the merge gate, so its blast radius is a published artifact rather than a green check.
The chain
.github/workflows/release.yml:456-459gathers release assets:findexits 0 when it matches nothing. Confirmed:Upstream, every one of the six
upload-artifactsteps leavesif-no-files-foundat its default (warn):if-no-files-foundbuild-binariesbuild-compliancebuild-test-evidencebuild-wasmbuild-vsixbuild-sbomSo an empty or partial build produces no upload error,
findcopies nothing, andsha256sum ./*(:481), the cosign signing, andgh release create ... release-assets/*(:559-562) all proceed over whatever survived — up to and including nothing.The repo already knows the difference, which is what makes this a gap rather than a policy: the SBOM step immediately below (
:464-470) is explicitly guarded withif [ -n "$SBOM_SRC" ] ... else ::error:: ... exit 1, andtrace-fixtures.yml:453setsif-no-files-found: error. Five sibling globs were not given the same treatment.Second instance in the same workflow
release.yml:213-220:exit_code=is written and never read — a grep of the whole repo finds line 220 as its only occurrence. A panic, or a--rootselector that stops matching after a model rename, yields a green step, a greenbuild-test-evidencejob, a satisfiedneeds, and a released evidence bundle whose "validation" content is a crash trace.Suggested fix
if-no-files-found: erroron all six uploads (the value the repo already uses elsewhere).findwith a gathered-and-counted form that fails on zero, and assert an expected minimum asset count before signing — signing is the step that converts "we built nothing" into "we attested nothing".rcin the evidence step, or stop writing a value nothing consumes.Acceptance: a release run with an artificially emptied
artifacts/must fail beforegh release create, not after.NOT claimed: that any published release is currently empty. This is about what the pipeline permits.
🤖 Generated with Claude Code