Files
fusion/scripts/__tests__/check-runtime-skill-loader-drift.test.mjs
gsxdsm f1fe399184 FN-8991: add runtime skill-loader drift gate
Enforce the intentional Claude-to-Grok runtime skill-loader clone relationship across verification lanes.

- Add an exact rename-diff validator with fixture and live-loader coverage.
- Run the validator in pretest, fast verification, and static merge-gate checks.
- Document the loader duplication contract and expanded static-validator inventory.

Files changed:
 AGENTS.md                                          |   2 +
 docs/testing.md                                    |   4 +-
 package.json                                       |   7 +-
 .../check-runtime-skill-loader-drift.test.mjs      | 113 +++++++++++++++++++++
 scripts/__tests__/run-static-gate-checks.test.mjs  |   1 +
 scripts/__tests__/verify-fast.test.mjs             |   1 +
 scripts/check-runtime-skill-loader-drift.mjs       | 113 +++++++++++++++++++++
 7 files changed, 236 insertions(+), 5 deletions(-)

Fusion-Task-Id: FN-8991

Fusion-Task-Lineage: a3b67752-0043-4336-9a2a-ff391672b31f

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-11 05:23:02 -07:00

114 lines
4.2 KiB
JavaScript

import test from "node:test";
import assert from "node:assert/strict";
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { main } from "../check-runtime-skill-loader-drift.mjs";
function loader(name, { sentinel = true, extra = "" } = {}) {
const sessionName = sentinel ? `stage${name}SessionSkills` : "stageSessionSkills";
return [
`export function ${sessionName}() { return "${name}"; }`,
`const runtime = "${name.toLowerCase()}";`,
...Array.from({ length: 100 }, (_, index) => `// ${name} loader line ${index + 1}`),
extra,
].join("\n");
}
function fixture(claude, grok) {
const root = mkdtempSync(join(tmpdir(), "runtime-skill-loader-drift-"));
const claudePath = join(root, "claude.ts");
const grokPath = join(root, "grok.ts");
writeFileSync(claudePath, claude);
writeFileSync(grokPath, grok);
return { root, claudePath, grokPath };
}
function captureErrors(callback) {
const errors = [];
const original = console.error;
console.error = (message) => errors.push(String(message));
try {
return { result: callback(), errors };
} finally {
console.error = original;
}
}
function withFixture(claude, grok, assertion) {
const files = fixture(claude, grok);
try {
assertion(files);
} finally {
rmSync(files.root, { recursive: true, force: true });
}
}
test("clean Claude-to-Grok rename pair passes", () => {
const claude = loader("Claude");
withFixture(claude, claude.replace(/Claude/g, "Grok").replace(/claude/g, "grok"), ({ claudePath, grokPath }) => {
assert.equal(main([], { claudePath, grokPath }), 0);
});
});
test("one-sided edit reports its differing line", () => {
const claude = loader("Claude");
const grok = `${claude.replace(/Claude/g, "Grok").replace(/claude/g, "grok")}\nif (process.platform === "darwin") stageGrokSessionSkills();`;
withFixture(claude, grok, ({ claudePath, grokPath }) => {
const { result, errors } = captureErrors(() => main([], { claudePath, grokPath }));
assert.equal(result, 1);
assert.match(errors.join("\n"), /first difference: line 104/);
});
});
test("raw Grok token in Claude copy fails the cross-name guard", () => {
const claude = `${loader("Claude")}\nconst accidental = "Grok";`;
withFixture(claude, loader("Grok"), ({ claudePath, grokPath }) => {
const { result, errors } = captureErrors(() => main([], { claudePath, grokPath }));
assert.equal(result, 1);
assert.match(errors.join("\n"), /raw Grok\/grok token/);
});
});
test("missing input refuses to report success", () => {
const files = fixture(loader("Claude"), loader("Grok"));
try {
const { result, errors } = captureErrors(() => main([], { claudePath: join(files.root, "missing.ts"), grokPath: files.grokPath }));
assert.equal(result, 1);
assert.match(errors.join("\n"), /refusing to report success: unable to read Claude loader/);
} finally {
rmSync(files.root, { recursive: true, force: true });
}
});
test("empty or below-floor input refuses to report success", () => {
withFixture("", loader("Grok"), ({ claudePath, grokPath }) => {
const { result, errors } = captureErrors(() => main([], { claudePath, grokPath }));
assert.equal(result, 1);
assert.match(errors.join("\n"), /non-empty and contain at least 100 lines/);
});
});
test("zero-substitution Claude fixture refuses to report success", () => {
const generic = loader("Generic", { sentinel: false });
withFixture(generic, generic, ({ claudePath, grokPath }) => {
const { result, errors } = captureErrors(() => main([], { claudePath, grokPath }));
assert.equal(result, 1);
assert.match(errors.join("\n"), /canonicalization made zero Claude\/claude substitutions/);
});
});
test("missing sentinel export refuses to report success", () => {
const claude = loader("Claude", { sentinel: false });
withFixture(claude, claude.replace(/Claude/g, "Grok").replace(/claude/g, "grok"), ({ claudePath, grokPath }) => {
const { result, errors } = captureErrors(() => main([], { claudePath, grokPath }));
assert.equal(result, 1);
assert.match(errors.join("\n"), /expected sentinel exports/);
});
});
test("real runtime loaders remain a clean rename pair", () => {
assert.equal(main(), 0);
});