From 6dd97ff7543ec61ffb288e38cae9b94e53e2bf6d Mon Sep 17 00:00:00 2001 From: Brett Date: Fri, 1 May 2026 02:36:55 -0500 Subject: [PATCH] chore(skill): drop deprecated SHA-pin enforcement surface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The skill SHA pin was deprecated in agentnative-skill PR #11 (2026-04-29) — update detection moved to the skill bundle's bin/check-update (compares the local bundle's VERSION against main on GitHub). The site repo carried the full enforcement surface (manifest fields, build validation, schema docs, release runbook, tests, e2e probe, prose) as dead ceremony — every skill release had to think about a SHA bump that no longer carries the contract. Removed: - src/data/skill.json: source.commit, verify object - src/build/skill.mjs: COMMIT_RE regex, verify validation, REQUIRED_VERIFY list, "pinned at commit" prose, "## Verify" markdown section - docs/DESIGN.md §3.9: schema-table rows for source.commit and verify.*, build-validation prose, source-repo-coupling paragraph, release runbook bullet - RELEASES.md §"Skill releases": rewrote re-pin step to a manifest bump conditional on user-facing field changes - scripts/SYNCS.md: re-pin language in skill-release flow + reference list - tests/build.test.ts: non-hex / uppercase-hex commit rejection tests + source.commit / verify fixture fields - tests/regression.test.ts: source.commit / verify.expected invariants; required-keys list (verify dropped) - tests/e2e/skill.e2e.ts: pin-freshness checks (HEAD == source.commit and remote-HEAD == source.commit) Surviving SHA references are unrelated domains and stay: scorecard anc.commit rendering (src/build/scorecards-render.mjs, the scorecard schema), font supply chain (scripts/fonts/download.sh), and CLI registry version parsing (registry.yaml). Cross-repo: agentnative-cli's src/skill_install/skill.json fixture pulls src/data/skill.json from this repo. The shape change here will surface in its skill-fixture-drift workflow on next PR; coordinated CLI-side update should land in lockstep. Build, lint, and 203/203 unit + regression tests green. --- RELEASES.md | 28 +++++++++--------- docs/DESIGN.md | 61 +++++++++++++++++++--------------------- scripts/SYNCS.md | 23 ++++++++------- src/build/skill.mjs | 34 +++------------------- src/data/skill.json | 8 +----- tests/build.test.ts | 22 ++------------- tests/e2e/skill.e2e.ts | 27 ++++-------------- tests/regression.test.ts | 14 ++------- 8 files changed, 72 insertions(+), 145 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index bf93993..4b42b84 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -210,12 +210,13 @@ Committing the JSON alongside the code means ruleset changes land via the same r `/skill.json` and `/skill` advertise the `agent-native-cli` skill, hosted at [`brettdavies/agentnative-skill`](https://github.com/brettdavies/agentnative-skill). This site vendors the skill's -upstream commit SHA in `src/data/skill.json`; the skill repo holds the actual content. Surface contract in -`docs/DESIGN.md` §3.9. +manifest (per-host install commands, version, surface metadata) in `src/data/skill.json`; the skill repo holds the +actual content. Surface contract in `docs/DESIGN.md` §3.9. Update detection at install sites is delegated to the skill +bundle's `bin/check-update`, which compares the local bundle's `VERSION` against `main` on GitHub. The skill repo's branch model: `main` is the published-release pointer (default branch); `dev` is the integration -branch. The bare `git clone --depth 1` in each install command lands on `main` — so each release REQUIRES the skill -maintainer to fast-forward `main` to the new tag before the site re-pins. +branch. The bare `git clone --depth 1` in each install command lands on `main` — so each release requires the skill +maintainer to fast-forward `main` to the new tag. ### Skill-release procedure @@ -223,17 +224,18 @@ maintainer to fast-forward `main` to the new tag before the site re-pins. `git push origin dev --follow-tags`. Fast-forward `main` to the new tag and push: `git checkout main && git merge --ff-only v0.x.y && git push origin main`. The site's bare `git clone --depth 1` lands on `main`, so the fast-forward is what makes the new release reachable. -2. **Re-pin in this repo**: edit `src/data/skill.json` — bump `version`, `source.commit`, and `verify.expected` - (`source.commit` and `verify.expected` are the same SHA until v2 schema decouples them). `loadSkillData()` will - reject a non-hex / non-lowercase / non-40-char SHA at build time, so a typo fails fast. +2. **Bump the manifest in this repo (only when user-facing fields changed)**: edit `src/data/skill.json` to bump + `version` and update any per-host install commands, description, or other surface fields the release modified. If + nothing user-facing changed, skip the manifest bump entirely — the skill bundle's `bin/check-update` is what tells + installed users a new release exists. 3. **PR to `dev`**: CI runs the unit + worker tests on the bumped manifest. Squash-merge on green. 4. **Release `dev` → `main`** via the standard `release/*` flow above. Site deploys to `anc.dev`. -5. **Cache-purge** `/skill`, `/skill.json`, and `/skill.md` via the Cloudflare cache-purge API. Required for - security-relevant pin updates so users don't pick up the old SHA from the 24h `s-maxage` window. Use the API token - stored in 1Password (`secrets-dev` vault, `Cloudflare API Token - Wrangler (bigdaddy)`). First-deploy-after-rename - note (cutover from `/install*` → `/skill*`): also purge `/install`, `/install.json`, and `/install.md` once to evict - any cached skill content under the old paths. Skip this on subsequent deploys. -6. **Verify the deployed pin**: `curl -s https://anc.dev/skill.json | jq -r .source.commit` matches the new SHA. The +5. **Cache-purge** `/skill`, `/skill.json`, and `/skill.md` via the Cloudflare cache-purge API after a manifest bump, so + users don't pick up the old shape from the 24h `s-maxage` window. Use the API token stored in 1Password + (`secrets-dev` vault, `Cloudflare API Token - Wrangler (bigdaddy)`). First-deploy-after-rename note (cutover from + `/install*` → `/skill*`): also purge `/install`, `/install.json`, and `/install.md` once to evict any cached skill + content under the old paths. Skip this on subsequent deploys. +6. **Verify the deployed manifest**: `curl -s https://anc.dev/skill.json | jq -r .version` matches the new version. The Playwright `skill` project (`bun x playwright test --project=skill`) re-runs the live 4-host clone against the advertised hosts; run it locally before tagging if anything in the manifest's host commands changed. diff --git a/docs/DESIGN.md b/docs/DESIGN.md index 71b5d18..d0739d9 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -463,41 +463,37 @@ for the single advertised skill (`agent-native-cli`); per-skill `/skill/` second skill ships, `/skill` becomes an index and per-skill content moves under `/skill/` — the Worker's JSON-extension dispatch is already shape-agnostic, so no Worker code change is anticipated for that transition. -**Source repo coupling.** This site vendors a single string — the upstream commit SHA — committed at site build time -into `src/data/skill.json`. No fetch-on-build, no submodule, no `marketplace.json` machinery. Per +**Source repo coupling.** This site vendors the skill manifest's per-host install commands and metadata at site build +time into `src/data/skill.json`. No fetch-on-build, no submodule, no `marketplace.json` machinery. Per `docs/solutions/architecture-patterns/cross-repo-artifact-sync-commit-over-fetch-20260420.md`. The skill repo (`brettdavies/agentnative-skill`) holds `main` as the published-release pointer and `dev` as the integration branch; the install command's bare `git clone --depth 1` lands on the skill repo's default branch (`main`), which the skill -maintainer fast-forwards to each new release tag. +maintainer fast-forwards to each new release tag. Update detection is delegated to the skill bundle's +`bin/check-update`, which compares the local bundle's `VERSION` against `main` on GitHub. **`/skill.json` shape (v1):** -| Key | Type | Notes | -| ------------------ | -------------------------- | --------------------------------------------------------------------------------------------------- | -| `schema_version` | integer | `1`. Bump on incompatible structural change. | -| `type` | string | `"agent-skill"`. | -| `name` | string | `"agent-native-cli"`. Slug, kebab-case. | -| `version` | string | Semver. Bumped per skill release. | -| `description` | string | One sentence. | -| `principles_url` | string | `https://anc.dev/p1`. | -| `license` | string | `"MIT"`. | -| `source.type` | string | `"git"`. | -| `source.url` | string | `https://github.com/brettdavies/agentnative-skill.git`. | -| `source.commit` | string (40-char lower hex) | The pinned commit. Validated at build time. | -| `install` | object | Per-host map: `claude_code`, `codex`, `cursor`, `opencode` → `git clone --depth 1 …` command. | -| `verify.command` | string | `git -C rev-parse HEAD`. Agent substitutes ``. | -| `verify.expected` | string | Same SHA as `source.commit` until v2 schema decouples them. Mismatch = upstream moved past the pin. | -| `verify.semantics` | string | Free-form description of what mismatch means. | -| `update` | string | `cd && git pull --ff-only`. | -| `uninstall` | string | `rm -rf `. | -| `skill_page_html` | string | `https://anc.dev/skill`. | - -**Build-emitter validation (`src/build/skill.mjs`).** `loadSkillData()` is fail-fast: missing required keys, non-hex or -non-lowercase `source.commit`, non-semver `version`, empty `install` map, install commands not starting with `git clone ---depth 1`, and bare-clone commands (no explicit destination path) all reject the build at startup. The -explicit-destination invariant is non-optional defense for the repo-name asymmetry: the skill repo is named -`agentnative-skill` but the skill itself is named `agent-native-cli`; a bare `git clone` lands on the repo name and -breaks every host's skill-discovery convention. +| Key | Type | Notes | +| ----------------- | ------- | --------------------------------------------------------------------------------------------- | +| `schema_version` | integer | `1`. Bump on incompatible structural change. | +| `type` | string | `"agent-skill"`. | +| `name` | string | `"agent-native-cli"`. Slug, kebab-case. | +| `version` | string | Semver. Bumped per skill release. | +| `description` | string | One sentence. | +| `principles_url` | string | `https://anc.dev/p1`. | +| `license` | string | `"MIT"`. | +| `source.type` | string | `"git"`. | +| `source.url` | string | `https://github.com/brettdavies/agentnative-skill.git`. | +| `install` | object | Per-host map: `claude_code`, `codex`, `cursor`, `opencode` → `git clone --depth 1 …` command. | +| `update` | string | `cd && git pull --ff-only`. | +| `uninstall` | string | `rm -rf `. | +| `skill_page_html` | string | `https://anc.dev/skill`. | + +**Build-emitter validation (`src/build/skill.mjs`).** `loadSkillData()` is fail-fast: missing required keys, non-semver +`version`, empty `install` map, install commands not starting with `git clone --depth 1`, and bare-clone commands (no +explicit destination path) all reject the build at startup. The explicit-destination invariant is non-optional defense +for the repo-name asymmetry: the skill repo is named `agentnative-skill` but the skill itself is named +`agent-native-cli`; a bare `git clone` lands on the repo name and breaks every host's skill-discovery convention. **Header contract (`src/worker/headers.ts`).** The Worker's HTML/markdown branches are joined by a JSON-extension branch detected by URL ending in `.json` (extension, not prefix — any `/.json` endpoint reuses the branch, so the v2 @@ -524,9 +520,10 @@ twin. `/skill` enters `sitemap.xml` and `llms.txt` (under a `## Skill` section); `/skill.json` enters `llms.txt` but NOT the sitemap because `X-Robots-Tag: noindex` keeps it out of search engines. -**Release runbook entry.** The skill-release procedure lives in `RELEASES.md`. Each skill release bumps `version`, -`source.commit`, and `verify.expected` in `src/data/skill.json` (cache-purge `/skill`, `/skill.json`, and `/skill.md` -against the Cloudflare cache-purge API after deploy). +**Release runbook entry.** The skill-release procedure lives in `RELEASES.md`. Each skill release bumps `version` in +`src/data/skill.json` if the manifest's user-facing fields changed (cache-purge `/skill`, `/skill.json`, and `/skill.md` +against the Cloudflare cache-purge API after deploy). Update detection at install sites is handled by the skill bundle's +`bin/check-update`, not by a manifest field. ### 3.10 CLI install — `/install` diff --git a/scripts/SYNCS.md b/scripts/SYNCS.md index c244da0..28990ea 100644 --- a/scripts/SYNCS.md +++ b/scripts/SYNCS.md @@ -7,7 +7,7 @@ but not built*. Update this file whenever a sync script, workflow, endpoint, or Existing top-level docs cover adjacent concerns but none give a single map: -- `RELEASES.md` documents the skill-release procedure (the downstream-facing `/skill.json` re-pin) and the deploy +- `RELEASES.md` documents the skill-release procedure (the downstream-facing `/skill.json` manifest bump) and the deploy pipeline, but treats syncs as one step in a larger runbook. - `docs/DESIGN.md` §3.9 / §3.10 cover the `/skill` and `/install` build contracts, not the cross-repo data flow. - `AGENTS.md` describes endpoints and content authorship, not sync direction. @@ -95,9 +95,9 @@ site-own version (`package.json` is `"0.0.0"` deliberately — the spec version ### Build-time vendoring by other repos -| Consumer | Mechanism | What's exported | Trigger / cadence | Drift check | -| ------------------------------------------------------------ | ---------------------------------------------------------------------------------- | ----------------------------- | --------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `brettdavies/agentnative-cli` `src/skill_install/skill.json` | CLI's `scripts/sync-skill-fixture.sh` pulls from this repo's `src/data/skill.json` | Skill bundle metadata fixture | When this repo bumps `src/data/skill.json` (skill release re-pin per RELEASES.md §"Skill releases") | CLI's `skill-fixture-drift` GitHub Actions workflow runs the fixture's `--check` equivalent on every PR; CLI side fails if its vendored copy lags this repo. Effectively the inverse of the coverage-matrix arrangement: source-of-truth lives here, drift gate lives there. | +| Consumer | Mechanism | What's exported | Trigger / cadence | Drift check | +| ------------------------------------------------------------ | ---------------------------------------------------------------------------------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `brettdavies/agentnative-cli` `src/skill_install/skill.json` | CLI's `scripts/sync-skill-fixture.sh` pulls from this repo's `src/data/skill.json` | Skill bundle metadata fixture | When this repo bumps `src/data/skill.json` (skill release manifest bump per RELEASES.md §"Skill releases") | CLI's `skill-fixture-drift` GitHub Actions workflow runs the fixture's `--check` equivalent on every PR; CLI side fails if its vendored copy lags this repo. Effectively the inverse of the coverage-matrix arrangement: source-of-truth lives here, drift gate lives there. | ### Deploy-time emission to Cloudflare Workers @@ -128,10 +128,13 @@ The four flows interact, but each is independently triggered: for the workflow). Spec's `repository_dispatch:spec-release` event already fires here on tag publish; a consumer-side handler that auto-PRs the resync is tracked as follow-up work. -4. **Skill repo cuts a release** → maintainer fast-forwards `agentnative-skill:main` to the new tag → edits this repo's - `src/data/skill.json` (`version`, `source.commit`, `verify.expected`) → PR to `dev` → release flow to `main` → - `wrangler deploy` updates `/skill.json` on `anc.dev` → Cloudflare cache purge → CLI's next PR exercises - `skill-fixture-drift` against the new fixture. Full runbook in `RELEASES.md` §"Skill-release procedure". +4. **Skill repo cuts a release** → maintainer fast-forwards `agentnative-skill:main` to the new tag → if any user-facing + manifest fields changed (per-host install commands, version, description), edits this repo's `src/data/skill.json` to + bump `version` plus the changed fields → PR to `dev` → release flow to `main` → `wrangler deploy` updates + `/skill.json` on `anc.dev` → Cloudflare cache purge → CLI's next PR exercises `skill-fixture-drift` against the new + fixture. If the release didn't change any manifest fields, skip the manifest bump entirely — installed users learn + about the new release via the skill bundle's `bin/check-update`, not via a manifest change here. Full runbook in + `RELEASES.md` §"Skill-release procedure". 5. **Site code/content change** → push to `dev` (auto-deploys to staging Worker) → PR `dev` → `main` → push to `main` (auto-deploys to `anc.dev`). @@ -145,8 +148,8 @@ The four flows interact, but each is independently triggered: and runs `score-anc100.sh` inside the container, writing scorecards back to the host via bind mount. The container is the single source of truth for scoring; host-side `regen-scorecards.sh` is deprecated. - `src/data/spec/README.md` — what's vendored, why, and the manual reconciliation workflow when spec prose drifts. -- `RELEASES.md` §"Skill releases" — the downstream re-pin procedure for `src/data/skill.json` end-to-end (commit → - cache-purge → live verify). +- `RELEASES.md` §"Skill releases" — the downstream manifest-bump procedure for `src/data/skill.json` end-to-end + (manifest edit → cache-purge → live verify). - `docs/DESIGN.md` §3.9 (`/skill` + `/skill.json` build contract) and §3.10 (`/install` HTML-only contract). - `AGENTS.md` — repo conventions and the `content/principles/` vs `src/data/spec/principles/` separation rule. - `docs/plans/2026-04-23-001-feat-sync-spec-plan.md` (dev branch only, gated off main) — the plan that introduced diff --git a/src/build/skill.mjs b/src/build/skill.mjs index 5f021c0..d2bb03d 100644 --- a/src/build/skill.mjs +++ b/src/build/skill.mjs @@ -6,8 +6,7 @@ // §"/install.json shape", relocated to /skill.json by 2026-04-28-003): // // - skill.json IS the source of truth. The emitter validates and copies; -// it does not synthesize fields. verify.expected and source.commit are -// hand-co-edited at release time. +// it does not synthesize fields. // - dist/skill.json is byte-stable across runs: keys sorted, two-space // indent, trailing newline. // - Per-host commands MUST start with `git clone --depth 1` and terminate @@ -19,7 +18,6 @@ import { readFile, writeFile } from 'node:fs/promises'; import { join } from 'node:path'; import { renderMarkdown } from './render.mjs'; -const COMMIT_RE = /^[0-9a-f]{40}$/; const SEMVER_RE = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/; const REQUIRED_TOP_LEVEL = [ 'schema_version', @@ -31,13 +29,11 @@ const REQUIRED_TOP_LEVEL = [ 'license', 'source', 'install', - 'verify', 'update', 'uninstall', 'skill_page_html', ]; -const REQUIRED_SOURCE = ['type', 'url', 'commit']; -const REQUIRED_VERIFY = ['command', 'expected', 'semantics']; +const REQUIRED_SOURCE = ['type', 'url']; /** * Read + validate src/data/skill.json. Fail-fast on missing/malformed @@ -71,15 +67,6 @@ export async function loadSkillData(dataPath) { throw new Error(`${dataPath}: missing required key "source.${key}"`); } } - if (!COMMIT_RE.test(data.source.commit)) { - throw new Error(`${dataPath}: "source.commit" must be a 40-char lowercase hex SHA, got "${data.source.commit}"`); - } - - for (const key of REQUIRED_VERIFY) { - if (!data.verify[key]) { - throw new Error(`${dataPath}: missing required key "verify.${key}"`); - } - } if (!data.install || typeof data.install !== 'object' || Array.isArray(data.install)) { throw new Error(`${dataPath}: "install" must be a non-empty object mapping host → command`); @@ -195,7 +182,7 @@ export function buildSkillMarkdown(data) { lines.push('## What this does'); lines.push(''); lines.push( - `Clones \`${data.source.url}\` (pinned at commit \`${data.source.commit}\`) into your host's skills directory. \`.git/\` is preserved so future updates are a \`git pull\`.`, + `Clones \`${data.source.url}\` into your host's skills directory. \`.git/\` is preserved so future updates are a \`git pull\`.`, ); lines.push(''); @@ -233,20 +220,7 @@ export function buildSkillMarkdown(data) { lines.push('## Trust model'); lines.push(''); lines.push( - "Piping a remote shell script into the local shell is the failure mode this install path rejects. Installation runs `git clone` against a content-addressed commit on a specific repository — the scripts are open-source and visible at the producer repo before they execute on the user's machine. The site advertises a single upstream commit SHA in `/skill.json`; agents that care about provenance can verify it.", - ); - lines.push(''); - - lines.push('## Verify'); - lines.push(''); - lines.push("After install, confirm the local checkout matches the site's advertised pin:"); - lines.push(''); - lines.push('```bash'); - lines.push(data.verify.command); - lines.push('```'); - lines.push(''); - lines.push( - `Expected: \`${data.verify.expected}\`. ${data.verify.semantics.charAt(0).toUpperCase()}${data.verify.semantics.slice(1)}.`, + "Piping a remote shell script into the local shell is the failure mode this install path rejects. Installation runs `git clone` against a specific repository on a specific host — the scripts are open-source and visible at the producer repo before they execute on the user's machine.", ); lines.push(''); diff --git a/src/data/skill.json b/src/data/skill.json index d0b2033..1db85ea 100644 --- a/src/data/skill.json +++ b/src/data/skill.json @@ -8,8 +8,7 @@ "license": "MIT", "source": { "type": "git", - "url": "https://github.com/brettdavies/agentnative-skill.git", - "commit": "47a76cceb8b7b1bc013c19ee18a5e38179b1dd0e" + "url": "https://github.com/brettdavies/agentnative-skill.git" }, "install": { "claude_code": "git clone --depth 1 https://github.com/brettdavies/agentnative-skill.git ~/.claude/skills/agent-native-cli", @@ -19,11 +18,6 @@ "kiro": "git clone --depth 1 https://github.com/brettdavies/agentnative-skill.git ~/.kiro/skills/agent-native-cli", "opencode": "git clone --depth 1 https://github.com/brettdavies/agentnative-skill.git ~/.config/opencode/skills/agent-native-cli" }, - "verify": { - "command": "git -C rev-parse HEAD", - "expected": "47a76cceb8b7b1bc013c19ee18a5e38179b1dd0e", - "semantics": "advisory freshness probe; mismatch means upstream has moved past the site's pin" - }, "update": "cd && git pull --ff-only", "uninstall": "rm -rf ", "skill_page_html": "https://anc.dev/skill" diff --git a/tests/build.test.ts b/tests/build.test.ts index b719779..1f0cbf5 100644 --- a/tests/build.test.ts +++ b/tests/build.test.ts @@ -1550,17 +1550,11 @@ describe('loadSkillData — fail-fast validation', () => { source: { type: 'git', url: 'https://github.com/brettdavies/agentnative-skill.git', - commit: '47a76cceb8b7b1bc013c19ee18a5e38179b1dd0e', }, install: { claude_code: 'git clone --depth 1 https://github.com/brettdavies/agentnative-skill.git ~/.claude/skills/agent-native-cli', }, - verify: { - command: 'git -C rev-parse HEAD', - expected: '47a76cceb8b7b1bc013c19ee18a5e38179b1dd0e', - semantics: 'advisory', - }, update: 'cd && git pull --ff-only', uninstall: 'rm -rf ', skill_page_html: 'https://anc.dev/skill', @@ -1580,8 +1574,8 @@ describe('loadSkillData — fail-fast validation', () => { } test('happy path: valid manifest loads', async () => { - const data = (await writeAndLoad(validManifest())) as { source: { commit: string } }; - expect(data.source.commit).toBe('47a76cceb8b7b1bc013c19ee18a5e38179b1dd0e'); + const data = (await writeAndLoad(validManifest())) as { source: { url: string } }; + expect(data.source.url).toBe('https://github.com/brettdavies/agentnative-skill.git'); }); test('missing top-level key fails with the key name', async () => { @@ -1590,18 +1584,6 @@ describe('loadSkillData — fail-fast validation', () => { await expect(writeAndLoad(m)).rejects.toThrow(/missing required key "license"/); }); - test('non-hex commit rejected', async () => { - const m = validManifest(); - m.source.commit = 'NOT-A-SHA'; - await expect(writeAndLoad(m)).rejects.toThrow(/40-char lowercase hex SHA/); - }); - - test('uppercase-hex commit rejected (must be lowercase)', async () => { - const m = validManifest(); - m.source.commit = '47A76CCEB8B7B1BC013C19EE18A5E38179B1DD0E'; - await expect(writeAndLoad(m)).rejects.toThrow(/40-char lowercase hex SHA/); - }); - test('non-semver version rejected', async () => { const m = validManifest(); m.version = '0.1'; diff --git a/tests/e2e/skill.e2e.ts b/tests/e2e/skill.e2e.ts index 3e457da..2bfd493 100644 --- a/tests/e2e/skill.e2e.ts +++ b/tests/e2e/skill.e2e.ts @@ -1,8 +1,8 @@ // 4-host skill-distribution install verification + bare-clone footgun // check. This is the live-network e2e: it fetches /skill.json from the // local Worker, runs every advertised host's clone command into a sandbox -// HOME, and asserts SKILL.md lands at the expected path with the pinned -// commit checked out. +// HOME, and asserts SKILL.md lands at the expected path on the skill +// repo's default branch. // // Isolation: this spec runs only under the `skill` Playwright project // (see playwright.config.ts). It is excluded from the default `bun run @@ -16,8 +16,8 @@ // Post-cutover usage (producer is public — HTTPS works as advertised): // bun x playwright test --project=skill // -// The URL override only swaps the clone source; the destination paths, -// commit pin, and host names are still drawn from /skill.json. +// The URL override only swaps the clone source; the destination paths and +// host names are still drawn from /skill.json. import { execFileSync } from 'node:child_process'; import { mkdtempSync, rmSync, statSync } from 'node:fs'; @@ -29,7 +29,7 @@ const BASE = 'http://localhost:8787'; const URL_OVERRIDE = process.env.ANC_SKILL_URL; interface Manifest { - source: { url: string; commit: string }; + source: { url: string }; install: Record; } @@ -73,7 +73,7 @@ function runInSandbox(command: string, sandboxHome: string): void { } test.describe('skill-distribution install — 4-host live clone', () => { - test('every advertised host clones, lands SKILL.md, and pins commit', async ({ request }) => { + test('every advertised host clones and lands SKILL.md', async ({ request }) => { const manifest = await fetchManifest(request); expect(Object.keys(manifest.install).length).toBeGreaterThanOrEqual(1); @@ -88,21 +88,6 @@ test.describe('skill-distribution install — 4-host live clone', () => { // canonical entry point. const skillPath = join(dest, 'SKILL.md'); expect({ host, exists: statSync(skillPath).isFile() }).toEqual({ host, exists: true }); - - // Pin freshness: the local checkout's HEAD must match source.commit. - const head = execFileSync('git', ['-C', dest, 'rev-parse', 'HEAD']).toString().trim(); - expect({ host, head }).toEqual({ host, head: manifest.source.commit }); - - // Pin freshness: the skill repo's default-branch HEAD must equal the - // pin. This is the actual invariant — if upstream has moved past the - // pin, the install is stale and the next site release is overdue. - // (`git ls-remote --exit-code ` would NOT work — ls-remote - // resolves ref names, not SHAs.) - const remoteHead = execFileSync('git', ['-C', dest, 'ls-remote', 'origin', 'HEAD']) - .toString() - .trim() - .split(/\s+/)[0]; - expect({ host, remoteHead }).toEqual({ host, remoteHead: manifest.source.commit }); } finally { rmSync(sandboxHome, { recursive: true, force: true }); } diff --git a/tests/regression.test.ts b/tests/regression.test.ts index bd2c8fb..7e8d74a 100644 --- a/tests/regression.test.ts +++ b/tests/regression.test.ts @@ -143,21 +143,12 @@ describe('regression #5 — /skill.json (skill-distribution canonical surface)', expect(parsed).toBeDefined(); }); - test('dist/skill.json source.commit matches src/data/skill.json', async () => { + test('dist/skill.json mirrors src/data/skill.json byte-for-byte', async () => { const distRaw = await readFile(join(DIST, 'skill.json'), 'utf8'); const sourceRaw = await readFile(join(REPO_ROOT, 'src', 'data', 'skill.json'), 'utf8'); const dist = JSON.parse(distRaw); const source = JSON.parse(sourceRaw); - expect(dist.source.commit).toBe(source.source.commit); - // Pin-freshness invariant: verify.expected mirrors source.commit until - // a v2 schema decouples them. - expect(dist.verify.expected).toBe(dist.source.commit); - }); - - test('dist/skill.json source.commit is 40-char lowercase hex', async () => { - const raw = await readFile(join(DIST, 'skill.json'), 'utf8'); - const parsed = JSON.parse(raw); - expect(parsed.source.commit).toMatch(/^[0-9a-f]{40}$/); + expect(dist).toEqual(source); }); test('dist/skill.json has every required key', async () => { @@ -173,7 +164,6 @@ describe('regression #5 — /skill.json (skill-distribution canonical surface)', 'license', 'source', 'install', - 'verify', 'update', 'uninstall', 'skill_page_html',