From 9d1340df3b274bb0b0131e71aec6c8da959d5502 Mon Sep 17 00:00:00 2001 From: NagyVikt Date: Tue, 21 Apr 2026 16:37:16 +0200 Subject: [PATCH] Keep npm release verification green when the version is already live The current main branch already passes the release-path test suite, but workflow_dispatch and backfill release runs can still fail later on npm publish when the exact package version is already on the registry. This teaches the release workflow to detect that case and skip publish cleanly while leaving verification intact. Constraint: Existing published versions on npm are immutable and should not make healthy verification runs fail Constraint: Release verification must still run on every workflow invocation before publish decisions are made Rejected: Remove workflow_dispatch support | would block maintainer-side verification on current main Rejected: Skip npm publish unconditionally on manual runs | hides legitimate unpublished release failures Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep the release workflow idempotent for already-published versions so GitHub release state can be backfilled without forcing a version bump Tested: npm test Tested: node --check bin/multiagent-safety.js Tested: npm pack --dry-run Tested: openspec validate agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30 --type change --strict Tested: openspec validate --specs Not-tested: Live GitHub Actions workflow execution before merge --- .github/workflows/release.yml | 26 +++++++++++++++++++ .../.openspec.yaml | 2 ++ .../proposal.md | 14 ++++++++++ .../specs/release-workflow/spec.md | 10 +++++++ .../tasks.md | 21 +++++++++++++++ test/metadata.test.js | 11 ++++++++ 6 files changed, 84 insertions(+) create mode 100644 openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/.openspec.yaml create mode 100644 openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/proposal.md create mode 100644 openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/specs/release-workflow/spec.md create mode 100644 openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/tasks.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ab02297..40df43a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,5 +35,31 @@ jobs: node --check bin/multiagent-safety.js npm pack --dry-run + - name: Resolve package metadata + id: pkg + run: | + echo "name=$(node -p "require('./package.json').name")" >> "$GITHUB_OUTPUT" + echo "version=$(node -p "require('./package.json').version")" >> "$GITHUB_OUTPUT" + + - name: Check npm registry for current version + id: registry + env: + PACKAGE_NAME: ${{ steps.pkg.outputs.name }} + PACKAGE_VERSION: ${{ steps.pkg.outputs.version }} + run: | + if npm view "${PACKAGE_NAME}@${PACKAGE_VERSION}" version >/dev/null 2>&1; then + echo "already_published=true" >> "$GITHUB_OUTPUT" + else + echo "already_published=false" >> "$GITHUB_OUTPUT" + fi + - name: Publish with provenance + if: ${{ steps.registry.outputs.already_published != 'true' }} run: npm publish --provenance --access public + + - name: Skip already-published npm version + if: ${{ steps.registry.outputs.already_published == 'true' }} + env: + PACKAGE_NAME: ${{ steps.pkg.outputs.name }} + PACKAGE_VERSION: ${{ steps.pkg.outputs.version }} + run: echo "${PACKAGE_NAME}@${PACKAGE_VERSION} is already on npm; skipping publish." diff --git a/openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/.openspec.yaml b/openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/.openspec.yaml new file mode 100644 index 0000000..4b8c565 --- /dev/null +++ b/openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-04-21 diff --git a/openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/proposal.md b/openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/proposal.md new file mode 100644 index 0000000..fe6eb1a --- /dev/null +++ b/openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/proposal.md @@ -0,0 +1,14 @@ +## Why + +- The failing `Release to npm (provenance)` runs in GitHub Actions are stale release-tag executions; current `main` is green locally, and `@imdeadpool/guardex@7.0.16` is already published on npm. +- Today the workflow always executes `npm publish`, so manual verification runs or backfill GitHub releases for an already-published version would fail even when the code under test is healthy. + +## What Changes + +- Teach `.github/workflows/release.yml` to resolve the package name/version from `package.json`, check npm for that exact version, and skip `npm publish` when it already exists. +- Add a metadata regression test in `test/metadata.test.js` that locks the workflow's already-published skip behavior. + +## Impact + +- Makes the release workflow idempotent for already-published versions while preserving the existing verify steps and normal publish path for new versions. +- Lets maintainers run `workflow_dispatch` on a healthy `main` release commit without re-breaking on an already-published npm version. diff --git a/openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/specs/release-workflow/spec.md b/openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/specs/release-workflow/spec.md new file mode 100644 index 0000000..0e3ae90 --- /dev/null +++ b/openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/specs/release-workflow/spec.md @@ -0,0 +1,10 @@ +## ADDED Requirements + +### Requirement: Release workflow skips already-published package versions +The `Release to npm (provenance)` workflow SHALL verify the package on every run, but it SHALL skip `npm publish` when the exact `package.json` version is already present on npm. + +#### Scenario: workflow_dispatch on an already-published version +- **GIVEN** the workflow is running against a commit whose `package.json` version already exists on npm +- **WHEN** the verify steps complete successfully +- **THEN** the workflow SHALL detect that published version before the publish step +- **AND** it SHALL report that publish is being skipped instead of failing on `npm publish`. diff --git a/openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/tasks.md b/openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/tasks.md new file mode 100644 index 0000000..b326bbc --- /dev/null +++ b/openspec/changes/agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30/tasks.md @@ -0,0 +1,21 @@ +## 1. Specification + +- [x] 1.1 Finalize proposal scope and acceptance criteria for `agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30`. +- [x] 1.2 Define normative requirements in `specs/release-workflow/spec.md`. + +## 2. Implementation + +- [x] 2.1 Update `.github/workflows/release.yml` so it skips `npm publish` when the current package version already exists on npm. +- [x] 2.2 Add/update `test/metadata.test.js` regression coverage for the release-workflow skip behavior. + +## 3. Verification + +- [x] 3.1 Run `npm test`, `node --check bin/multiagent-safety.js`, and `npm pack --dry-run`. Result: `npm test` passed `152/152`; `node --check bin/multiagent-safety.js` passed; `npm pack --dry-run` produced `imdeadpool-guardex-7.0.16.tgz`. +- [x] 3.2 Run `openspec validate agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30 --type change --strict`. Result: `Change 'agent-codex-make-release-workflow-idempotent-when-ve-2026-04-21-16-30' is valid`. +- [x] 3.3 Run `openspec validate --specs`. Result: `No items found to validate.` + +## 4. Completion + +- [ ] 4.1 Finish the agent branch via PR merge + cleanup (`gx finish --via-pr --wait-for-merge --cleanup` or `bash scripts/agent-branch-finish.sh --branch --base --via-pr --wait-for-merge --cleanup`). +- [ ] 4.2 Record PR URL + final `MERGED` state in the completion handoff. +- [ ] 4.3 Confirm sandbox cleanup (`git worktree list`, `git branch -a`) or capture a `BLOCKED:` handoff if merge/cleanup is pending. diff --git a/test/metadata.test.js b/test/metadata.test.js index e2b91a6..4d63748 100644 --- a/test/metadata.test.js +++ b/test/metadata.test.js @@ -31,6 +31,17 @@ test('release workflow publishes with provenance in CI', () => { assert.match(workflow, /npm publish --provenance --access public/); }); +test('release workflow skips publish when the current version is already on npm', () => { + const workflowPath = path.join(repoRoot, '.github', 'workflows', 'release.yml'); + const workflow = fs.readFileSync(workflowPath, 'utf8'); + assert.match(workflow, /name:\s+Resolve package metadata/); + assert.match(workflow, /name:\s+Check npm registry for current version/); + assert.match(workflow, /npm view "\$\{PACKAGE_NAME\}@\$\{PACKAGE_VERSION\}" version/); + assert.match(workflow, /if:\s+\$\{\{\s*steps\.registry\.outputs\.already_published != 'true'\s*\}\}/); + assert.match(workflow, /if:\s+\$\{\{\s*steps\.registry\.outputs\.already_published == 'true'\s*\}\}/); + assert.match(workflow, /skipping publish\./); +}); + test('release workflow only publishes from published releases or manual dispatch', () => { const workflowPath = path.join(repoRoot, '.github', 'workflows', 'release.yml'); const workflow = fs.readFileSync(workflowPath, 'utf8');