chore: address Copilot review round 17

- ciRunsUnconditionally never looked at the workflow-level `on:` trigger, so a
  workflow switched to `on: release` still contained the step and reported as
  wired while no PR ever ran the validator. It now requires `push` or
  `pull_request`, handling all three shapes `on:` takes. The positive fixtures
  had no trigger at all, so they could not assert the "on every push" half of
  the contract; they carry one now, alongside a release-only negative. Verified
  by mutation.
- Stale JSDoc from the round-16 version change: `probeClaudeVersion` is now
  generic in its parser's result rather than hard-coding `number[]`,
  `validatorCommand` documents `{ parts, prerelease }`, and `runValidator`
  documents the `platform` option it already accepted. Annotations that lie make
  valid call sites look type-invalid.

Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-08-29 04:54:04 -04:00
commit 1203246537

View file

@ -82,9 +82,35 @@ export function runsCommand(script, command) {
.some((segment) => segment === command);
}
/**
* Whether a workflow's `on:` includes an event that fires for ordinary changes.
*
* `on:` takes three shapes — a bare string, a list, or a map of event names.
* Note the YAML 1.1 `on` → `true` pitfall does not apply here: the `yaml`
* package parses to the 1.2 core schema, where the key stays the string `on`.
*
* @param {unknown} on
*/
function triggersOnPush(on) {
const events =
typeof on === "string"
? [on]
: Array.isArray(on)
? on
: on !== null && typeof on === "object"
? Object.keys(on)
: [];
return events.some((e) => e === "push" || e === "pull_request");
}
/**
* Whether the workflow has a step that runs `command` on every push.
*
* Three things must hold, and the guard has needed each one added in turn:
* the WORKFLOW must fire on a push or pull_request (one switched to `on:
* release` still contains the step but never runs it on a PR — Copilot), the
* JOB must be unconditional, and so must the STEP.
*
* Deliberately conservative: a job or step carrying **any** `if:` is skipped.
* This guard cannot evaluate an Actions expression, and the property being
* asserted is "CI validates the skills on a PR" — which a conditional step does
@ -102,6 +128,8 @@ export function ciRunsUnconditionally(workflowText, command) {
} catch {
return false;
}
if (!triggersOnPush(doc?.on)) return false;
const jobs = doc?.jobs;
if (jobs === null || typeof jobs !== "object") return false;