chore(catalog): pin v5.0.0, retire three dead workarounds, propagate into Markdown
What
Pins the Public Sector catalog at v5.0.0 and retires three consumer-side workarounds, each because the catalog fixed the thing it was working around. Separately, extends version propagation to Markdown, which is the surface this repo actually ships.
CATALOG-VERSION is the source of truth and just sync did the propagation. Sixteen pins across seven files.
The three workarounds
osv-verdict no longer needs an explicit image:. The override existed because the component defaulted to ci-tools:$[[ component.version ]] written inside a spec.inputs default, and a spec.inputs default is not re-interpolated when it is substituted into the body, so the literal string reached the runner and was rejected as an invalid reference format. Catalog v5.0.0 defaults the input to a variable name and interpolates component.version in the job's own variables: block. Confirmed in merged YAML: VERDICT_IMAGE: "$CATALOG_REGISTRY/ci-tools:v5.0.0", and both v5.0.0 and 5.0.0 are published tags on ci-tools, so the pin resolves either way.
Three entries leave vale's disable_rules: gitlab_docs.InternalLinkFormat, gitlab_base.Offerings, gitlab_base.LatinTerms. All three are off by default in the component's project mode as of v5.0.0. The sweep behind that change found each one disabled by hand in 3 of 3 vale consumers, which is the tell that a rule is a papercut with a default rather than a standard. Keeping the local override would now assert a project decision where the answer is upstream behaviour.
vale is no longer exempt from version propagation. Both sync-versions.sh and validate-versions.sh skipped vale@ pins, on the stated grounds that vale had to pin ahead of CATALOG-VERSION to reach the disable_rules input and the ci-tools image-tag fix. Both shipped, so both reasons were gone and the exemption had quietly become a hole in the mechanism that exists to close holes. The two comments also cited v4.0.1 while the pin actually sat at v4.1.0, which is how long nobody had read them.
Version propagation now covers Markdown
Both scripts matched only *.yml/*.yaml. So the taught example inside a SKILL.md, the thing a reader of this repo actually copies, was the one thing version propagation could not reach:
standards/pipeline/SKILL.mdpinnedcontainer@v3.0.0and pointed its "exhaustive examples live in the catalog itself" link at/-/tree/v3.0.0standards/provenance/SKILL.mdpinnedextra-assets@v3.0.0
while the YAML beside them tracked v4.1.2. A reader following the standard was pinning two majors behind, and just validate printed PASS over it every pipeline. A validator narrower than its sync is worse than no validator, because it reports clean over precisely the files nothing maintains.
Both scripts now match *.md, and both handle the /-/tree/vX.Y.Z browse-link shape alongside the @vX.Y.Z pin shape. CHANGELOG.md is excluded by name: it is a dated record, and rewriting a version inside a past entry edits history to make the present look tidy. Same rule that keeps CHANGELOG.md out of the vale paths input.
What was verified, not assumed
- Every component named in a pin was confirmed present at
v5.0.0before the pin was written (git ls-tree v5.0.0 -- templates/).reference-checkis absent from that listing, which is why its pins are untouched. - Every input each taught example passes was cross-checked against that tag's
spec.inputs. 11 of 11 includes validate, 0 undeclared inputs. POST /projects/:id/ci/lint:valid=true, 0 errors.- Both new
.mdshapes were regressed by hand and the validator reported each one, so the extension is proven able to fail rather than only able to pass. just validateall gates green;just check-self20 standards passed, 0 failed;just guard0 violations.validate-binding.pyrun withPIPELINE_REPOset so invariants 3 and 4 execute rather than skip: 31 controls, binding intact.
Deliberately not in this MR
reference-check pins stay at v3.1.0, all six of them. That component exists at no tag, so renumbering would restate a false claim at a fresher number. The exemption is load-bearing and stays; what changed is its stated reason, which used to read "ships on its own cadence (planned for catalog v3.1.0)" and asserted a delivery date that never happened. Whether the component becomes real or the story gets deleted is !13 (closed).
One behavioural change to expect
vale's allow_failure: true now routes through tolerated_exit_codes, defaulting to [1]. Exit 1 (vale found prose findings) is still tolerated. Exit 2 (vale could not run at all) is not, and will fail the job where it previously passed silently. That is the intended tightening. A red vale job after this lands should be read as the check working before it is read as a regression.
Noted, not fixed here
- The catalog ships the deprecated
retry:whenvaluestuck_or_timeout_failurein at least 10 templates, so every consumer inherits deprecation warnings it cannot fix locally. That is a catalog fix, not a reference one. validate-binding.pyreportsautomerge-gateis owed a control, "held until the gate's exit-code semantics land". They landed inv5.0.0, so that deferral's condition is now met and the note is stale.- The
.gitlab-ci.ymlheader recommendedglab ci lintfor pre-push validation. It resolvesinclude: local:against the default branch, so a file that exists only on your branch is reported as missing. Corrected to the/ci/lintAPI, which is what caught the errors behind this change.