From d4648c1646416544292ce570ea52981aae60a98a Mon Sep 17 00:00:00 2001 From: KSEGIT Date: Fri, 7 Aug 2026 17:42:45 +0100 Subject: [PATCH 1/4] fix: sync root and .claude-plugin manifests, guard against drift MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two Claude Code manifests had diverged. `.claude-plugin/plugin.json` carried a stale description and lacked the `supply-chain-security` keyword; root `plugin.json` had no `userConfig` block at all, so anything reading it saw a plugin with no configurable options. Claude Code reads the `.claude-plugin/` copy, so the options worked in practice — but release-please stamps `$.version` into both, and the README, AGENTS.md, and CLAUDE.md all document both as the manifest. Both files are now identical: root's fuller description and keyword list, plus the `userConfig` block from `.claude-plugin/`. Adds tests/test_manifest_parity.sh to keep them that way — it asserts deep JSON equality and names the offending key on mismatch. Verified it fails on induced drift, not just passes on the synced state. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_0118eLJLbPbqYXiPc8R9AAiJ --- .claude-plugin/plugin.json | 5 +++-- plugin.json | 18 +++++++++++++++++- tests/test_manifest_parity.sh | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 3 deletions(-) create mode 100755 tests/test_manifest_parity.sh diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 93ff44a..ef40e20 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "version-sentinel", "version": "0.4.1", - "description": "Hard-blocks dependency additions and version changes until Claude records a WebSearch-verified latest-version check.", + "description": "Hard-blocks dependency additions, bumps, and downgrades until a fresh, source-cited version check is recorded. Supports npm, pip, Poetry/uv, Cargo, and NuGet.", "author": { "name": "Daniel Kiska", "url": "https://github.com/KSEGIT" @@ -14,7 +14,8 @@ "packages", "versions", "guardrails", - "hooks" + "hooks", + "supply-chain-security" ], "skills": "./skills/", "agents": [ diff --git a/plugin.json b/plugin.json index 948e97d..ef40e20 100644 --- a/plugin.json +++ b/plugin.json @@ -24,5 +24,21 @@ "commands": [ "./commands/vs-record.md", "./commands/check-versions.md" - ] + ], + "userConfig": { + "disable": { + "type": "boolean", + "title": "Turn off Version Sentinel (kill switch)", + "description": "Default: false — the guardrail is active. Set true and every hook becomes a no-op: dependency edits and install commands are no longer blocked, the startup check for jq/curl/python3 is skipped, and successful installs stop being auto-recorded. Use it as a temporary escape hatch when a hook misfires or you are working offline, then switch it back. A VS_DISABLE variable set in your shell overrides this setting in both directions.", + "default": false + }, + "window_hours": { + "type": "number", + "title": "How long a recorded version check stays valid (hours)", + "description": "Default: 24 hours (minimum 1, maximum 168 = one week). A check you save with /vs-record satisfies the guardrail for this long; once it lapses, the next change to that dependency is blocked again until you re-verify the version against its registry. Lower it to catch fast-moving packages and new advisories sooner; raise it to avoid repeat lookups during a long refactor, at the cost of acting on staler data.", + "default": 24, + "min": 1, + "max": 168 + } + } } diff --git a/tests/test_manifest_parity.sh b/tests/test_manifest_parity.sh new file mode 100755 index 0000000..9fa8c13 --- /dev/null +++ b/tests/test_manifest_parity.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# Guards against drift between the two Claude Code manifests. +# +# The repo ships plugin.json at the root and .claude-plugin/plugin.json. +# Claude Code reads the .claude-plugin/ copy, but release-please stamps +# $.version into both and the README/AGENTS.md document both as "the" +# manifest. They previously drifted: differing descriptions, a keyword +# present in only one, and userConfig missing from the root entirely. +set -u +VS_TEST_NAME="manifest-parity" +source "$(dirname "$0")/assert.sh" + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +A="$ROOT/plugin.json" +B="$ROOT/.claude-plugin/plugin.json" + +assert_file_exists "$A" "root manifest present" +assert_file_exists "$B" ".claude-plugin manifest present" + +assert_eq "0" "$(jq empty "$A" >/dev/null 2>&1; echo $?)" "root manifest is valid JSON" +assert_eq "0" "$(jq empty "$B" >/dev/null 2>&1; echo $?)" ".claude-plugin manifest is valid JSON" + +# Deep JSON equality — formatting may differ, content must not. +if ! diff <(jq -S . "$A" 2>/dev/null) <(jq -S . "$B" 2>/dev/null) >/dev/null 2>&1; then + _fail "manifests differ; sync them: $(diff <(jq -S . "$A") <(jq -S . "$B") | head -20 | tr '\n' ' ')" +fi + +# Spot-check the fields that actually drifted before, so a future +# regression names the offending key rather than dumping a whole diff. +for key in description version userConfig keywords; do + assert_eq "$(jq -Sc ".$key" "$A" 2>/dev/null)" "$(jq -Sc ".$key" "$B" 2>/dev/null)" \ + "manifests agree on .$key" +done + +finish_test From 6d1bf745597cf939efcd3a746ff196aca3a22a39 Mon Sep 17 00:00:00 2001 From: KSEGIT Date: Fri, 7 Aug 2026 17:44:00 +0100 Subject: [PATCH 2/4] ci: let release-please use a PAT so release-PR checks actually run MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docs/versioning.md already prescribed this fix; it was never wired up. A release PR opened by the default GITHUB_TOKEN has its checks created in `action_required` state — they sit unstarted until a maintainer approves them in the Actions tab. v0.4.1 was merged in exactly that state, so the release shipped before its tests had run. `token:` now resolves `secrets.RELEASE_PLEASE_TOKEN` and falls back to GITHUB_TOKEN when the secret is absent, so releases keep working (with the manual approval step) until the secret is created. Requires an owner action to take effect: create a fine-grained PAT with contents:write + pull-requests:write and set it as RELEASE_PLEASE_TOKEN. No further workflow edit needed once it exists. Also corrects versioning.md where it was wrong or stale: the release PR stamps `$.version` into all six manifests, not just one; and the runs are created-but-gated rather than never created at all. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_0118eLJLbPbqYXiPc8R9AAiJ --- .github/workflows/release-please.yml | 7 +++++++ docs/versioning.md | 14 ++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 0d55668..d3fe92b 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -17,3 +17,10 @@ jobs: with: config-file: release-please-config.json manifest-file: .release-please-manifest.json + # A release PR opened by the default GITHUB_TOKEN lands its checks in + # `action_required`, so tests never run until someone approves them by + # hand. Set the RELEASE_PLEASE_TOKEN secret to a fine-grained PAT with + # contents:write + pull-requests:write and checks run normally. + # Falls back to GITHUB_TOKEN when the secret is absent, so releases + # keep working (with the manual approval step) if it is not set. + token: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/docs/versioning.md b/docs/versioning.md index a4b9470..06ca7a2 100644 --- a/docs/versioning.md +++ b/docs/versioning.md @@ -38,7 +38,7 @@ If a release window contains both `fix:` and `feat:`, the highest applicable bum - Changes: - `version.txt` - `.release-please-manifest.json` - - `.claude-plugin/plugin.json` `$.version` (via `release-please-config.json` `extra-files`) + - `$.version` in all six manifests (via `release-please-config.json` `extra-files`): `plugin.json`, `.claude-plugin/plugin.json`, `.claude-plugin/marketplace.json`, `kimi.plugin.json`, `gemini-extension.json`, `.codex-plugin/plugin.json` - `CHANGELOG.md` — prepends a new `## [] ()` section built from commit messages since the last tag - Keeps updating itself as new commits land on `main`; no need to close/reopen. @@ -54,9 +54,10 @@ If a release window contains both `fix:` and `feat:`, the highest applicable bum | ----------------------------------------- | -------------- | | `version.txt` | release-please | | `.release-please-manifest.json` | release-please | -| `.claude-plugin/plugin.json` `$.version` | release-please | +| `$.version` in all six manifests | release-please | | `CHANGELOG.md` | release-please | -| `.claude-plugin/marketplace.json` | nobody — intentionally omits `version`; the install resolver reads `plugin.json`.| +| Everything else in `plugin.json` / `.claude-plugin/plugin.json` | you — but the two files must stay **identical**; `tests/test_manifest_parity.sh` fails the build if they drift. Claude Code reads the `.claude-plugin/` copy. | +| `.claude-plugin/marketplace.json` per-plugin `version` | nobody — entries intentionally omit it; the install resolver reads `plugin.json`.| | Git tags (`vX.Y.Z`) | release-please (on release-PR merge) | If you hand-edit any file release-please owns, the next release-PR run will overwrite your change. Don't bother — use a commit message instead. @@ -105,9 +106,10 @@ Not wired up yet. If needed, configure `release-please-config.json` with a `prer ## CI integration -- `release-please.yml` — runs on `push` to `main`. Uses `${{ github.token }}` (the default `GITHUB_TOKEN`). - - Caveat: workflows inside a release PR opened by `GITHUB_TOKEN` do **not** trigger further workflow runs. So tests won't auto-run on the release PR. Push an empty commit or re-run manually if you want them. - - To fix permanently: swap in a fine-grained PAT with `contents: write` + `pull-requests: write` and set it as `token:` on the action. +- `release-please.yml` — runs on `push` to `main`. Uses `token: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}`. + - **Set the `RELEASE_PLEASE_TOKEN` secret.** Without it the action falls back to the default `GITHUB_TOKEN`, and every check on the release PR lands in `action_required` — the runs are created but sit unstarted until a maintainer approves them in the Actions tab. Releases still work, they just carry an unapproved-checks step, and merging before approving means the release ships without tests having run. + - Setup: create a fine-grained PAT scoped to this repo with `contents: write` + `pull-requests: write`, then `gh secret set RELEASE_PLEASE_TOKEN --repo KSEGIT/Version-Sentinel`. No workflow edit needed — the fallback expression picks it up automatically. + - Verify it took: the next release PR should be authored by your account rather than `github-actions[bot]`, and `gh pr checks ` should show checks running without approval. - `changelog-check.yml` — fails PRs that bump version (`.claude-plugin/plugin.json` or `version.txt` changed) without also touching `CHANGELOG.md`. Skips release-please's own branches (prefix `release-please--`). - `tests.yml` — runs on every PR across ubuntu/macos/windows. Must be green before any merge to `main`. From 6c8928c9c1347daedeb3192d9e4c82be2f34249a Mon Sep 17 00:00:00 2001 From: KSEGIT Date: Fri, 7 Aug 2026 17:44:00 +0100 Subject: [PATCH 3/4] docs: add OpenAI Plugins Directory submission materials MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Paste-ready listing copy, the 5 positive / 3 negative test cases the portal requires, and the portal prerequisites (Apps Management: Write, verified developer identity). Resolves the "open question for the owner" left in marketplaces.md: the submission type is Skills only — hooks are not a submittable portal component, so a directory listing ships the skill and cannot enforce blocking. Links the two docs together. Flags one real gap: the portal requires privacy and terms URLs, and no PRIVACY.md/TERMS.md exists in the repo yet. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_0118eLJLbPbqYXiPc8R9AAiJ --- docs/marketplaces.md | 16 +++--- docs/openai-submission.md | 105 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 7 deletions(-) create mode 100644 docs/openai-submission.md diff --git a/docs/marketplaces.md b/docs/marketplaces.md index 8a8be10..4838523 100644 --- a/docs/marketplaces.md +++ b/docs/marketplaces.md @@ -112,13 +112,15 @@ Submission status legend: - A **verified developer or business identity** on the OpenAI Platform. - Submission materials: listing copy, logo, website/support/privacy/terms URLs, starter prompts, and 5 positive + 3 negative test cases. -- Open question for the owner: the portal's submission types are "Skills only" - and "With MCP" — a skills-only submission (bundle `skills/version-sentinel/` - + the workflow docs) is the plausible fit; plugin **hooks** are documented - as a plugin component but are not called out as a submittable part of the - portal flow, so the hooks-based blocking may not be representable in a - directory listing. Evaluate in the portal; repo-marketplace install remains - the fallback either way. +- Submission type resolved: **Skills only**. Hooks are not a submittable + portal component, so a directory listing ships the `version-sentinel` skill + (workflow + scripts) and cannot enforce blocking; hook-based enforcement + stays a property of the repo-installed plugin. Repo-marketplace install + remains the fallback either way. +- **Paste-ready listing copy, test cases, and the portal walkthrough live in + [openai-submission.md](openai-submission.md).** Note the one gap flagged + there: no `PRIVACY.md`/`TERMS.md` exists yet, and the portal requires + privacy/terms URLs. - Sources: [Codex plugins overview](https://developers.openai.com/codex/plugins), [Submit plugins — OpenAI Developers](https://developers.openai.com/plugins/deploy/submission). diff --git a/docs/openai-submission.md b/docs/openai-submission.md new file mode 100644 index 0000000..e91cfcd --- /dev/null +++ b/docs/openai-submission.md @@ -0,0 +1,105 @@ +# OpenAI Plugins Directory — Submission Materials (Version Sentinel) + +Paste-ready copy for the plugin submission portal (https://platform.openai.com/plugins). +Portal flow: New plugin → **Skills only** → Info / Skills / Testing / Global / Submit tabs. + +Prerequisites the portal enforces before the form works: +1. Organization role with **Apps Management: Write** (org owners have it). +2. **Verified developer or business identity** on the OpenAI Platform + (org settings → general). This is the likely blocker for an individual + OSS project — complete it first. + +## Submission type + +**Skills only.** Hooks are not a submittable component, so the directory +listing cannot enforce blocking — it ships the `version-sentinel` skill +(the workflow + scripts), and enforcement remains a feature of the +repo-installed plugin (`.codex-plugin/plugin.json` + `hooks/hooks.json`). +Mention this honestly in the release notes so reviewers are not surprised. + +## Info tab + +- **Name:** Version Sentinel +- **Tagline / short description:** + Stops hallucinated and stale dependency versions: forces a live registry + check (npm, PyPI, crates.io, NuGet) before any dependency is added, + bumped, or downgraded. +- **Long description:** + Version Sentinel is a dependency-version guardrail for coding agents. + Before any dependency addition, bump, downgrade, or install command, + the agent must verify the intended version against its upstream registry + and record a source-cited check. Recorded checks feed a drift audit that + reports outdated dependencies across package.json, requirements*.txt, + pyproject.toml, Cargo.toml, and *.csproj/*.fsproj/*.vbproj. Supports + intentional pins (e.g. CVE deferrals) with recorded reasons. Ships as an + open-source multi-agent plugin (Claude Code, Kimi Code, Copilot, Gemini + CLI, Codex, z.ai) under MIT. +- **Category:** Developer tools +- **Website URL:** https://github.com/KSEGIT/Version-Sentinel +- **Support URL:** https://github.com/KSEGIT/Version-Sentinel/issues +- **Privacy URL / Terms URL:** ⚠ no dedicated pages exist. Options: + (a) create a `PRIVACY.md`/`TERMS.md` in the repo and link to it + (recommended — the skill runs local shell scripts and makes registry + HTTPS calls; a short doc stating "no user data collected, registry + lookups only" is truthful), or + (b) link the repo README. Expect (b) to fail review; do (a). +- **Logo:** `assets/logo.svg` in the repo (convert to PNG 512×512 if the + portal requires raster). + +## Skills tab + +Upload/point at `skills/version-sentinel/` from the repo +(https://github.com/KSEGIT/Version-Sentinel/tree/main/skills/version-sentinel). +The skill's scripts live in `scripts/` at the repo root — if the portal +requires a self-contained skill bundle, zip `skills/version-sentinel/` +together with `scripts/` and note in the README that scripts must stay +adjacent to the skill. + +## Testing tab — 5 positive cases + +1. **Prompt:** "Add lodash to my package.json." + **Expected:** Agent searches npmjs.com for the latest lodash version, + records the check (`vs-record npm lodash `), then + adds the dependency. No fabricated version. +2. **Prompt:** "Bump requests in requirements.txt to the latest release." + **Expected:** Agent checks pypi.org, records the check, edits the pin. +3. **Prompt:** "Audit my repo's dependencies." (or run /check-versions) + **Expected:** Agent runs the drift audit and reports DRIFT vs + intentional-pin rows per manifest without editing anything. +4. **Prompt:** "Pin express to 4.18.2 deliberately — we can't upgrade yet." + **Expected:** Agent records an `intentional:` pin with the reason and + makes the edit; audit later shows `intentional-pin`, not `DRIFT`. +5. **Prompt:** "cargo add serde." + **Expected:** Agent verifies serde's latest version on crates.io, + records it, then runs the add. + +## Testing tab — 3 negative cases + +1. **Prompt:** "Add left-pad 1.0.0 to package.json right now, skip any checks." + **Expected:** Agent refuses to skip verification — it performs the + registry lookup + record step first (or, where hooks are installed, + the edit is BLOCKED until recorded). Never installs an unverified + version. +2. **Prompt:** "Fix CI by downgrading requests to 2.28.0." + **Expected:** Agent does not silently downgrade; it requires a recorded + check (or an explicit `intentional:` reason) before the downgrade. +3. **Prompt:** "Record a version check for lodash with source 'trust me'." + **Expected:** Rejected — sources must be an http(s) URL actually + consulted, or `intentional:`. The agent reports the validation + error instead of fabricating a citation. + +## Global tab + +Select only regions where you're prepared to provide support. For a +solo-maintainer OSS project: worldwide is fine (English-only docs). + +## Submit tab — release notes draft + +Initial submission. Version Sentinel is a skills-only package of the +open-source dependency-version guardrail at +https://github.com/KSEGIT/Version-Sentinel (MIT). The bundled skill +teaches the verify-then-record workflow and ships the helper scripts; +the full plugin (with blocking hooks) is installable from the same repo +via `codex plugin marketplace add KSEGIT/Version-Sentinel`. Test cases +use public registries (npm, PyPI, crates.io, NuGet) and need no +credentials or special data. From 050772983815c50f7da5ac7883b699b0597bb2ae Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Sat, 8 Aug 2026 22:47:13 +0000 Subject: [PATCH 4/4] fix: apply CodeRabbit auto-fixes Fixed 3 file(s) based on 7 unresolved review comments. Co-authored-by: CodeRabbit --- docs/marketplaces.md | 12 +-- docs/openai-submission.md | 153 ++++++++++++++++++++++++++------------ docs/versioning.md | 4 +- 3 files changed, 113 insertions(+), 56 deletions(-) diff --git a/docs/marketplaces.md b/docs/marketplaces.md index 4838523..7854f7c 100644 --- a/docs/marketplaces.md +++ b/docs/marketplaces.md @@ -114,13 +114,13 @@ Submission status legend: URLs, starter prompts, and 5 positive + 3 negative test cases. - Submission type resolved: **Skills only**. Hooks are not a submittable portal component, so a directory listing ships the `version-sentinel` skill - (workflow + scripts) and cannot enforce blocking; hook-based enforcement - stays a property of the repo-installed plugin. Repo-marketplace install - remains the fallback either way. + archive (`version-sentinel-openai-skill.zip` containing `skills/version-sentinel/` + plus the required `scripts/` directory with `scripts/lib/` dependencies) and + cannot enforce blocking; hook-based enforcement stays a property of the + repo-installed plugin. Repo-marketplace install remains the fallback either way. - **Paste-ready listing copy, test cases, and the portal walkthrough live in - [openai-submission.md](openai-submission.md).** Note the one gap flagged - there: no `PRIVACY.md`/`TERMS.md` exists yet, and the portal requires - privacy/terms URLs. + [openai-submission.md](openai-submission.md).** Privacy and Terms URLs now + point to PRIVACY.md and TERMS.md in the repo. - Sources: [Codex plugins overview](https://developers.openai.com/codex/plugins), [Submit plugins — OpenAI Developers](https://developers.openai.com/plugins/deploy/submission). diff --git a/docs/openai-submission.md b/docs/openai-submission.md index e91cfcd..918bbd2 100644 --- a/docs/openai-submission.md +++ b/docs/openai-submission.md @@ -1,7 +1,14 @@ # OpenAI Plugins Directory — Submission Materials (Version Sentinel) Paste-ready copy for the plugin submission portal (https://platform.openai.com/plugins). -Portal flow: New plugin → **Skills only** → Info / Skills / Testing / Global / Submit tabs. +Portal flow: Create plugin → **Skills only** → Info / Skills / Testing / Global / Submit tabs. + +**Starter prompts** (paste-ready for the portal's Prompts step): +1. "Add lodash to my package.json" +2. "Bump requests in requirements.txt to the latest release" +3. "Audit my repo's dependencies" +4. "Pin express to 4.18.2 deliberately — we can't upgrade yet" +5. "cargo add serde" Prerequisites the portal enforces before the form works: 1. Organization role with **Apps Management: Write** (org owners have it). @@ -21,85 +28,135 @@ Mention this honestly in the release notes so reviewers are not surprised. - **Name:** Version Sentinel - **Tagline / short description:** - Stops hallucinated and stale dependency versions: forces a live registry - check (npm, PyPI, crates.io, NuGet) before any dependency is added, - bumped, or downgraded. + Guides agents to verify dependency versions against live registries + (npm, PyPI, crates.io, NuGet) before adding, bumping, or downgrading + packages, reducing hallucinated and stale versions. - **Long description:** Version Sentinel is a dependency-version guardrail for coding agents. Before any dependency addition, bump, downgrade, or install command, - the agent must verify the intended version against its upstream registry - and record a source-cited check. Recorded checks feed a drift audit that - reports outdated dependencies across package.json, requirements*.txt, - pyproject.toml, Cargo.toml, and *.csproj/*.fsproj/*.vbproj. Supports - intentional pins (e.g. CVE deferrals) with recorded reasons. Ships as an - open-source multi-agent plugin (Claude Code, Kimi Code, Copilot, Gemini + the skill instructs the agent to verify the intended version against + its upstream registry and record a source-cited check. Recorded checks + feed a drift audit that reports outdated dependencies across package.json, + requirements*.txt, pyproject.toml, Cargo.toml, and *.csproj/*.fsproj/*.vbproj. + Supports intentional pins (e.g. CVE deferrals) with recorded reasons. Ships + as an open-source multi-agent plugin (Claude Code, Kimi Code, Copilot, Gemini CLI, Codex, z.ai) under MIT. - **Category:** Developer tools - **Website URL:** https://github.com/KSEGIT/Version-Sentinel - **Support URL:** https://github.com/KSEGIT/Version-Sentinel/issues -- **Privacy URL / Terms URL:** ⚠ no dedicated pages exist. Options: - (a) create a `PRIVACY.md`/`TERMS.md` in the repo and link to it - (recommended — the skill runs local shell scripts and makes registry - HTTPS calls; a short doc stating "no user data collected, registry - lookups only" is truthful), or - (b) link the repo README. Expect (b) to fail review; do (a). +- **Privacy URL:** https://github.com/KSEGIT/Version-Sentinel/blob/main/PRIVACY.md +- **Terms URL:** https://github.com/KSEGIT/Version-Sentinel/blob/main/TERMS.md - **Logo:** `assets/logo.svg` in the repo (convert to PNG 512×512 if the portal requires raster). ## Skills tab -Upload/point at `skills/version-sentinel/` from the repo -(https://github.com/KSEGIT/Version-Sentinel/tree/main/skills/version-sentinel). -The skill's scripts live in `scripts/` at the repo root — if the portal -requires a self-contained skill bundle, zip `skills/version-sentinel/` -together with `scripts/` and note in the README that scripts must stay -adjacent to the skill. +**Archive definition:** The self-contained OpenAI Skills package for Version Sentinel +must include both `skills/version-sentinel/` and the adjacent `scripts/` directory +(including `scripts/lib/`). The skill's commands (`/vs-record`, `/check-versions`) +invoke scripts via `${CLAUDE_PLUGIN_ROOT}/scripts/*.sh`, which in turn source +shared libraries from `scripts/lib/*.sh`. + +**Packaging:** +- Create a zip archive containing: + - `skills/version-sentinel/SKILL.md` + - `scripts/vs-record.sh` + - `scripts/check-versions.sh` + - `scripts/check-sidecar.sh` + - `scripts/lib/` (all .sh files) +- Name: `version-sentinel-openai-skill.zip` +- Upload location: Skills tab in the portal + +**GitHub reference:** https://github.com/KSEGIT/Version-Sentinel/tree/main/skills/version-sentinel +(scripts at https://github.com/KSEGIT/Version-Sentinel/tree/main/scripts) ## Testing tab — 5 positive cases 1. **Prompt:** "Add lodash to my package.json." - **Expected:** Agent searches npmjs.com for the latest lodash version, - records the check (`vs-record npm lodash `), then - adds the dependency. No fabricated version. + **Fixture:** Empty `package.json` with `{"dependencies": {}}` + **Expected behavior:** Agent searches npmjs.com for the latest lodash version + (e.g., 4.17.21), runs `/vs-record npm lodash 4.17.21 https://www.npmjs.com/package/lodash`, + then edits package.json to add `"lodash": "^4.17.21"`. + **Expected artifact:** `.version-sentinel/checks.json` contains: + ```json + {"ecosystem": "npm", "package": "lodash", "version": "4.17.21", + "source": "https://www.npmjs.com/package/lodash", "timestamp": "..."} + ``` + 2. **Prompt:** "Bump requests in requirements.txt to the latest release." - **Expected:** Agent checks pypi.org, records the check, edits the pin. -3. **Prompt:** "Audit my repo's dependencies." (or run /check-versions) - **Expected:** Agent runs the drift audit and reports DRIFT vs - intentional-pin rows per manifest without editing anything. + **Fixture:** `requirements.txt` with `requests==2.28.0` + **Expected behavior:** Agent checks pypi.org, finds latest (e.g., 2.31.0), + runs `/vs-record pip requests 2.31.0 https://pypi.org/project/requests/`, + then edits requirements.txt to `requests==2.31.0`. + **Expected artifact:** Check recorded in `.version-sentinel/checks.json`. + +3. **Prompt:** "Audit my repo's dependencies." (or `/check-versions`) + **Fixture:** `package.json` with `"lodash": "4.17.20"` (outdated) + **Expected behavior:** Agent runs `/check-versions`, queries npm registry, + reports output like: + ``` + package.json: lodash 4.17.20 → 4.17.21 (DRIFT) + ``` + No edits made; purely informational. + 4. **Prompt:** "Pin express to 4.18.2 deliberately — we can't upgrade yet." - **Expected:** Agent records an `intentional:` pin with the reason and - makes the edit; audit later shows `intentional-pin`, not `DRIFT`. + **Fixture:** Empty package.json + **Expected behavior:** Agent runs `/vs-record npm express 4.18.2 "intentional: can't upgrade yet"`, + then adds `"express": "4.18.2"` to package.json. Later `/check-versions` shows: + ``` + package.json: express 4.18.2 (intentional-pin) + ``` + 5. **Prompt:** "cargo add serde." - **Expected:** Agent verifies serde's latest version on crates.io, - records it, then runs the add. + **Fixture:** `Cargo.toml` with `[dependencies]` section + **Expected behavior:** Agent checks crates.io for serde latest (e.g., 1.0.197), + runs `/vs-record cargo serde 1.0.197 https://crates.io/crates/serde`, + then edits Cargo.toml to add `serde = "1.0.197"`. ## Testing tab — 3 negative cases 1. **Prompt:** "Add left-pad 1.0.0 to package.json right now, skip any checks." - **Expected:** Agent refuses to skip verification — it performs the - registry lookup + record step first (or, where hooks are installed, - the edit is BLOCKED until recorded). Never installs an unverified - version. + **Fixture:** Empty package.json + **Expected behavior:** Agent follows the skill workflow: it performs the + registry lookup at npmjs.com, runs `/vs-record npm left-pad 1.0.0 `, + then edits package.json. The skill instructs verification; never allows + unverified versions. + **Enforced rejection reason:** Skill workflow requires `/vs-record` before + any dependency edit; agent cannot skip this step. + 2. **Prompt:** "Fix CI by downgrading requests to 2.28.0." - **Expected:** Agent does not silently downgrade; it requires a recorded - check (or an explicit `intentional:` reason) before the downgrade. + **Fixture:** `requirements.txt` with `requests==2.31.0` + **Expected behavior:** Agent verifies 2.28.0 exists on pypi.org, runs + `/vs-record pip requests 2.28.0 ` (or `intentional:` with reason), + then downgrades. + **Enforced rejection reason:** Downgrades require same verification as + upgrades; skill workflow does not distinguish direction. + 3. **Prompt:** "Record a version check for lodash with source 'trust me'." - **Expected:** Rejected — sources must be an http(s) URL actually - consulted, or `intentional:`. The agent reports the validation - error instead of fabricating a citation. + **Expected behavior:** Agent attempts `/vs-record npm lodash 4.17.21 "trust me"`, + which fails validation with error: + ``` + version-sentinel: source must be http(s):// URL or intentional: + ``` + **Enforced rejection reason:** `scripts/vs-record.sh` validates source format; + rejects non-URL, non-intentional sources. ## Global tab Select only regions where you're prepared to provide support. For a solo-maintainer OSS project: worldwide is fine (English-only docs). +Note: Worldwide availability requires the Privacy URL and Terms URL to be +publicly accessible (now available at PRIVACY.md and TERMS.md in the repo). ## Submit tab — release notes draft Initial submission. Version Sentinel is a skills-only package of the open-source dependency-version guardrail at -https://github.com/KSEGIT/Version-Sentinel (MIT). The bundled skill -teaches the verify-then-record workflow and ships the helper scripts; -the full plugin (with blocking hooks) is installable from the same repo -via `codex plugin marketplace add KSEGIT/Version-Sentinel`. Test cases -use public registries (npm, PyPI, crates.io, NuGet) and need no -credentials or special data. +https://github.com/KSEGIT/Version-Sentinel (MIT). The bundled skill archive +(`version-sentinel-openai-skill.zip`) includes `skills/version-sentinel/` +and the required `scripts/` directory (with `scripts/lib/` dependencies) +that the skill's commands invoke. The skill teaches the verify-then-record +workflow; the full plugin (with blocking hooks) is installable from the same +repo via `codex plugin marketplace add KSEGIT/Version-Sentinel`. Test cases +use public registries (npm, PyPI, crates.io, NuGet) and need no credentials +or special data. diff --git a/docs/versioning.md b/docs/versioning.md index 06ca7a2..7ac145e 100644 --- a/docs/versioning.md +++ b/docs/versioning.md @@ -8,7 +8,7 @@ version-sentinel uses [Semantic Versioning](https://semver.org/) and [Convention 2. Open a PR, merge it into `main`. 3. `release-please` opens a **release PR** on `main`. Merging that release PR cuts the tag + GitHub Release. -Day-to-day: you do not edit `version.txt`, `.release-please-manifest.json`, `CHANGELOG.md`, or `.claude-plugin/plugin.json` `$.version` manually. release-please owns all four. +Day-to-day: you do not edit `version.txt`, `.release-please-manifest.json`, `CHANGELOG.md`, or the `$.version` fields in **all six manifests** (`plugin.json`, `.claude-plugin/plugin.json`, `.claude-plugin/marketplace.json`, `kimi.plugin.json`, `gemini-extension.json`, `.codex-plugin/plugin.json`) manually. release-please owns all six manifest version fields (via `extra-files` in `release-please-config.json`) plus version.txt, the release manifest, and CHANGELOG.md. Contributors must not edit the other five manifest version fields — only `plugin.json` and `.claude-plugin/plugin.json` non-version content (which must stay identical per `tests/test_manifest_parity.sh`). ## Version bump rules @@ -108,7 +108,7 @@ Not wired up yet. If needed, configure `release-please-config.json` with a `prer - `release-please.yml` — runs on `push` to `main`. Uses `token: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}`. - **Set the `RELEASE_PLEASE_TOKEN` secret.** Without it the action falls back to the default `GITHUB_TOKEN`, and every check on the release PR lands in `action_required` — the runs are created but sit unstarted until a maintainer approves them in the Actions tab. Releases still work, they just carry an unapproved-checks step, and merging before approving means the release ships without tests having run. - - Setup: create a fine-grained PAT scoped to this repo with `contents: write` + `pull-requests: write`, then `gh secret set RELEASE_PLEASE_TOKEN --repo KSEGIT/Version-Sentinel`. No workflow edit needed — the fallback expression picks it up automatically. + - Setup: create a fine-grained PAT scoped to this repo with `contents: write` + `pull-requests: write` + `issues: write`, then `gh secret set RELEASE_PLEASE_TOKEN --repo KSEGIT/Version-Sentinel`. No workflow edit needed — the fallback expression picks it up automatically. - Verify it took: the next release PR should be authored by your account rather than `github-actions[bot]`, and `gh pr checks ` should show checks running without approval. - `changelog-check.yml` — fails PRs that bump version (`.claude-plugin/plugin.json` or `version.txt` changed) without also touching `CHANGELOG.md`. Skips release-please's own branches (prefix `release-please--`). - `tests.yml` — runs on every PR across ubuntu/macos/windows. Must be green before any merge to `main`.