Skip to content

CI path filter: rivet.yaml is not allow-listed, so pure-manifest PRs run the full heavy suite #379

Description

@avrabe

What

ci.yml's changes job classifies a diff as code-vs-artifacts with this allow-list:

allow='^(artifacts/.*\.ya?ml|safety/.*|docs/.*|[^/]*\.md|.*/.*\.md)$'

Root-level rivet.yaml does not match any alternative[^/]*\.md covers only root markdown. Since "any path outside the allow-list makes the WHOLE diff code", a PR that edits nothing but rivet artifacts plus rivet.yaml is classified code=true and runs Clippy, Test, Miri, Proptest, Fuzz smoke, Code Coverage and Mutation Testing.

Evidence

PR #374 (fix(artifacts): move 276 statuses onto rivet's lifecycle vocabulary) changes 8 files, all artifact-plane:

artifacts/architecture.yaml          <- allow-listed
artifacts/requirements.yaml          <- allow-listed
artifacts/verification.yaml          <- allow-listed
rivet.yaml                           <- NOT allow-listed  <<<
safety/stpa/rendering-analysis.yaml  <- allow-listed
safety/stpa/requirements.yaml        <- allow-listed
safety/stpa/solver-requirements.yaml <- allow-listed
safety/stpa/validation.yaml          <- allow-listed

One unmatched path ⇒ full suite. Measured cost of the single worst job: Mutation Testing over the last 40 CI runs, successful completions only — n=15, min 35.2 min, median 68.9 min, max 118.4 min. It also requires a lean-mem runner, and only pulseengine-ci-01-{1,2,3,4} ever serve it — so a pure-manifest PR occupies 1 of 4 scarce runners for ~an hour.

The workflow already contradicts itself. ci.yml:382 describes the artifact plane as:

# in artifacts/, safety/stpa/, and rivet.yaml.

so rivet.yaml is understood to be artifact-plane by the rivet-validate job, while the allow-list 300 lines earlier treats it as code.

Why the fix is sound

The allow-list must only admit paths that cannot affect a mutant, a fuzz target, or a Miri run. For rivet.yaml:

  • Zero Rust sources reference itgrep -rn "rivet\.yaml" --include="*.rs" . ⇒ 0 files.
  • Repo-wide, it is referenced only by AGENTS.md, two docs/plans/*.md, and ci.yml itself.
  • It is consumed by the external rivet binary in Rivet validate (artifacts), which deliberately has no changes gate and therefore still runs on every PR. So the check that actually validates rivet.yaml keeps running; only the jobs that cannot observe it get skipped.

Suggested change

Add rivet.yaml to the allow-list, e.g.

allow='^(artifacts/.*\.ya?ml|safety/.*|docs/.*|rivet\.yaml|[^/]*\.md|.*/.*\.md)$'

and add a fixture asserting a diff of rivet.yaml alone classifies as code=false, so the constant is tested directly rather than via a glob the test rebuilds.

Related

Same failure shape as the *.yml-glob defect fixed in #372's PR: a pattern that omits a real case, where the omission is silent and biases toward "do the expensive/unsafe thing". Here the bias is fail-safe (over-runs rather than under-runs), so it costs runner-hours rather than correctness — which is why it survived unnoticed.

Not fixed inline right now deliberately: the fix touches ci.yml (hence itself needs a full suite), and main has required_status_checks.strict = true with 7 PRs already queued in a serial merge chain — landing an 8th mid-chain costs more than it saves today.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions