fix: address Copilot review round 6

- `reachableScripts` now follows every npm spelling of `run` — the
  canonical `run-script` and its `run`/`rum`/`urn` aliases — with a unit
  case, so `npm run-script validate:web` appended to `local:gate` fails
  the whole-gate check instead of slipping past it.
- docs/quality-gate.md `verify:skills` row, AGENTS.md skills rule 1 and the
  #2085 narration now describe the actual chain (`validate:guards`, reached
  by both `validate` and `local:validate`) and the qualified promise.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PsmUAZ6aLUAFqenjJKSSRp
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-09-12 20:40:44 -04:00
commit 61fcada059

View file

@ -25,14 +25,17 @@ export function tokenize(command) {
* <name>` references within a single manifest's `scripts`, plus npm's implicit
* `pre<name>`/`post<name>` lifecycle hooks (npm runs those around `<name>`
* without an explicit `npm run`, so a gate moved into e.g. `prevalidate` is
* still reached). A gate harvested from a script that nothing reachable from
* still reached). Every spelling npm accepts for `run` is followed —
* `run-script` is the canonical command and `run`, `rum` and `urn` are its
* aliases — so a chain written as `npm run-script validate:web` reaches the
* same scripts as `npm run validate:web` (Copilot, #2341). A gate harvested from a script that nothing reachable from
* `entry` invokes gates nothing, so callers restrict to this set to assert "CI
* actually runs this", not merely "the script exists".
*/
export function reachableScripts(scripts, entry = "validate") {
const reached = new Set();
const queue = [entry];
const runRef = /npm run ([\w:-]+)/g;
const runRef = /npm (?:run-script|run|rum|urn) ([\w:-]+)/g;
while (queue.length > 0) {
const name = queue.shift();
if (reached.has(name)) continue;