Skip to content

Commit 0644c1d

Browse files
committed
fix(gates): regenerate the hand-written docs ledger, and stop the coverage gate skipping v17
Two consequences of the v17 split that CI found and the local run had not. 1. `scripts/docs-audit/handwritten-docs.json` is GENERATED output — the audit scope, derived as `content/docs/**/*.mdx` minus the generated references, with hand-editing rejected outright. The split moved six pages under it, so `check:docs-audit-scope` went red naming both directions of the drift: one listed path gone, six real pages unlisted. Regenerated with the gate's own `--write`; the diff is exactly those six for that one, nothing else moved. Left unfixed this is not cosmetic: a FULL audit would have called itself full while silently skipping every v17 page, and an agent pointed at the dead path would report "0 fixes" — indistinguishable in the summary from a page that was read and found accurate. 2. `check-release-section-coverage` looked only for a flat `v<major>.mdx`, so after the split it printed "(v17: … skipped)" and then reported "2 GA major(s) in scope (v16, v17); every one has a heading on its major's release page". It was asserting nothing about v17 while naming it as covered. The skip is explicitly justified in the source by the two sibling gates going red on the same fact — and this PR taught both of them the folder layout, so that justification had gone. It now reads either layout, the folder as one corpus, because "some heading names this minor" is a property of the major's prose wherever it is written; the split moved `## What's new in 17.1.0` from line 3475 of one file to line 5 of another, which is not a coverage change. Proven to measure rather than merely stop printing "skipped": with every heading naming the 17.4 series mutated away, the gate reports the finding against `content/docs/releases/v17/` and exits 0 advisory / 1 --strict. Its 50-case self-test is unchanged and still passes. No gate is weakened and no baseline or exception list is added: one generated artifact regenerated by its own generator, and one lookup taught the second layout its two siblings already accept. check:docs-audit-scope, doc-anchors, doc-authoring, docs-redirects, docs-single-h1, release-notes, release-page-status, release-index-currency-sync, role-word, quick-reference-counts, corpus-claim-drift, published-readme-links, org-identifier and docs-image-tag all pass; lychee reports 2152 links, 0 errors. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016M1gHPfToXY4sUdzsD2CHa
1 parent e3c2866 commit 0644c1d

2 files changed

Lines changed: 46 additions & 6 deletions

File tree

scripts/check-release-section-coverage.mjs

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@
181181
// changelogs" is exactly why a finding here is a report and not a build failure:
182182
// the process itself sanctions the other branch, and no gate should hard-fail a
183183
// state its own process document permits.
184-
import { readFileSync, existsSync, appendFileSync } from 'node:fs';
184+
import { readFileSync, existsSync, readdirSync, appendFileSync } from 'node:fs';
185185
import { tmpdir } from 'node:os';
186186
import { dirname, isAbsolute, resolve } from 'node:path';
187187
import { fileURLToPath } from 'node:url';
@@ -376,6 +376,41 @@ export function coverageFindings(major, minor, pagePath, pageText) {
376376
];
377377
}
378378

379+
/**
380+
* The corpus this gate reads for one major, across BOTH page layouts, or null
381+
* when the major has no page at all.
382+
*
383+
* A major is either one flat `v17.mdx` or a `v17/` folder holding `index.mdx`
384+
* plus one page per minor — the same two layouts check:release-notes and
385+
* check:release-page-status accept, and neither is deprecated. The assertion
386+
* here is "some heading NAMES this minor", which is a property of the major's
387+
* prose wherever it is written down, so the folder is read as one corpus: the
388+
* split moved the `## What's new in 17.1.0` heading from line 3475 of one file
389+
* to line 5 of another, and that is not a coverage change.
390+
*
391+
* ⛔ Returning null on a folder that exists would be worse than the old flat-only
392+
* lookup, not equal to it: the skip below is only honest while a sibling gate
393+
* really does go red on the same fact, and once those two learned this layout a
394+
* skip here became a silent hole reported as "in scope" by renderOk.
395+
*
396+
* @param {number} major
397+
* @returns {{ pagePath: string, pageText: string } | null}
398+
*/
399+
export function releasePageCorpus(major) {
400+
const flat = `${RELEASES_DIR}/v${major}.mdx`;
401+
if (existsSync(flat)) return { pagePath: flat, pageText: readFileSync(flat, 'utf8') };
402+
403+
const dir = `${RELEASES_DIR}/v${major}`;
404+
if (!existsSync(`${dir}/index.mdx`)) return null;
405+
const files = readdirSync(dir)
406+
.filter((f) => f.endsWith('.mdx'))
407+
.sort();
408+
return {
409+
pagePath: `${dir}/`,
410+
pageText: files.map((f) => readFileSync(`${dir}/${f}`, 'utf8')).join('\n'),
411+
};
412+
}
413+
379414
// ── Assertion 2: index currency ──────────────────────────────────────────────
380415

381416
/**
@@ -1210,14 +1245,14 @@ function main(argv) {
12101245
const indexText = existsSync(INDEX_PATH) ? readFileSync(INDEX_PATH, 'utf8') : null;
12111246

12121247
for (const major of inScopeMajors) {
1213-
const pagePath = `${RELEASES_DIR}/v${major}.mdx`;
1214-
if (!existsSync(pagePath)) {
1248+
const page = releasePageCorpus(major);
1249+
if (page === null) {
12151250
// check:release-notes and check:release-page-status both already fail on
12161251
// this. Reporting it a third time is three reds for one fix.
1217-
console.log(` (v${major}: no ${pagePath} — page existence is check:release-notes' verdict; skipped)`);
1252+
console.log(` (v${major}: no ${RELEASES_DIR}/v${major}.mdx and no ${RELEASES_DIR}/v${major}/ — page existence is check:release-notes' verdict; skipped)`);
12181253
continue;
12191254
}
1220-
const pageText = readFileSync(pagePath, 'utf8');
1255+
const { pagePath, pageText } = page;
12211256
for (const [maj, min] of minors) {
12221257
if (maj !== major) continue;
12231258
findings.push(...coverageFindings(maj, min, pagePath, pageText));

scripts/docs-audit/handwritten-docs.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,12 @@
191191
"content/docs/releases/v14.mdx",
192192
"content/docs/releases/v15.mdx",
193193
"content/docs/releases/v16.mdx",
194-
"content/docs/releases/v17.mdx",
194+
"content/docs/releases/v17/17-0.mdx",
195+
"content/docs/releases/v17/17-1.mdx",
196+
"content/docs/releases/v17/17-2.mdx",
197+
"content/docs/releases/v17/17-3.mdx",
198+
"content/docs/releases/v17/17-4.mdx",
199+
"content/docs/releases/v17/index.mdx",
195200
"content/docs/releases/v9.mdx",
196201
"content/docs/ui/actions.mdx",
197202
"content/docs/ui/apps.mdx",

0 commit comments

Comments
 (0)