Skip to content

Commit 77fb046

Browse files
authored
Merge pull request #65 from olehsvyrydov/ci/validate-gates
Run the gates that were being run by hand
2 parents d2b7610 + f888e34 commit 77fb046

4 files changed

Lines changed: 387 additions & 0 deletions

File tree

.github/workflows/validate.yml

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
name: Validate
2+
3+
# The skills in this repository are markdown: nothing compiles and no test suite
4+
# runs, so a defect stays invisible until someone reads the exact line. Until
5+
# now every gate was run by hand, which means they were run when someone
6+
# remembered. These are the same checks, on every change.
7+
8+
on:
9+
pull_request:
10+
push:
11+
branches: [main]
12+
13+
permissions:
14+
contents: read
15+
16+
jobs:
17+
gates:
18+
name: Framework gates
19+
runs-on: ubuntu-latest
20+
steps:
21+
- uses: actions/checkout@v4
22+
23+
- uses: actions/setup-python@v5
24+
with:
25+
python-version: "3.12"
26+
27+
- name: Install PyYAML
28+
run: pip install --quiet pyyaml
29+
30+
# Frontmatter, manifests, counts, links, credential shapes, retired
31+
# mechanisms, and the vendor-neutrality boundary. Each gate exists
32+
# because the thing it checks actually broke once.
33+
- name: Framework gates
34+
run: python3 scripts/validate-framework.py
35+
36+
plugin-validate:
37+
name: claude plugin validate
38+
runs-on: ubuntu-latest
39+
steps:
40+
- uses: actions/checkout@v4
41+
42+
- uses: actions/setup-node@v4
43+
with:
44+
node-version: "22"
45+
46+
# Pinned on purpose. An unpinned validator means a green pull request can
47+
# turn red tomorrow with no change in this repository, and a behaviour
48+
# change in the checker arrives silently. Bump it deliberately.
49+
- name: Install Claude Code
50+
env:
51+
CLAUDE_CODE_VERSION: 2.1.221
52+
run: npm install --global --no-fund --no-audit "@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}"
53+
54+
# Runs without credentials — it reads the plugin off disk. This is the
55+
# check that first caught a skill whose frontmatter did not parse, which
56+
# had been loading with empty metadata and could never auto-trigger.
57+
- name: Validate the plugin
58+
run: claude plugin validate ./claude
59+
60+
installer:
61+
name: install.sh still works
62+
runs-on: ubuntu-latest
63+
steps:
64+
- uses: actions/checkout@v4
65+
66+
# install.sh is the path for Cursor, Kiro and VS Code and stays supported
67+
# through the plugin transition, so it must keep running.
68+
- name: Dry-run every editor
69+
run: ./install.sh --dry-run --editors=all --scope=project --yes

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ This file records all notable changes to the project. Versioning roughly follows
44

55
## [Unreleased]
66

7+
### Added
8+
- **CI.** The skills here are markdown: nothing compiles and no test suite runs, so a defect stays invisible until someone reads the exact line. Every gate was previously run by hand, which means it was run when someone remembered — and the record of what shipped was reconciled against the tree only after it had drifted nine behind. Three jobs now run on every pull request: `scripts/validate-framework.py`, `claude plugin validate` (which needs no credentials), and `install.sh --dry-run --editors=all`.
9+
- **`scripts/validate-framework.py`** — seven gates, each covering something that has actually broken in this repository: a `SKILL.md` whose frontmatter does not parse and therefore loads with empty metadata and can never auto-trigger; malformed plugin JSON, or a `version` key reappearing where it is omitted on purpose; a documented count that no longer matches the tree; a relative link pointing at nothing; a credential-shaped file being tracked in a public repository; a reference to a mechanism that was removed; and a backend product named inside a skill rather than in its adapter reference. Every gate was verified to fail on its own defect and pass when clean, because a gate that cannot fail is decoration.
10+
711
## [5.2.0] — 2026-08-03
812

913
Distribution moves from copying files to a versioned plugin, and three skills that documented

CONTRIBUTING.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,31 @@ Key ideas:
6565
```
6666
Then invoke the agent/command in your editor and confirm it behaves as intended.
6767

68+
### Run the gates before you push
69+
70+
CI runs these on every pull request; running them locally is faster than a round trip.
71+
72+
```bash
73+
pip install pyyaml # once
74+
python3 scripts/validate-framework.py # the framework gates
75+
claude plugin validate ./claude # the plugin manifest + component check
76+
./install.sh --dry-run --editors=all --scope=project --yes
77+
```
78+
79+
`validate-framework.py` checks seven things, each of which has actually broken here at least once:
80+
81+
| Gate | Catches |
82+
|------|---------|
83+
| G1 frontmatter | A `SKILL.md` whose YAML does not parse. It then loads with **empty metadata** — no description, so it can never auto-trigger, while looking correct in the file |
84+
| G2 manifests | Malformed plugin JSON, and a `version` key reappearing in `plugin.json` (omitted on purpose so the commit SHA is the version) |
85+
| G3 counts | A documented skill/command/template count that no longer matches the tree |
86+
| G4 links | A relative `.md` link pointing at nothing. Fenced blocks are skipped, since template examples are illustrative |
87+
| G5 credentials | A credential-shaped file being tracked in a public repository |
88+
| G6 retired | A reference to a mechanism that was removed — a promise the repo no longer keeps |
89+
| G7 vendor-neutrality | A backend product named inside a skill instead of in its adapter reference |
90+
91+
If a gate is wrong, change the gate deliberately and say why in the commit — do not work around it.
92+
6893
## Pull requests & commits
6994

7095
- Branch from `main`: `feat/<short-desc>` or `fix/<short-desc>`.

0 commit comments

Comments
 (0)