The module system cut over on 2026-08-12 and website's `edge` branch was deleted, so `ci/core-ref.json` named a branch that no longer exists. The sha did not move. The pinned commit is an ancestor of `main`, the contract is still MODULE_API_VERSION 1.5.0, and no chapter changed - this is a label correction, not a re-pin, and deliberately not the moment d2 exists to create. Nothing in CI reads the `branch` field: the workflow clones the repo and checks out the sha, which is both why the cutover could not break the build and why a wrong label here would have sat unnoticed indefinitely. The field is for the person deciding whether a newer core is worth re-reading the book for, and a branch that no longer exists tells them nothing. The `why` block now says which half is load-bearing. checkCoreApi.js's not-a-core error also asserted that core `main` "has none until the cutover", which stopped being true at the same merge. A reader hitting that message would have gone looking for a cutover that already happened - the kit's own lesson from the CRLF defect, that a failure message naming a diagnosis has to be right about it, applied to the kit's own scripts. Verified both paths: the check still passes against a real core (^1.5.0 vs 1.5.0), and the rewritten message renders as intended. checkLinks, checkRenameSites and checkChapterPaths all clean. Co-Authored-By: Claude <noreply@anthropic.com>
82 lines
3.6 KiB
JavaScript
82 lines
3.6 KiB
JavaScript
#!/usr/bin/env node
|
|
// The kit declares exactly one contract version, in `template/module.json`'s
|
|
// `coreApi` — the same field a reader copies. This asserts it still names the
|
|
// version the pinned core actually exports.
|
|
//
|
|
// WHY EQUALITY AND NOT "SATISFIES": a range check is what CORE does at load time,
|
|
// and it is right there — a module built against 1.4.0 should keep loading into
|
|
// 1.5.0. It is the wrong question here. This kit's job is to be *current*: if core
|
|
// moved to 1.5.0, `^1.4.0` still satisfies, the build stays green, and nobody ever
|
|
// re-reads the chapters. Green would mean "the template still loads", when what we
|
|
// need it to mean is "someone has looked at this since the contract changed".
|
|
//
|
|
// So the failure is deliberate and expected on every core bump, and the fix is a
|
|
// human reading the book — not a version string.
|
|
//
|
|
// Usage: node scripts/checkCoreApi.js --core <path to a core checkout>
|
|
|
|
const fs = require('fs')
|
|
const path = require('path')
|
|
|
|
const ROOT = path.resolve(__dirname, '..')
|
|
|
|
function arg(name) {
|
|
const i = process.argv.indexOf(name)
|
|
return i === -1 ? null : process.argv[i + 1]
|
|
}
|
|
|
|
const corePath = arg('--core')
|
|
if (!corePath) {
|
|
console.error('usage: node scripts/checkCoreApi.js --core <path to a core checkout>')
|
|
process.exit(2)
|
|
}
|
|
|
|
const manifestPath = path.join(ROOT, 'template', 'module.json')
|
|
if (!fs.existsSync(manifestPath)) {
|
|
// Slice 0 landed this check before the template it checks. Not an error: the
|
|
// workflow guards on the same file, and this message is what a local run says.
|
|
console.log('checkCoreApi: no template/module.json yet — nothing to check')
|
|
process.exit(0)
|
|
}
|
|
|
|
const versionFile = path.resolve(corePath, 'server/src/modules/version.js')
|
|
if (!fs.existsSync(versionFile)) {
|
|
console.error(`checkCoreApi: ${versionFile} does not exist.`)
|
|
console.error(' Either --core does not point at a website checkout, or the pin in')
|
|
console.error(' ci/core-ref.json names a core from before the module system existed')
|
|
console.error(' (it reached `main` at the 2026-08-12 cutover, so any ref older than')
|
|
console.error(' that on `main` has no server/src/modules/ at all).')
|
|
process.exit(1)
|
|
}
|
|
|
|
// Core's version.js is a plain CommonJS module with no dependencies, so it can be
|
|
// required straight out of an uninstalled checkout.
|
|
const { MODULE_API_VERSION: core } = require(versionFile)
|
|
|
|
const declared = String(JSON.parse(fs.readFileSync(manifestPath, 'utf8')).coreApi || '')
|
|
// A `coreApi` is a RANGE (`^1.4.0`); the version it is built on is its base.
|
|
const base = declared.replace(/^[\^~>=<\s]+/, '').trim()
|
|
|
|
if (!base) {
|
|
console.error(`checkCoreApi: template/module.json declares no coreApi (got ${JSON.stringify(declared)})`)
|
|
process.exit(1)
|
|
}
|
|
|
|
if (base !== core) {
|
|
console.error('checkCoreApi: the kit is written against a different core than it is pinned to.')
|
|
console.error('')
|
|
console.error(` template/module.json coreApi = ${declared} (base ${base})`)
|
|
console.error(` pinned core MODULE_API_VERSION = ${core}`)
|
|
console.error('')
|
|
console.error(' This is the anti-rot check firing, not a broken build. Someone has to:')
|
|
console.error(' 1. read MODULE_API.md §1.1 for what changed in the new version;')
|
|
console.error(' 2. read the book and the template for anything that is now untrue;')
|
|
console.error(' 3. update template/module.json and ci/core-ref.json together.')
|
|
console.error('')
|
|
console.error(' Bumping the two files without doing step 2 is the one way to make this')
|
|
console.error(' check worthless.')
|
|
process.exit(1)
|
|
}
|
|
|
|
console.log(`checkCoreApi: coreApi ${declared} matches the pinned core's ${core} — OK`)
|