chore: address Copilot review round 2

- verify-skills: version-gate the optional `claude plugin validate`. Probing
  only for the executable failed the MANDATORY gate for anyone on a CLI older
  than 2.1.233, which has no such subcommand and exits nonzero on it. Skips
  below the floor; a failure now means a supported validator rejected the skills.
- skill-eval: match the invoked skill structurally. A substring test counted
  `{"skill":"not-testing"}` as a hit for `testing` and inflated the rate.
- skill-eval: `runOnce` → exported `runPrompt` with an injectable `spawnFn`, so
  the nonzero-exit branch is actually pinned. Reverting it now fails a test
  (verified by mutation) rather than leaving the suite green.
- board-ops: filter the item lookups by repository. Issue numbers are repo-local
  and these are org boards — #11 really does carry a `servers` card — so an
  unfiltered `select` can move or delete the wrong card, or pass two ids.
- issue-triage: the audit's `own()` filter dropped draft cards, which carry no
  `.content.repository`, so the "non-Issue on a board" check reported 0 while
  the invariant was being violated. Verified against a fixture.
- pr-flow: cut the branch from the base it will target. The blanket "cut from
  v2/main" was wrong for v1, whose history is unrelated.

Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-08-29 01:07:41 -04:00
commit ca6828916f

View file

@ -31,6 +31,9 @@ import { spawnSync } from "node:child_process";
import path from "node:path";
import { fileURLToPath } from "node:url";
import {
compareVersions,
parseClaudeVersion,
PLUGIN_VALIDATE_MIN_VERSION,
parseSkill,
validateEvalCases,
listingCost,
@ -121,11 +124,27 @@ function main() {
`(${modelInvoked.join(", ")}); listing ${cost}/${LISTING_BUDGET} chars.`,
);
// The authoritative parse, when it is available.
const probe = spawnSync("claude", ["--version"], { stdio: "ignore" });
if (probe.error) {
// 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.
const probe = spawnSync("claude", ["--version"], { encoding: "utf8" });
if (probe.error || probe.status !== 0) {
console.log(
"verify:skills — `claude` CLI not on PATH; skipped `claude plugin validate`.",
"verify:skills — `claude` CLI not usable here; skipped `claude plugin validate`.",
);
return;
}
const version = parseClaudeVersion(probe.stdout ?? "");
if (
version === null ||
compareVersions(version, PLUGIN_VALIDATE_MIN_VERSION) < 0
) {
console.log(
`verify:skills — \`claude\` ${version?.join(".") ?? "(unreadable version)"} predates ` +
`${PLUGIN_VALIDATE_MIN_VERSION.join(".")}; skipped \`claude plugin validate\`.`,
);
return;
}