Repository navigation
[quality] Recorded CI exemptions cite substitute gates that nothing verifies still exist #1665
Description
Finding
tests/workflow-scripts.test.mjs keeps two exemption maps that let a gate stay out of CI as long as the omission is a decision on the record:
GATES_NOT_RUN_BY_CI— why avalidate:*/check:*script is run by no workflow.REFRESH_SCRIPTS_NOT_RUN_BY_CI— why acollect:*/fetch:*script is refreshed by no workflow.
Every reason argues its case by naming another file. The validate:end-user-talks entry is the load-bearing example:
Exists for the Netlify publish path … the PR lane already enforces the same link allow-list through
tests/end-user-talks-data.test.mjsundernpm run test:unit, so a separate ci.yml step would only run the contract a second time.
Four guards already cover these maps — the key is a defined script, the reason is non-empty, no workflow contradicts the entry — but none of them reads the reason for what it points at.
So: rename or delete tests/end-user-talks-data.test.mjs and the pull-request lane loses the end-user-talks link allow-list outright (no ci.yml step runs validate:end-user-talks, by design, because that test exists), while every check in the repository stays green and the exemption still reads as a standing decision. The recorded decision survives the evidence it rested on. The same holds for each collector exemption that cites its scripts/ header.
Verified on a621619: moving tests/end-user-talks-data.test.mjs aside leaves the whole suite green except the rendering tests — no guard names the broken citation.
This is not a coverage gap. Unit coverage of src/ and scripts/ is 100.00% lines / 100.00% regions (npm run test:unit:coverage, node v22, commit a621619), and the e2e lane gates at --check-source 100 --require-source-files. It is a missing contract over a hand-maintained rationale, not uncovered code.
Recommendation
Add one guard to tests/workflow-scripts.test.mjs asserting that every repository file a recorded exemption cites still exists, over both maps, in the same shape the surrounding guards use:
- extract the citations as a pure function of the map so fixtures can prove the guard reports a stale one (the evidence convention
tests/data-gate-contract.test.mjssets out); - pin it against vacuity — both maps must be seen to cite something;
- scope the pattern to
.mjsfiles undertests/andscripts/, which is what an exemption has so far had reason to name, so prose and Markdown citations are not dragged in.
Test-only change, no production code and no workflow edit.
Priority
- Impact: medium — it does not break anything today, but it is the one way the end-user-talks link allow-list can leave the PR lane silently.
- Effort: low — one test file.
Filed by quality agent (hold-gated mode)
🐝 Hive Agent: quality | Instance: hosted-available-lke648397-260827-5n31 | SHA: a621619
— hive: agent=quality backend=copilot model=claude-opus-5 copilot=1.0.88
Activity
hivecommons-hive commentedon Oct 19, 2026
Folded finding. This was filed as a separate issue, but 1 open agent-filed issue (plus 5 closed as completed in the last 14 days) already cite
ci.yml, so it was folded into the oldest of them instead of opening another. Treat this issue as the tracker for that component: prefer one structural fix that covers every folded variant over a PR per variant.[quality] Stale gate rationale in two test headers predates the end-user-talks validator
Finding
Two test headers still describe a world two pull requests have since changed, and both are load-bearing prose — the repository uses these headers as the record of why a gate exists, so a reader reasoning from them reaches a wrong conclusion about what covers
data/end-user-talks.json.1.
tests/end-user-talks-data.test.mjs:7-10opens with:scripts/validate-end-user-talks.mjshas existed since #1624 and is chained intovalidate:all, so "no gate of its own" is false. "The only committed dataset" is also no longer accurate on its own terms —tests/data-gate-contract.test.mjs:7-9records thatdata/milestones.jsonlikewise has no validator.What the header loses by being stale is the thing a reader most needs: the two gates do not back each other up. No
ci.ymlstep runsvalidate:end-user-talks— the omission is deliberate and recorded inGATES_NOT_RUN_BY_CIintests/workflow-scripts.test.mjs, on the grounds that this very test enforces the same contract on the pull-request lane. So this file is the whole of that lane's cover, and the header does not say so.2.
tests/data-gate-contract.test.mjs:13-15cites the stale sentence as evidence:Correcting (1) strands this quotation, so the two have to move together. Its underlying point — that until #1383 the dataset had no reader at all — stays true and is kept.
Verified on
a621619:scripts/validate-end-user-talks.mjsexists andvalidate:allchains it (package.json:72); thevalidatejob inci.ymlruns 13 validators and not this one. No test asserts on either header's wording, so correcting the prose breaks nothing.Recommendation
Rewrite the first paragraph of
tests/end-user-talks-data.test.mjsto state the wiring as it now stands — this file is the pull-request lane's data contract,scripts/validate-end-user-talks.mjscovers the Netlify publish path viavalidate:all, and theGATES_NOT_RUN_BY_CIexemption means neither backs the other up — while keeping the rest of the header, which is still accurate and still the reason the file exists. Update the quotation intests/data-gate-contract.test.mjsin the same change so the cross-reference survives.Prose only: no assertion, no production code, no workflow change.
Priority
Filed by quality agent (hold-gated mode)
🐝 Hive Agent:
quality| Instance:hosted-available-lke648397-260827-5n31| SHA:a621619— hive: agent=quality backend=copilot model=claude-opus-5 copilot=1.0.88
Folded finding. This was filed as a separate issue, but 1 open agent-filed issue (plus 5 closed as completed in the last 14 days) already cite
ci.yml, so it was folded into the oldest of them instead of opening another. Treat this issue as the tracker for that component: prefer one structural fix that covers every folded variant over a PR per variant.[quality] ArchitectureCard's assets?. short-circuit is never exercised in a browser
Finding
ArchitectureCardinsrc/components/ReferenceArchitectures/index.jsresolvesits logo through an optional chain:
All eight entries in
data/architectures/catalog.jsoncarry anassetsarray,so in a browser the chain is always entered. Its nullish short-circuit — the
arm a catalog regenerated without the field would take — has never executed on
the real site.
Unit evidence
npm run test:unit:coverageatda7701breports the file at 100.00% lines /100.00% regions.
tests/reference-architectures.test.mjsalready pins thearm directly (
a card with no assets array at all still renders initials), sothis is not a missing unit test.
End-to-end evidence
Artifact
e2e-coverage(id11825781781) of CI runhttps://github.com/cncf/endusers/actions/runs/38418930699 (workflow
ci.yml,job
End-to-end coverage, head1e37cdc, 2026-10-13T15:04:47Z) reports:100% lines, 95.65% regions (44/46), uncovered regions at lines 13 and 34.
Per the coverage-evidence rules this is unit-covered but not end-to-end
covered → priority 2 (medium). The two coverage sources are not combinable at
statement granularity, so they were analyzed separately.
Recommendation
Use the mechanism
tests/tools/e2e-data-fixtures.cjsalready documents forexactly this shape — an additive overlay, not a new fixture build:
swisscom-cloud-native-telcocarries no logo or wordmark asset to begin with,so the chain already resolved to
undefinedfor it and the rendered card isunchanged — only which arm produced the
undefineddiffers.allianzandswisscom-kubernetes-servicekeep their arrays and their lack of a logo asset,so the entered-but-unmatched arm stays covered in the same build. No second
Docusaurus compile is added.
Then add a case to
tests/e2e/data-fixtures.spec.jsasserting the nullish cardstill renders and renders the no-logo shape, beside the unmatched and matched
cards on the same page.
assets: nullonto one catalog entry intests/e2e/fixtures/data/architectures/catalog.jsontests/e2e/data-fixtures.spec.jsPriority
Scope note: this issue is confined to
ReferenceArchitectures' assetresolution and its fixture/spec. It does not overlap the freshness-note,
initials(),GroupLinkStatus,FilterToolbarorMemberDirectoryclustersheld open by #1683, #1680, #1677, #1675, #1679 and #1682.
🐝 Hive Agent:
quality| Instance:hosted-available-lke648397-260827-5n31| SHA:da7701b— hive: agent=quality backend=copilot model=claude-opus-5 copilot=1.0.88
Folded finding. This was filed as a separate issue, but 1 open agent-filed issue (plus 5 closed as completed in the last 14 days) already cite
ci.yml, so it was folded into the oldest of them instead of opening another. Treat this issue as the tracker for that component: prefer one structural fix that covers every folded variant over a PR per variant.[quality] ReferenceArchitectures: the optional chain on architecture.assets never short-circuits in the e2e coverage build
Finding
ArchitectureCardinsrc/components/ReferenceArchitectures/index.jsresolvesits logo through an optional chain:
All eight entries in
data/architectures/catalog.jsoncarry anassetsarray,so in a browser the chain is always entered. Its nullish short-circuit — the
arm a catalog regenerated without the field would take — has never executed on
the real site.
Unit evidence
npm run test:unit:coverageatda7701breports the file at 100.00% lines /100.00% regions.
tests/reference-architectures.test.mjsalready pins thearm directly (
a card with no assets array at all still renders initials), sothis is not a missing unit test.
End-to-end evidence
Artifact
e2e-coverage(id11825781781) of CI runhttps://github.com/cncf/endusers/actions/runs/38418930699 (workflow
ci.yml,job
End-to-end coverage, head1e37cdc, 2026-10-13T15:04:47Z) reports:100% lines, 95.65% regions (44/46), uncovered regions at lines 13 and 34.
Per the coverage-evidence rules this is unit-covered but not end-to-end
covered → priority 2 (medium). The two coverage sources are not combinable at
statement granularity, so they were analyzed separately.
Recommendation
Use the mechanism
tests/tools/e2e-data-fixtures.cjsalready documents forexactly this shape — an additive overlay, not a new fixture build:
swisscom-cloud-native-telcocarries no logo or wordmark asset to begin with,so the chain already resolved to
undefinedfor it and the rendered card isunchanged — only which arm produced the
undefineddiffers.allianzandswisscom-kubernetes-servicekeep their arrays and their lack of a logo asset,so the entered-but-unmatched arm stays covered in the same build. No second
Docusaurus compile is added.
Then add a case to
tests/e2e/data-fixtures.spec.jsasserting the nullish cardstill renders and renders the no-logo shape, beside the unmatched and matched
cards on the same page.
assets: nullonto one catalog entry intests/e2e/fixtures/data/architectures/catalog.jsontests/e2e/data-fixtures.spec.jsPriority
Scope note: this issue is confined to
ReferenceArchitectures' assetresolution and its fixture/spec. It does not overlap the freshness-note,
initials(),GroupLinkStatus,FilterToolbarorMemberDirectoryclustersheld open by #1683, #1680, #1677, #1675, #1679 and #1682.
🐝 Hive Agent:
quality| Instance:hosted-available-lke648397-260827-5n31| SHA:da7701b— hive: agent=quality backend=copilot model=claude-opus-5 copilot=1.0.88