chore: address Copilot review round 6
- verify-skills: the best-effort `plugin validate` hand-off now also requires the EXACT pinned CLI. `local:gate` runs `validate` before the pinned `verify:skills:cli` step, so a newer local CLI could reject skills that CI's pinned validator accepts — and the gate would exit there, never reaching the reproducible check. One schema everywhere, or none. - Dropped `PLUGIN_VALIDATE_MIN_VERSION`: with both validators on the exact pin it was read only by its own test, and a constant nothing enforces is a claim nothing enforces. Its test now pins what actually matters — that equality is distinguishable from newer as well as older. - The CI step's comment described the old floor behavior. - issue-create: the duplicate check searched only open issues, so a completed or rejected match was invisible to a procedure that says never to duplicate. Searches all states and reports state/stateReason. Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
75ab1b069f
commit
c3c8a33e2c
3 changed files with 46 additions and 35 deletions
|
|
@ -237,9 +237,6 @@ export function validateEvalCases(skillName, cases) {
|
|||
*/
|
||||
export const PINNED_CLI_VERSION = "2.1.250";
|
||||
|
||||
/** First Claude Code release with `claude plugin validate`. */
|
||||
export const PLUGIN_VALIDATE_MIN_VERSION = [2, 1, 233];
|
||||
|
||||
/**
|
||||
* Read a semver triple out of `claude --version` output ("2.1.250 (Claude Code)").
|
||||
*
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import {
|
|||
listingCost,
|
||||
parseClaudeVersion,
|
||||
compareVersions,
|
||||
PLUGIN_VALIDATE_MIN_VERSION,
|
||||
PINNED_CLI_VERSION,
|
||||
} from "./skill-manifest.mjs";
|
||||
|
||||
const fm = (body) => `---\n${body}\n---\n\n# Body\n`;
|
||||
|
|
@ -208,16 +208,18 @@ test("parseClaudeVersion reads the CLI's version banner", () => {
|
|||
assert.equal(parseClaudeVersion("no version here"), null);
|
||||
});
|
||||
|
||||
test("compareVersions orders the plugin-validate floor correctly", () => {
|
||||
// An older CLI has no `plugin validate` and exits nonzero on it as an unknown
|
||||
// command; failing the mandatory gate for that would be wrong, so the probe
|
||||
// must skip below the floor rather than run and fail.
|
||||
assert.ok(compareVersions([2, 1, 232], PLUGIN_VALIDATE_MIN_VERSION) < 0);
|
||||
assert.equal(compareVersions([2, 1, 233], PLUGIN_VALIDATE_MIN_VERSION), 0);
|
||||
assert.ok(compareVersions([2, 1, 250], PLUGIN_VALIDATE_MIN_VERSION) > 0);
|
||||
assert.ok(compareVersions([2, 2, 0], PLUGIN_VALIDATE_MIN_VERSION) > 0);
|
||||
assert.ok(compareVersions([3], PLUGIN_VALIDATE_MIN_VERSION) > 0);
|
||||
assert.ok(compareVersions([1, 9, 999], PLUGIN_VALIDATE_MIN_VERSION) < 0);
|
||||
test("compareVersions can decide exact equality with the pin", () => {
|
||||
// Both validators require the EXACT pinned version, so what matters is that
|
||||
// "equal" is distinguishable from newer as well as older — a floor would let a
|
||||
// newer local CLI validate against a different schema than CI's.
|
||||
const pin = parseClaudeVersion(PINNED_CLI_VERSION);
|
||||
assert.equal(compareVersions(pin, pin), 0);
|
||||
assert.ok(compareVersions([pin[0], pin[1], pin[2] - 1], pin) < 0);
|
||||
assert.ok(compareVersions([pin[0], pin[1], pin[2] + 1], pin) > 0);
|
||||
assert.ok(compareVersions([pin[0] + 1, 0, 0], pin) > 0);
|
||||
// Shorter and longer triples still order sanely.
|
||||
assert.ok(compareVersions([pin[0]], pin) < 0);
|
||||
assert.equal(compareVersions([...pin, 0], pin), 0);
|
||||
});
|
||||
|
||||
test("the listing-entry cap covers description AND when_to_use together", () => {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import { fileURLToPath } from "node:url";
|
|||
import {
|
||||
compareVersions,
|
||||
parseClaudeVersion,
|
||||
PLUGIN_VALIDATE_MIN_VERSION,
|
||||
PINNED_CLI_VERSION,
|
||||
parseSkill,
|
||||
validateEvalCases,
|
||||
listingCost,
|
||||
|
|
@ -43,6 +43,9 @@ import {
|
|||
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const DEFAULT_SKILLS_DIR = path.join(ROOT, ".claude", "skills");
|
||||
|
||||
/** `PINNED_CLI_VERSION` as a comparable triple. */
|
||||
const PINNED_VERSION_PARTS = parseClaudeVersion(PINNED_CLI_VERSION) ?? [];
|
||||
|
||||
/** Directory names under `.claude/skills` that contain a SKILL.md. */
|
||||
export function skillDirs(
|
||||
dir,
|
||||
|
|
@ -134,27 +137,30 @@ function main(argv = process.argv.slice(2)) {
|
|||
|
||||
if (override) return;
|
||||
|
||||
// The authoritative parse, when it is available — strictly a bonus. An older
|
||||
// CLI does not have `plugin validate` and exits nonzero on it as an unknown
|
||||
// command, which would fail the MANDATORY gate for a contributor whose only
|
||||
// sin is not having upgraded (Copilot). So gate on the version that
|
||||
// introduced it and skip below it, reserving a failure for a validator that
|
||||
// actually rejected the skills.
|
||||
// The authoritative parse, when it is available — strictly a bonus, and only
|
||||
// from the EXACT pinned CLI.
|
||||
//
|
||||
// `local:gate` runs `validate` (and therefore this) before the pinned
|
||||
// `verify:skills:cli` step, so a local CLI on any other version could reject
|
||||
// skills that CI's pinned validator accepts — and the gate would exit here,
|
||||
// never reaching the reproducible step (Copilot). Accepting merely "new enough"
|
||||
// would reintroduce exactly the cross-machine schema drift the pin removes.
|
||||
// Anything else is left to `verify:skills:cli`, which fetches the pin.
|
||||
const probe = spawnSync("claude", ["--version"], { encoding: "utf8" });
|
||||
if (probe.error || probe.status !== 0) {
|
||||
console.log(
|
||||
"verify:skills — `claude` CLI not usable here; skipped `claude plugin validate`.",
|
||||
"verify:skills — no usable `claude` CLI here; `verify:skills:cli` runs the pinned validator.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
const version = parseClaudeVersion(probe.stdout ?? "");
|
||||
if (
|
||||
version === null ||
|
||||
compareVersions(version, PLUGIN_VALIDATE_MIN_VERSION) < 0
|
||||
compareVersions(version, PINNED_VERSION_PARTS) !== 0
|
||||
) {
|
||||
console.log(
|
||||
`verify:skills — \`claude\` ${version?.join(".") ?? "(unreadable version)"} predates ` +
|
||||
`${PLUGIN_VALIDATE_MIN_VERSION.join(".")}; skipped \`claude plugin validate\`.`,
|
||||
`verify:skills — local \`claude\` is ${version?.join(".") ?? "(unreadable)"}, not the pinned ` +
|
||||
`${PINNED_CLI_VERSION}; \`verify:skills:cli\` runs the pinned validator.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue