fix(vale): Make could-not-run block, and stop shipping rules every consumer disables

  • A vale that could not run was indistinguishable from a clean one, because allow_failure: true swallowed exit 2 along with exit 1. v4.3.0 already removes the runtime failure mode by baking vale and its styles into ci-tools, but the signal itself was still unreadable by the pipeline. The job now emits allow_failure: { exit_codes: $[[ inputs.tolerated_exit_codes ]] }, defaulting to [1]: prose findings advise, a check that could not run blocks. This is the estate's exit-code taxonomy finally enforced rather than merely documented -- 0 pass, 1 the-checked-thing-failed, 2 the-check-could-not-run, and only the middle one is a matter of taste.

    The input contract is unchanged, so this is a minor. allow_failure is still a boolean; tolerated_exit_codes derives its default from it via spec:inputs:rules: and resolves to [0] when allow_failure is false. [0] expresses "tolerate nothing" because exit 0 never fails, chosen over an empty array so the behaviour does not rest on GitLab's handling of exit_codes: []. Behaviour DOES change: a vale job that silently passed while linting nothing will now go red. That is the fix, not a regression.

    Proven by sim-vale-exit-codes, two arms covering both branches of the rule. exit_codes appeared nowhere in this catalog before now, and deriving an array default from another input is likewise new here; both failure modes are config failures, so an invalid shape means the pipeline does not create.

  • mode: project did not disable the rules its own description claimed, and three separate consumers had patched around it. The input said project mode disables InternalLink*; the emitted .vale.ini never disabled InternalLinkFormat. Sweeping every catalog consumer's .gitlab-ci.yml found gitlab_docs.InternalLinkFormat hand-disabled in 3 of 3 vale consumers, gitlab_base.Offerings in 3, and gitlab_base.LatinTerms in 3.

    All three are now off by default in project mode. A rule every consumer disables is not a standard, it is a papercut with a default: InternalLinkFormat enforces docs.gitlab.com link shape and means nothing in a README, and Offerings and LatinTerms enforce docs product voice on prose that is not product docs. A project that wants the full ruleset still has mode: docs. Substitutions and SubstitutionWarning deliberately stay on, because the mechanical discipline they carry is correct everywhere.

    After this ships, reference, gitlab-contribution and gitlab-simulation can each drop those entries from their disable_rules.

  • templates/paper/README.md taught @~latest in three places -- a floating ref in the one document a new consumer copies from. Now pinned. lab/project-lattice/paper pins @main, which components cannot resolve at all since they resolve from tags only, and is almost certainly downstream of this.

How these were found

Not by reading the catalog. By sweeping all 17 catalog consumers' .gitlab-ci.yml for consumer-side workarounds -- comments and overrides that indicate someone hit a catalog defect and patched around it locally. 29 hits across 9 projects. Three defects today came from that shape (component.version in a spec default, vale styles on master, and this mode description), so those workarounds are a self-reported map of catalog defects and reading them is higher-yield than auditing the catalog directly.

Deliberately not in this MR

The sweep also surfaced a supply-chain correctness issue: container-sbom and container-attest each declare the other as an optional needs:, so serialization depends on a naming coincidence. kaniko works around it with sequential stages and its comment says cosign attest's append behaviour races and "last-writer-wins silently drops one attestation". CONTRIBUTING rule 15 calls that ordering "a contract guarantee, not consumer responsibility". That deserves its own change with a sim that reproduces the race, not a slot in a bundled tag.

Merge request reports

Loading