All checks were successful
PR checks / checks (pull_request) Successful in 2m11s
PLAN.md §16 phase 16, with the org lead's D72-D74 (count of record 74). - Eight pages nested in the Rust group at /docs/rust/runicnpc/ (D72): the overview, Install RunicNPC, Commands, NPC profiles, NPC placements, Cost and caps, Profile fields (the one field reference the editor, rnpc.profile set, the data file and the API all share) and The API. - The API page carries every call, hook and data shape (D62), in the site's own words, with no decision numbers and no per-call version history (D74). Signatures print from src/data/runicnpc.mjs through Signature.astro. - checkReference.mjs gains two sections. Against docs main:runicnpc/API.md: the API version, 24 call and 10 hook signatures, and the field names of the profile, RunicNpc_List rows, placements, routes and owners (D73), plus exactly one <Signature> per call on api.mdx. Against runicnpc-rust main:plugin/RunicNPC.cs: console verbs, permissions, place options and config keys. Six planted drifts all failed before it was trusted. - checkSidebar and checkTerms read nested sidebar groups via flatLabels(). - The Rust overview, Events zones and rewards, and Canonical documents link the new pages; the canonical documents gain the four runicnpc/ docs. Both NPC admin screens were walked on the walk site first (D36); nothing was saved. npm run verify is green. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
80 lines
3.9 KiB
JavaScript
80 lines
3.9 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* checkSidebar.mjs — PLAN.md §12, added in phase 8.
|
||
*
|
||
* `src/config/sidebar.mjs` holds two trees: `docsSidebar`, which Starlight renders, and
|
||
* `plannedSidebar`, the tree §10 planned. While pages were still being written the second
|
||
* was a checklist. Now that every page exists it is a second copy of the first, maintained
|
||
* by hand — and a hand-maintained copy with nothing reading it is exactly the shape of
|
||
* thing §1 is about.
|
||
*
|
||
* It had already drifted, silently: phase 7 added the `Content` page under D37 and this
|
||
* list was never updated. Nothing failed, because nothing read it. That is the whole
|
||
* argument for this check.
|
||
*
|
||
* So the two must agree on groups, labels AND order. Order is checked because the order of
|
||
* "Getting started" IS the installation path — §10 calls it the priority of the whole
|
||
* project — and a reordering that nobody noticed would be a worse defect than a missing
|
||
* page.
|
||
*
|
||
* node scripts/checkSidebar.mjs
|
||
*
|
||
* No token and no network: both trees are in this repository.
|
||
*/
|
||
|
||
import { docsSidebar, plannedSidebar, flatLabels } from '../src/config/sidebar.mjs';
|
||
|
||
const failures = [];
|
||
const fail = (what, detail) => failures.push({ what, detail });
|
||
|
||
// A nested group (RunicNPC inside Rust, phase 16) flattens to `Group › Page` labels, so its
|
||
// pages and their order are held to the plan exactly as a top-level group's are.
|
||
const live = new Map(docsSidebar.map((g) => [g.label, flatLabels(g.items)]));
|
||
const planned = new Map(Object.entries(plannedSidebar).map(([label, items]) => [label, flatLabels(items)]));
|
||
|
||
// ── Groups ──────────────────────────────────────────────────────────────────
|
||
for (const label of live.keys()) {
|
||
if (!planned.has(label)) fail(`group ${label}`, 'is in the live sidebar and not in plannedSidebar');
|
||
}
|
||
for (const label of planned.keys()) {
|
||
if (!live.has(label)) fail(`group ${label}`, 'is in plannedSidebar and not in the live sidebar');
|
||
}
|
||
|
||
// ── Pages, in order ─────────────────────────────────────────────────────────
|
||
for (const [label, liveItems] of live) {
|
||
const plannedItems = planned.get(label);
|
||
if (!plannedItems) continue;
|
||
|
||
for (const page of liveItems) {
|
||
if (!plannedItems.includes(page)) fail(`${label} → ${page}`, 'is live but not in plannedSidebar');
|
||
}
|
||
for (const page of plannedItems) {
|
||
if (!liveItems.includes(page)) fail(`${label} → ${page}`, 'is planned but has no live sidebar entry');
|
||
}
|
||
|
||
// Only meaningful once membership matches; otherwise it just repeats the above.
|
||
if (liveItems.length === plannedItems.length && liveItems.every((p) => plannedItems.includes(p))) {
|
||
if (liveItems.join(' | ') !== plannedItems.join(' | ')) {
|
||
fail(
|
||
`${label} order`,
|
||
`live "${liveItems.join(' → ')}" vs planned "${plannedItems.join(' → ')}"`,
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
// ── Report ──────────────────────────────────────────────────────────────────
|
||
if (failures.length === 0) {
|
||
const pages = [...live.values()].reduce((n, items) => n + items.length, 0);
|
||
console.log(`checkSidebar: ${live.size} groups and ${pages} pages agree with plannedSidebar.`);
|
||
} else {
|
||
console.error(`\ncheckSidebar: ${failures.length} disagreement(s) between the two trees:\n`);
|
||
for (const f of failures) console.error(` ✗ ${f.what}\n ${f.detail}`);
|
||
console.error(`
|
||
Both trees are in src/config/sidebar.mjs. Decide which one is right — if a page was
|
||
deliberately added, renamed or reordered, plannedSidebar records that decision and
|
||
should move with it.
|
||
`);
|
||
process.exit(1);
|
||
}
|