Skip to content

test(skills): guard the version literals in skills/, and stop measuring with the buggy release - #123

Merged
hhkaos merged 1 commit into
mainfrom
chore/skill-version-pin-guard
Nov 6, 2026
Merged

hhkaos merged 1 commit into
mainfrom
chore/skill-version-pin-guard

Conversation

@hhkaos

@hhkaos hhkaos commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

skills/ carries version literals that nothing checked: the npx -p @opentechevents/<pkg>@<minor> lines an organizer actually runs, a pinned embed URL, a specVersion in an example an agent will copy. They had drifted, because the rule asking for the pins to move lives in SKILL.md and the pins live in references/.

This replaces that rule with a test.

What was wrong

Was Ships
references/website.md export-jsonld@0.4 ×2 0.6.0
references/website.md embed/v0.7.0 ×2 0.8.2
references/writing.md the canonical minimum writes specVersion: 0.4.0 0.6.0
references/website.md "application/ote+json — what v0.4 documents" spec is on 0.6
evals/test/assertions.test.ts the pins it claims the skill prescribes, on @0.4 0.6.0

And the one that mattered most: fileIsValid measured ground truth with the release whose version bug this kit exists to have fixed. It fetched the validate package on the 0.4 line, which judges a document against the newest release instead of the version it declares — so a correct 0.5 or 0.6 document came out broken, and a green run meant nothing. The eval README warns about exactly this ("an uncalibrated instrument turns a real regression into a green run") one screen above the pin that did it.

The guard

skills/evals/test/version-pins.test.ts — a plain unit test, no agent, no API key, runs in CI with everything else. It lists nothing: every expectation is derived at run time from the workspace's own package.json versions and from packages/validate's support policy, and failures name the file, the line and the value to write.

  • npm pins name a package this repo ships, on the line it ships (minor or exact both fine).
  • a pinned embed/v<x.y.z> URL matches apps/embed and has a committed versions/v<x.y.z>/ snapshot behind it — the skill tells organizers to put that URL in a live <script> tag.
  • a specVersion in anything skills/ote-event/ tells an organizer to write is the latest published version.
  • no .md names a bare spec minor in prose: "what v0.4 documents" is read as current by whoever reads it.

No per-line ignore mechanism, deliberately — a marker that silences this scan would be reached for on the day the scan is right. A comment that needs to discuss an old pin spells it in words ("the 0.4 line"). The guard caught its own comments on the first run, and that is how they were fixed.

The failure nobody would have seen

An eval fixture pinned to a spec version changes meaning when the support window moves. corunajug-feed.json was stamped 0.3.0 to be the "older but supported — a notice, never a defect" case. The day 0.5.0 and 0.6.0 shipped together, the window slid past it. The fixture still parsed, the case still ran, and it had quietly become a migration case still asserting the opposite — today it exits 3.

Fixtures now declare a role (latest / older-supported / none) that the test resolves against the live policy, so a window move names the file to re-stamp instead of silently rewriting what a case tests. Re-stamped to 0.4.0 (✓ valid against OTE 0.4.0, with the notice), and its watch_for rewritten — it described the pre-0.6 CLI behaviour as "live today".

fileIsValid now imports validateDocument from the workspace rather than pinning a published release. An instrument must be the code under review, not a copy of it from npm, and an unpinned import cannot drift. The agent-facing pins stay literal on purpose: they say which release the instructions were tested with.

Verification

By simulation, not by reading. Bumping validate to 0.7.0 and embed to 0.9.0 and re-stamping the fixture:

skills/ote-event/SKILL.md:17 pins @opentechevents/validate@0.6, workspace ships 0.7.0
skills/ote-event/references/website.md:92 pins embed v0.8.2, workspace ships v0.9.0
corunajug-feed.json must not be the latest release

Inserting a bare v0.5 into prose trips the prose rule with its file and line. Both reverted. pnpm lint, pnpm build, pnpm typecheck, pnpm test green (31 tests in skill-evals).

CLAUDE.md gets the procedure: the command, the five rules, and the fixture-role rule that matters on a release day — re-stamp the fixture, never move the bar.

What this does not fix

Found while investigating, filed rather than fixed here:

🤖 Generated with Claude Code

…ng with the buggy release

A skill is prose, and prose carries version literals nothing checks: the
`npx -p @opentechevents/<pkg>@<minor>` lines an organizer actually runs, a
pinned embed URL, a `specVersion` in an example an agent will copy. Each one
was right the day it was written. They drifted exactly as you would expect,
because the rule asking for the pins to move lived in SKILL.md and the pins
lived in references/:

  - references/website.md pinned export-jsonld@0.4 and embed v0.7.0, two
    minors and one widget line behind what this repo ships.
  - references/writing.md's canonical "minimum a standalone event needs"
    wrote specVersion 0.4.0, so an agent following it mints a document two
    minors behind on the day it is written.
  - evals/src/assertions.ts measured `fileIsValid` — what the README calls
    ground truth — by fetching the validate package on the 0.4 line, which
    judges a document against the newest release instead of the version it
    declares. That is the bug this kit fixed in 0.6, live in the instrument:
    a correct 0.5 or 0.6 document came out broken and a green run meant
    nothing.

So the rule is no longer discipline, it is a test. version-pins.test.ts
derives everything at run time — from the workspace's own package.json
versions and from packages/validate's support policy — and fails with the
file, the line and the value to write. It holds: npm pins name a package this
repo ships, on the line it ships; a pinned embed/v<x.y.z> URL matches
apps/embed AND has a committed snapshot behind it; a specVersion in anything
skills/ote-event/ tells an organizer to write is the latest published
version; and no .md names a bare spec minor in prose, because "what v0.4
documents" is read as current by whoever reads it.

The failure nobody would have seen without it: an eval fixture pinned to a
spec version changes meaning when the support window moves.
corunajug-feed.json was stamped 0.3.0 to be the "older but supported — a
notice, never a defect" case, and the day 0.5.0 and 0.6.0 shipped together
the window slid past it. The fixture still parsed and the case still ran,
having quietly become a migration case that asserted the opposite. Fixtures
now declare a role (latest / older-supported / none) resolved against the
live policy, so a window move names the file to re-stamp. Re-stamped to
0.4.0, and its watch_for rewritten: it described the pre-0.6 CLI behaviour as
"live today".

fileIsValid now imports validateDocument from the workspace instead of
pinning a published release. An instrument must be the code under review, not
a copy of it from npm — and an unpinned import cannot drift. The agent-facing
pins stay literal on purpose: they say which release the instructions were
tested with.

No per-line ignore mechanism, deliberately: a marker that silences this scan
would be reached for on the day the scan is right. A comment that needs to
discuss an old pin spells it in words ("the 0.4 line"), which reads better
anyway — the guard caught its own comments on first run and that is how it
was fixed.

Verified by simulation rather than by reading: bumping validate to 0.7.0 and
embed to 0.9.0 and re-stamping the fixture makes it report each stale pin with
its file and line, and inserting a bare `v0.5` into prose trips the prose rule.

What this does not fix: ote-validate still reports a healthy kit fork's
events/*.json as `0/3 valid` with exit 3 (#117), and the skill's Gate 2 still
sends the agent at them first (#119).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@hhkaos
hhkaos merged commit 368a9f9 into main Nov 6, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant