Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 45 additions & 12 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ on:
tags: ['v*']
pull_request:
branches: [main]
schedule:
- cron: '17 4 * * 1'
workflow_dispatch:

permissions:
contents: read
Expand All @@ -15,13 +18,9 @@ concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
test:
lint:
runs-on: ubuntu-latest
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
node-version: ['24.x', '25.x']
env:
CHECK_BASE_REF: origin/main
GH_TOKEN: ${{ github.token }}
Expand All @@ -31,30 +30,64 @@ jobs:
fetch-depth: 0
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
with:
node-version: ${{ matrix.node-version }}
node-version-file: .node-version
cache: 'npm'
cache-dependency-path: package-lock.json
- run: npm ci
- run: npm test
- run: npm run format:check
- if: matrix.node-version == '24.x'
run: mkdir -p artifacts && npm pack --pack-destination artifacts
- run: bash -n scripts/check-markdown.sh
- run: bash scripts/staged-install-verify.sh
- run: git diff --exit-code -- skills/markdown-formatter
- run: npm audit --audit-level=high
- run: npm run verify:urls
- run: npm run check:links
- name: Build tested npm package
run: mkdir -p artifacts && npm pack --pack-destination artifacts
- name: Upload tested npm package
if: matrix.node-version == '24.x'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: npm-package
path: artifacts/*.tgz
if-no-files-found: error

test:
runs-on: ubuntu-latest
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
node-version: ['24.x', '25.x']
env:
CHECK_BASE_REF: origin/main
GH_TOKEN: ${{ github.token }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
fetch-depth: 0
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
cache-dependency-path: package-lock.json
- run: npm ci
- run: npm test

external-contracts:
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
with:
node-version-file: .node-version
cache: 'npm'
cache-dependency-path: package-lock.json
- run: npm ci
- name: Verify evidence URLs
run: npm run verify:urls

publish:
needs: test
needs: [test, lint]
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
timeout-minutes: 5
Expand Down
21 changes: 17 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -444,12 +444,25 @@ compatible minor/patch npm updates and action updates into focused pull
requests; CI remains the merge gate. Review major npm updates separately for
runtime or formatting behavior changes.

### CI and pull requests

Every pull request must pass the deterministic `lint` gate and both runtime
matrix jobs (`test (24.x)` and `test (25.x)`). The lint gate runs formatting,
payload synchronization, dependency audit, offline link checks, and builds the
single npm tarball used by publishing; the runtime jobs focus on the test suite
across supported Node versions. Publishing is allowed only after all three
gates succeed.

Live HTTP checks are intentionally not required for pull requests. The
`external-contracts` job runs weekly and on demand from the Actions tab, so an
upstream outage does not make an otherwise deterministic change unmergeable.

### Evidence URLs

[`docs/evidence-urls.json`](docs/evidence-urls.json) records the external
references used by the README and skill. Unit tests enforce that every
`last_verified` value is no more than 30 days old; CI also performs live HTTP
verification.
`last_verified` value is no more than 30 days old. The scheduled/manual
`external-contracts` job performs live HTTP verification.

After checking the links live, refresh the timestamps with:

Expand Down Expand Up @@ -483,8 +496,8 @@ before removing that token.
Runtime changes must be merged before the isolated version-bump commit.
`release.sh` requires a clean tree, synchronized skill metadata, a stable
`x.y.z` version, an isolated version commit, a pushed `main`, and successful
CI. It creates the tag and GitHub Release; the tag workflow publishes the exact
npm tarball tested by CI.
CI. It creates the tag and GitHub Release; the `publish` job publishes the exact
npm tarball built and tested by CI.

```bash
npm version patch --no-git-tag-version # or minor/major
Expand Down
11 changes: 11 additions & 0 deletions scripts/validators/ci.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ function validateCi(files) {

// Required patterns that must appear
const required = [
{ pattern: /\n\s+lint:\s*\n/i, label: "runs a deterministic lint/package gate" },
{ pattern: /\n\s+external-contracts:\s*\n/i, label: "isolates live external URL checks" },
{ pattern: /npm\s+test\b/i, label: "runs the canonical npm test suite" },
{ pattern: /npm\s+run\s+format:check/i, label: "checks maintainer docs formatting" },
{ pattern: /staged-install-verify\.sh/i, label: "verifies staged runtime payload" },
Expand All @@ -48,6 +50,15 @@ function validateCi(files) {
}
}

// Keep network-dependent evidence checks out of the required runtime matrix.
const testJob = ci.match(/\n\s+test:\s*\n([\s\S]*?)(?=\n\s+[a-z][\w-]*:\s*\n|\s*$)/i);
if (testJob && /verify:urls/i.test(testJob[1])) {
errors.push("ci.yml: live evidence URL checks must run only in external-contracts");
}
if (!/needs:\s*\[\s*test\s*,\s*lint\s*\]/i.test(ci)) {
warnings.push("ci.yml: publish should require both test and lint jobs");
}

// .node-version alignment
const nodeVersionContent = read(".node-version");
const ciNodeVersion = nodeVersionContent
Expand Down