refactor(#2268): gen:golden one-command fixture regenerator (#2275)

Phase 3 (convenience form) of golden-parity redesign (epic #2264). Adds npm run gen:golden (regenerates both fixture sets) and points the golden-parity/tree failure messages at it. Full CI-auto-comment deferred (documented in ADR-2264). Closes #2268.
This commit is contained in:
Tom Boucher
2026-07-14 20:18:34 -04:00
committed by GitHub
parent 89b1bef881
commit fc913b37a5
4 changed files with 11 additions and 8 deletions

View File

@@ -90,4 +90,6 @@ Implementing Phase 2 began with an empirical spike classifying every emitted ins
1. **File-set snapshot** — `tests/golden-install-tree.test.cjs` + `tests/fixtures/install-tree/*.json`: a per-runtime sorted list of emitted paths (no hashes), reusing the single-source exclusions via `buildInstallTree` (= sorted keys of `buildParityManifest`). Cheap structural coverage; changes only when the file *set* changes, giving a clean reviewable diff instead of hash noise.
2. **Anti-staleness CI selection** — `scripts/ci-test-scope.cjs` gains a rule selecting `golden-install-parity` + the file-set snapshot whenever an installed-source path changes: the four `gsd-core/` content subtrees (`workflows`, `templates`, `references`, `contexts`) + `gsd-core/bin/shared/*.json`, plus `hooks/**`, `commands/**`, `agents/**`, `skills/**`, and the shipped `scripts/*` files (`scripts/changeset/`, `scripts/lib/`, and three named generators) — not just `src/`/installer paths. `gsd-core/bin/**` (tsc-compiled) is deliberately excluded (already covered by the installer rule). This makes the #2266 silent-staleness class impossible: a source edit that changes emitted output re-verifies the fixtures at PR time.
3. **Regeneration convenience** (Phase 3, #2268) — an `npm run gen:golden` one-command regenerator (both fixture sets, each now self-building `hooks/dist`) that the golden-parity/tree failure messages point at, so drift caught by (2) is a one-command fix. The full CI-auto-comment (auto-posting the fixture patch on drift, jest `--ci` posture) is deferred: it needs a write-token workflow running on PR code (an injection surface) that cannot be verified locally.
**Deferred as an optional future refinement:** the §3-style content-dedup of verbatim copies. The *safe* subset (structurally raw-copied non-`.md`/`.js` data files) is modest, and the broad form is unsound — a `.md` file that is byte-equal today transforms the moment a self-reference is added, producing false property-test failures. Since the anti-staleness rule already removes the root pain (silent stale merges) and the file-set snapshot adds structural coverage, the content-hash golden is retained as-is; churn on *transformed* output is legitimate (you should review transformed output when you change its source).

View File

@@ -90,6 +90,7 @@
"gen:plugin-skills": "node scripts/gen-plugin-skills.cjs --write",
"gen:capability-registry": "node scripts/gen-capability-registry.cjs --write",
"gen:registry": "node scripts/gen-registry.cjs --write",
"gen:golden": "node scripts/gen-golden-install-parity-zcode.cjs && node scripts/gen-install-tree-fixtures.cjs",
"validate:registry": "node scripts/validate-registry.cjs",
"prepack": "npm run build:lib",
"prepare": "npm run build:lib",

View File

@@ -90,7 +90,7 @@ for (const runtime of runtimes) {
if (!fs.existsSync(fixturePath)) {
assert.fail(
`Golden fixture missing for runtime '${runtime}': ${fixturePath}\n` +
'Run UPDATE_GOLDEN=1 node --test tests/golden-install-parity.test.cjs to capture.'
'Run "npm run gen:golden" to generate the fixtures (or, for humans, UPDATE_GOLDEN=1 node --test tests/golden-install-parity.test.cjs).'
);
}
@@ -108,7 +108,7 @@ for (const runtime of runtimes) {
if (added.length) lines.push(` added (${added.length}): ${added.join(', ')}`);
if (removed.length) lines.push(` removed (${removed.length}): ${removed.join(', ')}`);
if (changed.length) lines.push(` changed (${changed.length}): ${changed.join(', ')}`);
lines.push('Run UPDATE_GOLDEN=1 to recapture if the change is intentional.');
lines.push('If the change is intentional, run "npm run gen:golden" to regenerate the fixtures (works without node --test; humans may also use UPDATE_GOLDEN=1 node --test).');
assert.deepEqual(actual, golden, lines.join('\n'));
}
});
@@ -146,7 +146,7 @@ test('golden parity — claude (local legacy layout)', async (t) => {
if (!fs.existsSync(fixturePath)) {
assert.fail(
`Golden fixture missing for claude-local: ${fixturePath}\n` +
'Run UPDATE_GOLDEN=1 node --test tests/golden-install-parity.test.cjs to capture.',
'Run "npm run gen:golden" to generate the fixtures (or, for humans, UPDATE_GOLDEN=1 node --test tests/golden-install-parity.test.cjs).',
);
}
@@ -159,7 +159,7 @@ test('golden parity — claude (local legacy layout)', async (t) => {
if (added.length) lines.push(` added (${added.length}): ${added.join(', ')}`);
if (removed.length) lines.push(` removed (${removed.length}): ${removed.join(', ')}`);
if (changed.length) lines.push(` changed (${changed.length}): ${changed.join(', ')}`);
lines.push('Run UPDATE_GOLDEN=1 to recapture if the change is intentional.');
lines.push('If the change is intentional, run "npm run gen:golden" to regenerate the fixtures (works without node --test; humans may also use UPDATE_GOLDEN=1 node --test).');
assert.deepEqual(actual, golden, lines.join('\n'));
}
});

View File

@@ -86,7 +86,7 @@ for (const runtime of runtimes) {
if (!fs.existsSync(fixturePath)) {
assert.fail(
`Install-tree fixture missing for runtime '${runtime}': ${fixturePath}\n` +
'Run UPDATE_INSTALL_TREE=1 node --test tests/golden-install-tree.test.cjs to capture.'
'Run "npm run gen:golden" to generate the fixtures (or, for humans, UPDATE_INSTALL_TREE=1 node --test tests/golden-install-tree.test.cjs).'
);
}
@@ -98,7 +98,7 @@ for (const runtime of runtimes) {
const lines = [`Install-tree mismatch for runtime '${runtime}':`];
if (added.length) lines.push(` added (${added.length}): ${added.join(', ')}`);
if (removed.length) lines.push(` removed (${removed.length}): ${removed.join(', ')}`);
lines.push('Run UPDATE_INSTALL_TREE=1 to recapture if the change is intentional.');
lines.push('If the change is intentional, run "npm run gen:golden" to regenerate the fixtures (works without node --test; humans may also use UPDATE_INSTALL_TREE=1 node --test).');
assert.deepEqual(actual, fixture, lines.join('\n'));
}
});
@@ -132,7 +132,7 @@ test('install tree — claude (local legacy layout)', async (t) => {
if (!fs.existsSync(fixturePath)) {
assert.fail(
`Install-tree fixture missing for claude-local: ${fixturePath}\n` +
'Run UPDATE_INSTALL_TREE=1 node --test tests/golden-install-tree.test.cjs to capture.',
'Run "npm run gen:golden" to generate the fixtures (or, for humans, UPDATE_INSTALL_TREE=1 node --test tests/golden-install-tree.test.cjs).',
);
}
@@ -142,7 +142,7 @@ test('install tree — claude (local legacy layout)', async (t) => {
const lines = ['Install-tree mismatch for claude-local:'];
if (added.length) lines.push(` added (${added.length}): ${added.join(', ')}`);
if (removed.length) lines.push(` removed (${removed.length}): ${removed.join(', ')}`);
lines.push('Run UPDATE_INSTALL_TREE=1 to recapture if the change is intentional.');
lines.push('If the change is intentional, run "npm run gen:golden" to regenerate the fixtures (works without node --test; humans may also use UPDATE_INSTALL_TREE=1 node --test).');
assert.deepEqual(actual, fixture, lines.join('\n'));
}
});