The gate matched the role word only as `Driver`. Lines written in capitals, such as `DRIVER: "…"`, `THE DRIVER'S …` or a `…-DRIVER-REPORTED` card id, never registered: 42 of them sat in shipped text while the owners' sweeps, measured by this gate, read as nearly finished. research-dashboard alone carries 24, most of them verbatim quotes. The pattern is now `\b(?:Driver|DRIVER)\b`. A GPU or device driver, WebDriver and DRIVER_MERGE still do not match, and the self-test now plants the capitals form as well. Three of the 42 are reworded in this round. The other 39 join the baseline in a one-time re-measure, the only time it has grown: 15 of the 21 baselined lines remain, the 6 that repo's docs sweep removed are gone, and every added key is a capitals-only line. It holds 54 lines in 20 files and only shrinks from here. Two of the additions are code terms rather than citations (a "CAT-1 DRIVER CONTRACT", a "BATCH-UNION DRIVER"); their owners can reword them in their sweeps.
111 lines
6.3 KiB
JavaScript
111 lines
6.3 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* check-no-new-driver-references — shipped text neither quotes nor cites the Driver, and the references
|
|
* already in it may only go away.
|
|
*
|
|
* The rule: a comment, doc, UI string or commit says what the code does and why, never who said what.
|
|
* An open decision reads "provisional, pending a product decision". Measured on 2026-09-29, several
|
|
* hundred lines already referred to the Driver. Owners remove them in one sweep per area before the
|
|
* public giveaway; until then this gate holds the line FORWARD-ONLY:
|
|
* - a line that refers to the Driver and is not in the baseline FAILS (a new reference);
|
|
* - a baselined line that is gone is fine (owners sweep in parallel); --shrink tidies the baseline.
|
|
*
|
|
* WHAT COUNTS: the role word as a whole word, written `Driver` or in capitals, `DRIVER` (not a GPU or
|
|
* device driver, not WebDriver, not an env name like DRIVER_MERGE). KEYED by file plus the line's
|
|
* normalised text, with a count, not by line number, so edits elsewhere in a file never trip it; a new
|
|
* line with the same text in the same file raises the count and fails.
|
|
*
|
|
* ⚠ THE CAPITALS FORM WAS MISSING AT FIRST. The first pattern matched only `Driver`, so 42 lines written
|
|
* `DRIVER: "…"`, `THE DRIVER'S …` or `DRIVER-REPORTED` passed unseen, and a sweep measured by this gate
|
|
* read as finished while they stayed. The baseline was re-measured once with the wider pattern, the one
|
|
* time it has grown; it only shrinks from here.
|
|
*
|
|
* node internal/check-no-new-driver-references.mjs check
|
|
* node internal/check-no-new-driver-references.mjs --shrink drop what is gone (never adds)
|
|
* node internal/check-no-new-driver-references.mjs --init write the baseline, only if none exists
|
|
*
|
|
* Node only. Exit 0 pass · 1 fail.
|
|
*/
|
|
import { readFileSync, writeFileSync, existsSync } from 'node:fs'
|
|
import { execFileSync } from 'node:child_process'
|
|
import { join, resolve, dirname } from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..')
|
|
const SELF = 'internal/check-no-new-driver-references.mjs'
|
|
const BASELINE_REL = 'internal/fixtures/driver-references-baseline.json'
|
|
const BASELINE = join(ROOT, BASELINE_REL)
|
|
const ROLE = /\b(?:Driver|DRIVER)\b/
|
|
const AREAS = ['library', 'plugin-designs', 'internal', 'documentation', 'skills', 'README.md']
|
|
|
|
export function referencesIn(text) {
|
|
const out = []
|
|
text.split('\n').forEach((line, i) => {
|
|
if (ROLE.test(line)) out.push({ line: i + 1, norm: line.replace(/\s+/g, ' ').trim() })
|
|
})
|
|
return out
|
|
}
|
|
const keyOf = (file, norm) => `${file}\u0000${norm}`
|
|
|
|
function shippedFiles() {
|
|
const listed = execFileSync('git', ['-C', ROOT, 'ls-files', ...AREAS], { encoding: 'utf8' })
|
|
return listed.split('\n').filter((f) => /\.(vue|js|mjs|md|json|html|css)$/.test(f) && !f.includes('/data/')
|
|
&& f !== SELF && f !== BASELINE_REL)
|
|
}
|
|
|
|
function tally(files, read) {
|
|
const counts = new Map(); const where = new Map()
|
|
for (const f of files) {
|
|
let text
|
|
try { text = read(f) } catch { continue }
|
|
for (const r of referencesIn(text)) {
|
|
const k = keyOf(f, r.norm)
|
|
counts.set(k, (counts.get(k) || 0) + 1)
|
|
if (!where.has(k)) where.set(k, { file: f, line: r.line, text: r.norm })
|
|
}
|
|
}
|
|
return { counts, where }
|
|
}
|
|
|
|
// ── self-test: the role word is found, a device driver is not, and a new line beside a baselined one is isolated
|
|
{
|
|
const found = referencesIn('// the Driver ruled that numerals stay en-US\n// THE DRIVER\'S ASK\n// the GPU driver and WebDriver and DRIVER_MERGE\n')
|
|
if (found.length !== 2) { console.error('DRIVER-REFS: SELF-TEST FAILED — the role word (either case) is not told apart from other drivers'); process.exit(1) }
|
|
const { counts } = tally(['a.vue'], () => '// Driver: old\n// Driver: old\n// Driver: new\n')
|
|
const base = new Map([[keyOf('a.vue', '// Driver: old'), 2]])
|
|
const fresh = [...counts].filter(([k, n]) => n > (base.get(k) || 0))
|
|
if (fresh.length !== 1 || !fresh[0][0].endsWith('new')) { console.error('DRIVER-REFS: SELF-TEST FAILED — a new reference beside baselined ones was not isolated'); process.exit(1) }
|
|
}
|
|
|
|
const { counts, where } = tally(shippedFiles(), (f) => readFileSync(join(ROOT, f), 'utf8'))
|
|
const args = process.argv.slice(2)
|
|
const toObj = (m) => Object.fromEntries([...m].sort(([a], [b]) => (a < b ? -1 : 1)))
|
|
|
|
if (args.includes('--init')) {
|
|
if (existsSync(BASELINE)) { console.error(`DRIVER-REFS: --init refused, ${BASELINE_REL} exists (use --shrink)`); process.exit(1) }
|
|
writeFileSync(BASELINE, JSON.stringify({ about: 'Lines in shipped text that referred to the Driver on 2026-09-29 (re-measured that day when the pattern learned the capitals form, DRIVER), keyed by file and text, to be removed by the owners\' sweep before the public giveaway. Forward-only: this list may only shrink (--shrink).', counts: toObj(counts) }, null, 1) + '\n')
|
|
console.log(`DRIVER-REFS: baseline written: ${counts.size} line(s) in ${new Set([...where.values()].map((w) => w.file)).size} file(s)`)
|
|
process.exit(0)
|
|
}
|
|
|
|
const baseline = existsSync(BASELINE) ? JSON.parse(readFileSync(BASELINE, 'utf8')) : { counts: {} }
|
|
const base = new Map(Object.entries(baseline.counts || {}))
|
|
|
|
if (args.includes('--shrink')) {
|
|
const kept = new Map()
|
|
for (const [k, n] of base) { const now = counts.get(k) || 0; if (now > 0) kept.set(k, Math.min(n, now)) }
|
|
writeFileSync(BASELINE, JSON.stringify({ ...baseline, counts: toObj(kept) }, null, 1) + '\n')
|
|
console.log(`DRIVER-REFS: baseline shrunk ${base.size} -> ${kept.size} line(s) (it never grows)`)
|
|
process.exit(0)
|
|
}
|
|
|
|
const fresh = [...counts].filter(([k, n]) => n > (base.get(k) || 0)).map(([k]) => where.get(k))
|
|
const gone = [...base.keys()].filter((k) => !counts.has(k)).length
|
|
if (fresh.length) {
|
|
console.error(`DRIVER-REFS: FAIL — ${fresh.length} new reference(s) to the Driver in shipped text:`)
|
|
for (const w of fresh.slice(0, 20)) console.error(` ✗ ${w.file}:${w.line} ${w.text.slice(0, 120)}`)
|
|
console.error(' Keep the engineering reason and drop the speaker; an open decision reads "provisional, pending a product decision".')
|
|
process.exit(1)
|
|
}
|
|
console.log(`DRIVER-REFS: PASS — no new reference to the Driver; ${base.size - gone} older line(s) still baselined for the sweep`
|
|
+ (gone ? `, ${gone} already swept (tidy with --shrink)` : '') + '.')
|