mirror of
https://github.com/zernie/vigiles.git
synced 2026-09-14 20:53:57 +08:00
0b79d4825a
The `changes` job was a shell script: one `gh api` call for the changed-file list, two `grep -qvE`, and a fallback for every way that list can come back unusable — empty, failed call, a push whose `before` is all zeros. Every one of those branches is something `dorny/paths-filter` already does, and the edge cases were exactly the half we were maintaining by hand. This is `prefer-existing-solutions` applied to the place it had not been: BUILD was never justified here, it was just what got written first. THE COST, stated because it is real and it is the only one: on a `push` the action diffs with git and therefore needs a checkout, while the old classifier needed none and finished in ~3s. On a `pull_request` it reads the REST API and still needs nothing. `push` in this workflow only ever means a merge into main, so the checkout is paid once per merge and the job still rounds to the same single billable minute. 🔴 `predicate-quantifier: some-with-excludes` IS LOAD-BEARING. Read out of the action's own filter.ts before writing the filters, not assumed: the default is `some`, i.e. `patterns.some(...)`, under which a file beneath site/ matches the `'**'` pattern and sets root=true for a site-only diff — the exact hole the filter exists to prevent, silently. `some-with-excludes` makes a negated pattern a final exclusion ("included by >=1 pattern AND excluded by 0"). Also verified against the action's source rather than guessed: it calls picomatch with `{ dot: true }`. Without that, `**` would not match `.claude/…` and an agent-config-only diff would skip the root jobs. WHAT THE TEST CAN AND CANNOT DO NOW. It could previously re-run the rule by shelling out to `grep` with the workflow's own pattern. With an action it does the nearest honest thing: reads the filters with a YAML parser and evaluates them with the SAME matcher library the action uses, through a transcription of the action's predicate. That transcription is the one copied thing, and its risk is bounded two ways — the action is PINNED to `@v4.0.3` (a test fails on a floating major), and the setting the predicate depends on is asserted separately, because a transcription cannot catch its own premise being absent. The conservative fallback is no longer ours to test, and that is most of the point: the action documents that "all files are considered as added if there is no common ancestor with base branch or no previous commit" — a new branch or a shallow history runs everything. Mutation-proven, each failing exactly its own assertion: - delete `predicate-quantifier` -> `declares predicate-quantifier: …` - unpin `@v4.0.3` to `@v4` -> `pins the action to an exact version` - break `!site/**` to `!nosuch/**` -> `skips the root jobs for a site-only change` `picomatch` + `@types/picomatch` become explicit devDependencies. picomatch was already in the tree transitively; a test resting on an undeclared transitive dependency is a landmine, so it is declared. Gates: npm run check 18/18 · coverage 3975 passed, 0 failed. The 99.94% statement gate fails locally on src/mock-model.ts (untouched; 25 tests skip locally), green on CI. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>