mirror of
https://github.com/CharlesWiltgen/Axiom.git
synced 2026-09-20 19:58:20 +08:00
fix(gate): repair the count I clobbered, and close the drift check's blind spot
Two defects an independent review of the previous two commits found. `4fec1423` changed `claudeCode.commands!.length` to `claudeCode.skills!.length` in the commands loop's summary line, so the gate reported the skill count under a "manifest commands" label — 26 where 17 were checked. The edit was an anchoring slip: the replacement text carried `skills`, and it was accepted against a line reading `commands`. Restored, keeping `?? []` so a manifest without a commands array reports 0 rather than throwing on a non-null assertion. The drift check was one-directional. It iterated only the committed array, so a skill present on disk but absent from the manifest — adding a suite and forgetting to regenerate, the same mistake pointing the other way — passed `npm test` while the unit suite caught it. It now compares the name sets in both directions, and flags a manifest entry with no skill behind it as well. Verified in four states: description drift, a skill missing from the manifest, a manifest entry with no skill on disk, and clean. Each injecting case is caught and names the offending entry; the clean case reports 26 descriptions matching. Verified: npm test (static clean), npm run test:unit (524/524).
This commit is contained in:
+42
-16
@@ -352,7 +352,7 @@ if (claudeCode) {
|
||||
}
|
||||
}
|
||||
console.log(
|
||||
` ✓ ${claudeCode.skills!.length} manifest commands checked against filesystem`,
|
||||
` ✓ ${(claudeCode.commands ?? []).length} manifest commands checked against filesystem`,
|
||||
);
|
||||
|
||||
// Existence is not parity. `skills[]` is generated from each SKILL.md's
|
||||
@@ -363,24 +363,50 @@ if (claudeCode) {
|
||||
// manifest, and in the /axiom:ask generated from it, through a green local
|
||||
// gate and four red CI runs. Compare the text here too, so the cheap local
|
||||
// command catches what CI catches.
|
||||
const expectedDescriptions: Record<string, string> = Object.fromEntries(
|
||||
manifestSkillsFromDisk(
|
||||
pluginDir,
|
||||
(claudeCode.skills ?? []).map((s: { name: string }) => s.name),
|
||||
).map((e) => [e.name, e.description]),
|
||||
const committedSkills: { name: string; description: string }[] =
|
||||
claudeCode.skills ?? [];
|
||||
const generated = manifestSkillsFromDisk(
|
||||
pluginDir,
|
||||
committedSkills.map((s) => s.name),
|
||||
);
|
||||
const drifted = (claudeCode.skills ?? []).filter((skill: { name: string; description: string }) => {
|
||||
const want = expectedDescriptions[skill.name];
|
||||
return want !== undefined && want !== skill.description;
|
||||
});
|
||||
if (drifted.length > 0) {
|
||||
error(
|
||||
"manifest-drift",
|
||||
`${drifted.length} manifest description(s) drifted from SKILL.md frontmatter, starting with "${drifted[0].name}" — regenerate with \`npm run build:manifest\``,
|
||||
);
|
||||
const expectedDescriptions: Record<string, string> = Object.fromEntries(
|
||||
generated.map((e) => [e.name, e.description]),
|
||||
);
|
||||
const drifted = committedSkills.filter(
|
||||
(skill) =>
|
||||
expectedDescriptions[skill.name] !== undefined &&
|
||||
expectedDescriptions[skill.name] !== skill.description,
|
||||
);
|
||||
|
||||
// Both directions. Iterating only the committed array is blind to a skill that
|
||||
// is on disk but missing from the manifest — adding a suite and forgetting to
|
||||
// regenerate, the same mistake pointing the other way — and to an entry whose
|
||||
// skill is gone or manifest-excluded. The unit suite's deepEqual catches those;
|
||||
// this is the cheap command, so it has to as well.
|
||||
const committedNames = new Set(committedSkills.map((s) => s.name));
|
||||
const absent = generated.filter((e) => !committedNames.has(e.name)).map((e) => e.name);
|
||||
const extra = committedSkills
|
||||
.map((s) => s.name)
|
||||
.filter((name) => expectedDescriptions[name] === undefined);
|
||||
|
||||
if (drifted.length > 0 || absent.length > 0 || extra.length > 0) {
|
||||
const detail = [
|
||||
drifted.length > 0
|
||||
? `${drifted.length} description(s) drifted from SKILL.md frontmatter, starting with "${drifted[0].name}"`
|
||||
: "",
|
||||
absent.length > 0
|
||||
? `${absent.length} skill(s) on disk are missing from the manifest, starting with "${absent[0]}"`
|
||||
: "",
|
||||
extra.length > 0
|
||||
? `${extra.length} manifest entr(ies) have no corresponding skill on disk, starting with "${extra[0]}"`
|
||||
: "",
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("; ");
|
||||
error("manifest-drift", `${detail} — regenerate with \`npm run build:manifest\``);
|
||||
} else {
|
||||
console.log(
|
||||
` ✓ ${claudeCode.skills!.length} manifest descriptions match SKILL.md frontmatter`,
|
||||
` ✓ ${committedSkills.length} manifest descriptions match SKILL.md frontmatter`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user