Files
zernie__vigiles/.github/workflows/release.yml
zernie 6532dbf48d fix: five defects found dogfooding vigiles on a real consumer repo, plus CI job bounds (#162)
Five defects found dogfooding vigiles on a real consumer repo (zernie/mine — 43 skills,
48 harness files), each measured rather than reasoned about:

1. `exclude` never reached the markdown-refs pass. Measured: 9 broken refs, 8 of them
   inside a directory explicitly listed in `exclude`, all 9 still reported. After: 9 → 0.
   The consequence was larger than the cause — with no way to reach exit 0 the consumer
   set `continue-on-error: true`, and a lint whose exit code is discarded gates nothing.

2. Refs were found by TEXT, per line. Four of six probe inputs classified wrongly
   (`ctx.file("OUT")`, `obj.cmd(...)`, a comment, a string), and because the regex ran
   per line, any call split across lines was invisible — which had hidden two genuinely
   broken refs in this repo's own docs. Now parsed: a ref is a call whose callee is a
   bare identifier with a string-literal first argument. `typescript` was already a
   runtime dependency. Net: 21 refs → 25, all valid.

3. The script runner was a strict queue. Measured on 48 harness files: 270s → 77s
   (3.5x), byte-identical results. The default follows `entry` rather than a new flag:
   `test` fans out (free and deterministic by construction — mock model, no API key),
   `eval` stays at 1 (real model quota). `stdio: "inherit"` was the actual blocker.

4. Agent path attribution was keyed by name, so with `agents/foo.md` and
   `.claude/agents/foo.md` a lethal-trifecta finding was displayed against the wrong
   file. Ambiguous names now yield no path instead of an arbitrary one.

5. `compile` emitted invalid YAML. A description containing `: ` produced frontmatter
   where `skillContract()` reports `malformed: true, declared: []` — the blessed path
   producing the exact defect `frontmatter-valid` exists to report, and punishing anyone
   who fixed it by hand with a hash mismatch. Fixed by round-tripping through the same
   loader the linter uses, so already-safe values stay byte-identical and no integrity
   hash moves.

Plus: all 10 jobs across 6 workflows were unbounded and now carry `timeout-minutes`
sized to measured durations; docs updated in the pages that own each defect.

Gates: vitest 3186 passed - lint 0 errors - prettier clean - api:check no drift - build.
CI: all 7 jobs green.
2026-08-19 22:48:28 +05:00

83 lines
3.4 KiB
YAML

name: Release
on:
push:
branches: [main]
permissions:
contents: write
issues: write
pull-requests: write
jobs:
release:
runs-on: ubuntu-latest
# Bounded 2026-08-19. Every job in this repository was unbounded, so a hung one
# ran to GitHub's six-hour ceiling — the one failure mode a green checkmark
# cannot show you, because it does not fail, it bills. Sizes are ~3-4x the
# MEASURED duration on this repo's own runs, quoted per job so a later edit can
# see where the number came from instead of guessing again.
# not measured; publishes to npm, so bounded generously but bounded.
timeout-minutes: 20
# Skip release on commits that don't affect the package
if: "!contains(github.event.head_commit.message, '[skip ci]')"
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
# 🔴 20 → 22 on 2026-08-16, because the release job DIED on this line and took a
# major with it. `semantic-release` refuses to start on Node 20:
#
# [semantic-release]: node version ^22.14.0 || >= 24.10.0 is required. Found v20.20.2.
#
# It fails BEFORE analysing a single commit, so `fix(release)!:` in #152 — the commit
# that repaired the preset so `!` means major at all — never became v16.0.0. npm sat
# at 15.4.1 while `main` carried breaking changes, which is the exact condition #152
# existed to end.
#
# ⚠️ THE REAL DEFECT IS NOT THIS NUMBER. `semantic-release` is not in package.json at
# all, so `npx semantic-release` resolves the newest published version on every run.
# The release pipeline therefore changes behaviour with no commit to this repository:
# run 132 passed at 19:30 and run 133 failed at 21:24 on the same config, because a
# release in between dropped Node 20. Bumping the runtime unblocks today and leaves
# that open — pinning the tool as a devDependency is the fix that closes it.
node-version: "22"
cache: "npm"
- run: npm ci
- name: Build
run: npm run build
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npx semantic-release
- name: Update floating major-version tag for the Action
# Marketplace consumers pin `uses: zernie/vigiles@v1`. Point that
# floating tag at the newest released vX.Y.Z so @v1 always tracks the
# latest compatible release. Idempotent: a no-op when already current.
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
git fetch --tags --force
latest="$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | head -n1)"
if [ -z "$latest" ]; then
echo "No version tags yet — skipping major-tag update."
exit 0
fi
major="${latest%%.*}" # v1.2.3 -> v1
if [ "$(git rev-parse -q --verify "refs/tags/$major" || true)" = "$(git rev-parse "$latest")" ]; then
echo "$major already points at $latest — nothing to do."
exit 0
fi
echo "Pointing $major at $latest"
git tag -f "$major" "$latest"
git push origin -f "refs/tags/$major"