You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Ground truth: gh api repos/pulseengine/rivet/branches/main/protection → required_status_checks.contexts == ["CI Gate"], enforce_admins: true. Severity below is measured against that.
Verification status: agent-reported, several with constructed negative controls. I verified #953 and #954 myself; the rest need confirming before fixing.
1. The traceability gate cannot fail on a traceability regression — negative control constructed
ci.yml:304 runs rivet validate. On the real corpus: Result: PASS (931 warnings), exit 0. Every traceability finding is Warning or Info — 286 prose-mention-without-typed-link, 240/87 requirement-verification, 214/73 requirement-coverage, 119 orphan-artifact.
Control: appending one implemented requirement with no links, no design, no test still gives PASS, exit 0 (935 warnings). A PR merging an untraced implemented requirement passes the gate whose section header reads "The project that BUILDS the traceability tool must gate its own PRs on traceability."
The step comment is honest about it. But rivet-cli/src/docs.rs:1468,1498 publish to users the recipe rivet validate --strict-cited-sources --fail-on warning. rivet ships a stricter gate to its customers than it runs on itself. The ready-made ratchet — --new-since <ref> --fail-on … at main.rs:5445 — is wired into no workflow.
2. Supply Chain (cargo-vet) audits zero dependencies
ci.yml:1281-1313:
if [ !-d supply-chain ];then cargo vet init; ... fi
cargo vet --locked
ls supply-chain/ → No such file or directory. git log -- supply-chain → empty; it has never existed and is not gitignored. So init runs every time, and cargo vet init records every current dependency as an exemption precisely so the initial state passes. Observed green in run 35052742650. Also outside CI Gate's needs.
3. Miri and Proptest are outside CI Gate and outside its documented exclusion list
ci.yml:1520-1524 enumerates exclusions as "Kani, Mutation Testing, Coverage, Playwright" — presenting itself as complete. Also absent from needs and from that list: Miri (safety surface), Proptest (extended), Security Audit, Supply Chain, VS Code Extension, Zola export smoke, Traceability (hosted fallback).
Miri is the only gate over the std::mem::transmute of raw SyntaxKind in sexpr.rs/yaml_cst.rs — the sole unsafe in rivet-core. A Miri red cannot block a merge. (Whether that's intended is the decision; the list being wrong is the bug.)
4. yaml_test_suite.rs — 68 conformance cases blind to total loss of parse structure
Module doc promises three properties; the third ("graceful Error recovery") is never asserted — fn parse_has_errors at :93 has zero call sites. No test inspects tree shape. If yaml_cst::parse degenerated so every document lexed into one flat Scalar, root.text() == input would still hold (rowan is lossless by construction) and all 68 cases stay green against a CST with no mappings or sequences.
5. sexpr_fuzz::roundtrip_equivalence — the failure mode is prop_assume!d away
sexpr_fuzz.rs:375: prop_assume!(reparsed.is_ok(), "pretty-print must re-parse"). prop_assume! rejects the sample rather than failing. The one outcome the property exists to detect is routed to "skip". Partial breakage never surfaces.
6. unknown-field is Severity::Info
validate.rs:1187. Control: renaming priority→prioriti, category→categorie, adding relase: on REQ-001 gives PASS, exit 0 with three INFO lines. A rename silently detaches schema-declared fields; release-scoping queries go empty; the gate stays green.
7. Runner Liveness — the liveness branch is unreachable (opposite failure from REQ-354)
runner-liveness.yml:114-121 queries repo-scope runners. The workflow's own issue body at :264 says "runners are registered at the ORG. Repo scope returns 0 on a perfectly healthy pool and can never say otherwise." So total is always 0, the -gt 0 guard fails, and the "All N offline" branch is unreachable. Scenario: pool dies on a quiet Sunday with nothing queued → probe sees total=0 and oldest_age=0 → posts "✅ Runner pool healthy again" and closes the alert.
ci.yml:652 — cargo install cargo-deny --version 0.20.2 || true silently falls back to a preinstalled 0.16.4 whose unmaintained default differs. This one is in CI Gate's needs, so it ranks highest of this group. REQ-347's own text calls that "exactly how a gate passes vacuously".
ci.yml:1499 — cp target/nextest/ci/junit.xml … || true; the release tarball ships without test results, green.
9. Playwright specs that assert nothing
documents.spec.ts:32,53,84 select a[hx-get^='/documents/'], but render/documents.rs:53,81 emits href-only links. All three loops iterate an empty list; :84 then logs "No .doc-ref links found" while the corpus holds 234 [[…]] refs.
rendering-invariants.spec.ts:239 — comment says "Strict inequality: scoping must yield fewer nodes", assertion is toBeGreaterThanOrEqual. graph_view could ignore variant entirely and pass. The sibling at :243 gets it right.
audit-regression.spec.ts:104 — XSS regression test whose only assertion is body?.length > 0, inside an unguarded if (isVisible()) with no else/test.skip().
coverage-view.spec.ts:58, source-view.spec.ts:45,81 — assert row counts / body.length > 100; the nav shell alone clears them.
action.yml:308 — grep 'name:' rivet.yaml | head -1 | awk … in bash -e without pipefail; a missing file gives PROJECT="" and a misnamed archive that looks intentional.
zola-export-smoke.sh:93 computes PAGESafter the verdict; an empty content dir prints OK: … no absolute artifact-link leaks. Its leak regex requires exactly one path segment, so href="/artifacts/req-001/" evades it. (Advisory — continue-on-error: true.)
check-schema-version-bump.sh:30-33 — git diff | grep -v … || true swallows a failing git diff; STRICT defaults to 0 and ci.yml:257 never sets it.
install-hooks.sh:68 — … || echo "0"; a JSON-shape change or a validate panic yields errors=0 and the hook commits.
rivet-core/tests/integration.rs:1269 — strictdoc_reqif_import gated on /tmp/zephyr-reqif/… which no script creates. Dead on every machine.
The inverse, and the cheapest thing here
rivet check bidirectionalexits 1 on the live corpus and is run by no workflow (grep -rn 'rivet check' .github/ matches only verification-evidence). A working oracle wired into nothing.
Umbrella from the inconsistency audit, weak-green lens. Headline instances filed separately: #953 (compliance discards validate's exit code), #954 (
check verification-evidenceok-on-empty).Ground truth:
gh api repos/pulseengine/rivet/branches/main/protection→required_status_checks.contexts == ["CI Gate"],enforce_admins: true. Severity below is measured against that.Verification status: agent-reported, several with constructed negative controls. I verified #953 and #954 myself; the rest need confirming before fixing.
1. The traceability gate cannot fail on a traceability regression — negative control constructed
ci.yml:304runsrivet validate. On the real corpus:Result: PASS (931 warnings), exit 0. Every traceability finding is Warning or Info — 286prose-mention-without-typed-link, 240/87requirement-verification, 214/73requirement-coverage, 119orphan-artifact.Control: appending one
implementedrequirement with no links, no design, no test still gives PASS, exit 0 (935 warnings). A PR merging an untracedimplementedrequirement passes the gate whose section header reads "The project that BUILDS the traceability tool must gate its own PRs on traceability."The step comment is honest about it. But
rivet-cli/src/docs.rs:1468,1498publish to users the reciperivet validate --strict-cited-sources --fail-on warning. rivet ships a stricter gate to its customers than it runs on itself. The ready-made ratchet —--new-since <ref> --fail-on …atmain.rs:5445— is wired into no workflow.2.
Supply Chain (cargo-vet)audits zero dependenciesci.yml:1281-1313:ls supply-chain/→ No such file or directory.git log -- supply-chain→ empty; it has never existed and is not gitignored. Soinitruns every time, andcargo vet initrecords every current dependency as an exemption precisely so the initial state passes. Observed green in run35052742650. Also outsideCI Gate's needs.3. Miri and Proptest are outside
CI Gateand outside its documented exclusion listci.yml:1520-1524enumerates exclusions as "Kani, Mutation Testing, Coverage, Playwright" — presenting itself as complete. Also absent fromneedsand from that list: Miri (safety surface), Proptest (extended), Security Audit, Supply Chain, VS Code Extension, Zola export smoke, Traceability (hosted fallback).Miri is the only gate over the
std::mem::transmuteof rawSyntaxKindinsexpr.rs/yaml_cst.rs— the soleunsafein rivet-core. A Miri red cannot block a merge. (Whether that's intended is the decision; the list being wrong is the bug.)4.
yaml_test_suite.rs— 68 conformance cases blind to total loss of parse structureModule doc promises three properties; the third ("graceful Error recovery") is never asserted —
fn parse_has_errorsat:93has zero call sites. No test inspects tree shape. Ifyaml_cst::parsedegenerated so every document lexed into one flatScalar,root.text() == inputwould still hold (rowan is lossless by construction) and all 68 cases stay green against a CST with no mappings or sequences.5.
sexpr_fuzz::roundtrip_equivalence— the failure mode isprop_assume!d awaysexpr_fuzz.rs:375:prop_assume!(reparsed.is_ok(), "pretty-print must re-parse").prop_assume!rejects the sample rather than failing. The one outcome the property exists to detect is routed to "skip". Partial breakage never surfaces.6.
unknown-fieldisSeverity::Infovalidate.rs:1187. Control: renamingpriority→prioriti,category→categorie, addingrelase:on REQ-001 gives PASS, exit 0 with three INFO lines. A rename silently detaches schema-declared fields; release-scoping queries go empty; the gate stays green.7.
Runner Liveness— the liveness branch is unreachable (opposite failure from REQ-354)runner-liveness.yml:114-121queries repo-scope runners. The workflow's own issue body at:264says "runners are registered at the ORG. Repo scope returns 0 on a perfectly healthy pool and can never say otherwise." Sototalis always 0, the-gt 0guard fails, and the "All N offline" branch is unreachable. Scenario: pool dies on a quiet Sunday with nothing queued → probe seestotal=0andoldest_age=0→ posts "✅ Runner pool healthy again" and closes the alert.8.
|| trueon the release pathscripts/build-wasm.sh:27-28— success is echoed before the check, and thelsverifying both wasm filenames has stderr and exit code discarded. The only artifact assertion on the release path. (See Two spar revisions ship in one binary: native pinned to v0.10.0, wasm cloned from HEAD, and the drift guard is dead code #951.)ci.yml:652—cargo install cargo-deny --version 0.20.2 || truesilently falls back to a preinstalled 0.16.4 whoseunmaintaineddefault differs. This one is inCI Gate's needs, so it ranks highest of this group. REQ-347's own text calls that "exactly how a gate passes vacuously".ci.yml:1499—cp target/nextest/ci/junit.xml … || true; the release tarball ships without test results, green.9. Playwright specs that assert nothing
documents.spec.ts:32,53,84selecta[hx-get^='/documents/'], butrender/documents.rs:53,81emits href-only links. All three loops iterate an empty list;:84then logs "No .doc-ref links found" while the corpus holds 234[[…]]refs.rendering-invariants.spec.ts:239— comment says "Strict inequality: scoping must yield fewer nodes", assertion istoBeGreaterThanOrEqual.graph_viewcould ignorevariantentirely and pass. The sibling at:243gets it right.audit-regression.spec.ts:104— XSS regression test whose only assertion isbody?.length > 0, inside an unguardedif (isVisible())with noelse/test.skip().coverage-view.spec.ts:58,source-view.spec.ts:45,81— assert row counts /body.length > 100; the nav shell alone clears them.10. Smaller, verified
verify_archive_size.sh— cap with no floor; an empty report tars to ~45 bytes and passes (see Release path: the compliance action discards rivet validate's exit code, so a release can ship on a failing validate #953).action.yml:308—grep 'name:' rivet.yaml | head -1 | awk …inbash -ewithoutpipefail; a missing file givesPROJECT=""and a misnamed archive that looks intentional.zola-export-smoke.sh:93computesPAGESafter the verdict; an empty content dir printsOK: … no absolute artifact-link leaks. Its leak regex requires exactly one path segment, sohref="/artifacts/req-001/"evades it. (Advisory —continue-on-error: true.)check-schema-version-bump.sh:30-33—git diff | grep -v … || trueswallows a failinggit diff;STRICTdefaults to 0 andci.yml:257never sets it.install-hooks.sh:68—… || echo "0"; a JSON-shape change or a validate panic yieldserrors=0and the hook commits.rivet-core/tests/integration.rs:1269—strictdoc_reqif_importgated on/tmp/zephyr-reqif/…which no script creates. Dead on every machine.The inverse, and the cheapest thing here
rivet check bidirectionalexits 1 on the live corpus and is run by no workflow (grep -rn 'rivet check' .github/matches onlyverification-evidence). A working oracle wired into nothing.