Skip to content

[SHOWSTOPPER] Five gates claim guarantees they do not enforce — Rust coverage covers 9 of 12 crates, deslop states three ceilings, branch-protection has a dead trap check #232

Description

@MelbourneDeveloper

Five places where the gating configuration claims a guarantee the tooling does not enforce. Every one was found by running the real gates locally during a full ci-prep on fixes; each is verified against the file, not inferred.

CLAUDE.md is unambiguous about why this class matters: "A gate you can turn off is not a gate", "Advisory is deleted", and the record of both rulesets being found enforcement: disabled with a 212-file PR already merged past them. These findings are the same failure mode one layer down — the gate is on, but it does not check what its own documentation says it checks.

None of these are being fixed silently. Filing first.


1. BLOCKER — _coverage_check_rust gates 9 of 12 workspace members

Makefile:502 states:

Per-crate enforcement: every rust crate is gated independently against its own threshold (floor 95% + monotonic ratchet).

It is not. The loop at Makefile:512 iterates only the keys that already exist in the JSON:

for crate in $(jq -r '.projects | to_entries[] | select(.value.language=="rust") | .key' "$(COVERAGE_THRESHOLDS_FILE)")
Workspace members (Cargo.toml:6-19) 12
Rust entries in coverage-thresholds.json 9
Ungated osprey-fmt, osprey-project, tree-sitter-osprey

cargo llvm-cov --workspace measures all twelve into lcov.info. The gate then discards three. osprey-project is the module / name-resolution / purity layer — not a peripheral crate.

The correct implementation already exists 150 lines further down the same file. _coverage_check_c_runtime walks C_SHIPPED_UNITS and fails on any library that is neither gated nor explicitly exempt (Makefile:676-678), with the reason written out:

[c] FAIL: runtime/$n.c ships in a native archive but is neither gated in
coverage-thresholds.json nor in C_COV_EXEMPT -- an ungated library cannot
regress visibly

The Rust gate has no analogue. A crate added tomorrow inherits no floor and nothing reports it. Note also that tree-sitter-osprey is a workspace member outside crates/, so the gate's awk path match /crates/<name>/ could never see it even if a key were added — the completeness check and the path matcher both need fixing.

coverage-thresholds.json:3 compounds this by claiming "FLOOR: every Rust crate is gated at >=95%."

2. HIGH — the Rust coverage gate is lines-only, which this repo already diagnosed as dishonest

Makefile:515 sums LH:/LF: and nothing else. The FN/FNF/FNH function records llvm-cov writes into the same lcov.info are ignored.

The comment above the TypeScript gate (Makefile:770-774) says why that is wrong:

Gates EVERY metric the summary reports, not lines alone. Lines-only was the dishonest half of this gate: branches and functions were measured, printed … and then thrown away, so a module could add an untested else arm … without moving the number the gate reads.

The problem was diagnosed, fixed for the extension (all four metrics, Makefile:783-795), and left in place for the entire Rust compiler.

3. HIGH — .deslop.toml states three different ceilings, one of which is enforced

Line Says
12 # Ceiling = 4.4% (2026-08-11), ratcheted down from 5%.
46 8.0% remains the AUTHORISED HARD CAP — never raise above it.
51 max_duplication_percent = 5 ← the only one that runs

Today's measured value is 4.5% (3512 / 77401 LOC). If the file enforced what line 12 says, make _deslop and the required Build, Format & Analyse job would be red right now — the reviewed, written-down budget has been exceeded and no gate can say so. The measurement cited at line 13 (4.29% of 68400 LOC) is stale by ~9k LOC.

A ratchet whose own record contradicts itself cannot be audited, and the next person to raise the value can cite line 46 as authorisation.

Two smaller defects in the same file:

  • Line 9 says "Local make ci uses the deslop target". There is no deslop target; it is _deslop (Makefile:250, wired via lint: _deslop _lint). Commit 3f40dfb4 fixed this exact typo at line 14 and missed line 9 — the file's own instructions do not run as written.
  • Lines 6-7 say the gate covers "Rust/C#/Python/Dart" and that this scopes it to the Rust workspace. TypeScript and JavaScript are in scope too — the file's own exclude list proves it (**/*.test.ts at line 61, website/src/assets/vendor/** at line 82 justified as minified JS bundles "dominating the report"). Those exclusions are meaningless unless JS/TS is parsed. This matters for finding 4.

4. HIGH — verify-branch-protection.mjs has a dead field standing in for a check that does not exist

The header states:

Two traps are checked structurally, because both are why the gates were off.

Only one is. The phantom-context and path-filter traps are asserted. The job-level-if: trap is not: hasIf is assigned at line 88 and set at line 95, and read nowhere in the file.

$ grep -n hasIf scripts/verify-branch-protection.mjs
88:      current = { key: line.trim().replace(':', ''), name: null, hasIf: false }
95:    if (/^\s{4}if:/.test(line)) current.hasIf = true

Related, and arguably worse: the same file says

Every job in the four-stage pipeline is here, because every job blocks. There is no advisory tier: a suite worth running is worth failing the merge, and one marked "not required" has been deleted in all but name.

checkContexts compares EXPECTED_CONTEXTS against the ruleset in both directions, but never asserts that every job defined in ci.yml / ci-windows.yml appears in EXPECTED_CONTEXTS. The data is already gathered (allJobs, line 157) — only the assertion is missing. Adding a job tomorrow creates exactly the advisory tier this comment forbids, and the script stays green. The 14 contexts and both rulesets do reconcile today, but by hand, not by gate.

Also worth noting: make hawk is the dead-code gate and it is Rust-only, so nothing in the pipeline can catch a dead field in a .mjs checker.

5. HIGH — a website-only PR skips the duplication gate, and website source is in the measured set

The Deslop step lives in the build job, which carries if: needs.changes.outputs.code == 'true' with the filter code: - '!website/**'. A skipped required check counts as passing.

Per finding 3, website/src/js/** is measured — only website/scripts/** and website/src/assets/vendor/** are excluded. So a website-only PR can push duplication past the ceiling while the required check reports skipped.

Format, clippy, hawk and the manifest validation lose nothing by skipping on a website-only PR. This one does. Note the precedent already in the tree: test-website deliberately needs: nothing, with the comment "it must not be gated on changes — a job that needs: a failed job is SKIPPED".


Lower severity, same class

  • Every corpus floor ships with an environment override. crates/run_test_corpus.sh:68 GOLDEN_MIN=${OSPREY_GOLDEN_MIN:-142/203}, :72 GPU_MODE_MIN=${OSPREY_GPU_MODE_MIN:-18}, and Makefile:661 C_COV_EXEMPT ?= — each says "Ratchet UP; never lower it to turn a red build green" while providing the switch that does exactly that. CI sets none of them today. The contrast is inside the same script: the WASM skip set deliberately ships no regeneration flag, because "a --update switch would turn every new hole into one keystroke."
  • cargo install cargo-llvm-cov --locked || true — a swallowed non-zero inside the required Tests: Rust workspace (coverage) job. It fails closed today only because the next step errors without the binary; it is protected by what happens to come after it, not by an assertion.
  • _coverage_check_c_runtime discards suite output entirely (Makefile:671): { ./$(s) >/dev/null 2>&1 || true; } drops exit status, stdout and stderr. A failure occurring only under --coverage -DOSP_COVERAGE_DUMP is invisible and surfaces as an unexplained coverage number.
  • 53 silent this.skip() sites in vscode-extension/test/suite/test-explorer.test.ts (31) and test-docs.test.ts (22), guarded by if (!compiler). Mocha reports pending as green: no count printed, no manifest compared, nothing fails if all 53 vanish. crates/osprey-cli/src/test_skips.rs:60-64 makes a reason-less skip a hard error: for Osprey tests ([TESTING-SKIP-REASON]), and tests/WASM_UNPORTABLE.txt is diffed exactly — the extension suite has neither. The correct pattern is two functions away: resolveRequiredLldbDap() calls assert.fail instead of skipping.
  • coverage-thresholds.json documents a rule it also documents breaking. _doc says "each C entry is the WEAKEST measured platform minus ~2 points" and lists websocket_server, websocket_client, http_client, fiber, coverage_runtime, file_runtime, http_shared as having "come off this list" — all seven are pinned at 85, a ~5-point silent regression budget each. The same field then says "RATCHET OWED: … That was deliberately NOT done." The named to-do list (3 libraries) understates the real debt by 8: eleven C entries sit below the stated 90 target.
  • Monotonicity is policy, not gate. CLAUDE.md:38 and coverage-thresholds.json:3 both say thresholds only go up. It has held — no key has ever decreased — but by discipline alone. Branch protection got verify-branch-protection.mjs; this got nothing. A one-screen git-based check would pin it.

Suggested order

  1. Add the C_SHIPPED_UNITS-style completeness loop to _coverage_check_rust, and fix the awk path matcher so a non-crates/ member can be gated. Land this red — it will fail until entries exist for osprey-fmt, osprey-project and tree-sitter-osprey.
  2. Add those three entries at max(95, measured − 1), per the ratchet rule.
  3. Extend the Rust gate to functions/branches, mirroring _coverage_check_vscode_extension.
  4. Reconcile .deslop.toml to one ceiling — 4.5% by its own rule — and delete the other two numbers.
  5. Assert allJobs ⊆ EXPECTED_CONTEXTS in verify-branch-protection.mjs, and either use hasIf or delete it.
  6. Move the Deslop step out of build, or give the changes filter a deslop output that includes website/src/js/**.

Verified green during the same run

So this is not a report of a broken pipeline — the pipeline works, its self-description does not. On fixes: branch protection intact (2 rulesets, 14 required checks), cargo fmt, clippy -D warnings, extension lint, Shipwright manifest, deslop (4.5%/5%), hawk (0 findings), make build, the corpus at 203/203 with 203/203 goldens under all three memory backends (TEST_CORPUS_ARC_LEAKY=0), GPU alt-lowering 18/18, and the Docker web-compiler suite.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    criticalshowstopperCritical: blocks usage, must fix before release

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions