From 11d2f543d6ea0334521f519fbdc5b7047c6f57a4 Mon Sep 17 00:00:00 2001 From: Luis Mendez Date: Sun, 10 May 2026 22:08:03 +0200 Subject: [PATCH 1/9] chore(release): bump 0.7.0 -> 0.8.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CHANGELOG entry summarises the v0.8.0 cycle: - Plugin distribution moves to orphan dist/claude-plugin + git-subdir marketplace source (ADR-0043, closes #461 + #474). - New: /issue:tackle conductor skill (#443), /specorator:onboard guided series (#460), GitHub remote MCP server in project default (#471), conductor-driven model-tier injection (#440), `specorator --version` / `-v` flag (#424, #419). - Plugin install smoke test in CI (#427); operational-bot dry-run + drift checks (#438); ADR-0042 typed-artifact reader seam. - Fixes for plugin command prefix (#420), /quality:status CLI usage (#426, #428), init no-git warning (#421), product-page deployment (#456), archive-sync git identity (#468), feature-tracker readFile race (#408), and historical-status normalisation (#439). - ADR-0030 (repo-adoption) withdrawn — superseded by plugin packaging. - README + docs/specorator.md status banner refreshed for v0.8.0. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ README.md | 4 ++-- docs/specorator.md | 4 ++-- package.json | 2 +- 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0426d9176..bbb1c65b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,51 @@ All notable changes to Specorator are documented here. Format follows [Keep a Ch --- +## [v0.8.0] — 2026-05-10 + +### Changed + +- **Plugin distribution moves to an orphan dist branch.** The Claude Code plugin bundle (`claude-plugin/specorator/{agents,skills,commands,.claude-plugin}/` + `.mcp.json`) is now gitignored on `develop` and `main` and published to the long-lived orphan branch `dist/claude-plugin` by `.github/workflows/publish-claude-plugin.yml` on every push to `main`. The marketplace entry in `.claude-plugin/marketplace.json` switched from a relative-path source to a `git-subdir` source pinned to that ref. Marketplace consumers transparently pick up the new bundle on `/plugin marketplace update`. ADR-0043 records the rationale and considered alternatives. Closes #461 (gitignore-the-bundle) and #474 (install-flow redesign). +- `npm run check:claude-plugin` is now structural-only on a clean `develop`/`main` checkout — generated-output checks (manifest, `.mcp.json`, agents/skills/commands directories) are conditional on the file being present. The `build:claude-plugin --check` drift check is no longer part of the verify gate. +- `.github/workflows/release.yml` now runs `npm run build:claude-plugin` before the readiness gate so the npm tarball still ships the bundle in `claude-plugin/specorator/`. +- ADR-0030 (repo-adoption track) withdrawn — superseded by the plugin-packaging path. + +### Added + +- **`/issue:tackle` conductor skill** — triage-first workflow for resolving a GitHub issue or PR end-to-end: classifies type/priority, scans for open tasks, proposes a resolution path, creates an isolated worktree, guides execution, and opens a PR (#443). +- **`/specorator:onboard`** — guided 5-step onboarding series scaffolded on first install. Idempotent; falls back to local Markdown when `gh` auth is missing (#460). +- **GitHub remote MCP server** wired into the project `.mcp.json` and the plugin bundle. Issue, PR, branch, and review-comment operations are now first-class for any agent in a Specorator project (#471). +- **`specorator --version` / `-v`** CLI flag — version reported from `package.json#version`, no more drift (#424). Bare `--version` and `-V` now exit `0` (#419). +- **Conductor-driven model-tier injection** for subagents — orchestrator skills can specify the model tier per dispatch (#440). +- **Plugin install smoke test** in CI — `.github/workflows/smoke-test.yml` packs the tarball, installs `specorator` globally, and asserts CLI subcommand exit codes (#427). +- **ADR-0042** — adopt typed-artifact reader seam to keep agent IO contracts auditable (#442). +- **ADR-0043** — distribute Claude Code plugin bundle from an orphan dist branch via `git-subdir`. +- **Plugin user manual** at `docs/how-to/install-claude-plugin.md` rewritten for the orphan-branch flow + plugin-first install path (#410, #431, #467). +- **Operational bot dry-run + drift checks** — `tests/scripts/operational-bots.test.ts` validates every PROMPT.md surface (#438). +- **Agentic control-plane threat model** at `docs/security/control-plane-threats.md` (#437). +- **Specorator product ladder** at `docs/specorator-product/product-ladder.md` and supporting proof-loop asset (#435, #436). +- **Automation contract** entry point at `docs/automation-contract.md` cross-referencing every automation surface (#434). + +### Fixed + +- `scripts/build-claude-plugin.ts` strips the `specorator/` prefix from canonical command paths so `/specorator:specorator:init` collapses back to `/specorator:init` in the published bundle (#420). +- `specorator init` warns when the target directory has no `.git` so adopters see the missing-VCS state before running through onboarding (#421). +- `/quality:status` and quality-related agents/skills now invoke the `specorator` CLI rather than raw `npm run` (#426, #428). +- Hardcoded `model: opus` removed from `architect`, `dev`, and `reviewer` subagents — the conductor sets the tier (#429). +- Quality status loader normalises historical status values (`done`, `complete`) to the canonical `completed` to silence loader warnings on legacy specs (#439). +- Product page deployment moved to a dedicated `gh-pages` branch — broken images on the public site fixed (#456). +- `.github/workflows/sync-github-archive.yml` sets the bot git identity before the merge step so scheduled syncs no longer fail at `nothing to commit` (#468). +- Feature-tracker test guards `readFile` against the ENOENT race when the page-data fixture is regenerated mid-run (#408). + +### Internal + +- `actions/setup-node` bumped to v6.4.0 across remaining workflows (#473). +- v0.7 spec stub backfilled at `specs/v07-npm-publish/` for traceability (#432). +- `docs/specorator-product/site-vision.md` defines the Astro product-page roadmap. +- 30+ merged PRs since v0.7.0 — see the GitHub Release notes for the full enumeration. + +--- + ## [v0.7.0] — 2026-05-09 ### Breaking diff --git a/README.md b/README.md index c84a422dd..58b11b1a8 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # Specorator — Agentic Development Workflow -![Version](https://img.shields.io/badge/version-v0.7.0-blue) ![License](https://img.shields.io/badge/license-MIT-green) +![Version](https://img.shields.io/badge/version-v0.8.0-blue) ![License](https://img.shields.io/badge/license-MIT-green) [![Verify](https://github.com/Luis85/agentic-workflow/actions/workflows/verify.yml/badge.svg?branch=main)](https://github.com/Luis85/agentic-workflow/actions/workflows/verify.yml) [![gitleaks](https://github.com/Luis85/agentic-workflow/actions/workflows/gitleaks.yml/badge.svg?branch=main)](https://github.com/Luis85/agentic-workflow/actions/workflows/gitleaks.yml) [![typos](https://github.com/Luis85/agentic-workflow/actions/workflows/typos.yml/badge.svg?branch=main)](https://github.com/Luis85/agentic-workflow/actions/workflows/typos.yml) [![zizmor](https://github.com/Luis85/agentic-workflow/actions/workflows/zizmor.yml/badge.svg?branch=main)](https://github.com/Luis85/agentic-workflow/actions/workflows/zizmor.yml) **Build software the right way with AI.** Specorator is a spec-driven workflow template: humans decide what to build, specialist agents handle how, and every requirement, decision, task, test, and release note stays traceable. -> **Status:** v0.7.0 — npm publication migrated from GitHub Packages (`@luis85/agentic-workflow`) to npmjs.com as `specorator` (unscoped, public). Adopters now run `npm install -g specorator` with no `.npmrc` configuration or PAT. Trusted publishing via OIDC is deferred per [ADR-0041](docs/adr/0041-defer-npmjs-trusted-publishing.md) (tracking [#411](https://github.com/Luis85/agentic-workflow/issues/411)) — v0.7.x publishes via classic `NPM_TOKEN` and ships without npm provenance attestations until the trusted publisher path is unblocked. The GitHub Release tarball asset still carries a sigstore attestation. v0.6.2 patched the Layer 1 readiness `refs/heads/main` lookup. v0.6.1 shipped Specorator as a Claude Code plugin. v0.6.0 introduced the Astro 6 product page. Claude Code is first-class; Codex, Cursor, Aider, Copilot, and Gemini have Markdown-based walkthroughs. **Breaking:** the GitHub Packages package `@luis85/agentic-workflow` is deprecated; existing versions still install but new releases land only on npmjs.com. +> **Status:** v0.8.0 — Claude Code plugin bundle moves to a long-lived orphan branch (`dist/claude-plugin`) rebuilt by CI on every push to `main`; the marketplace entry now uses a `git-subdir` source pinned to that ref ([ADR-0043](docs/adr/0043-distribute-claude-plugin-bundle-from-orphan-dist-branch.md)). Bundle is gitignored on `develop`/`main`, so PR diffs stop carrying generated-artifact churn. New `/issue:tackle` conductor skill, `/specorator:onboard` guided issue series, GitHub remote MCP server in the project default, conductor-driven model-tier injection for subagents, and `specorator --version` flag. v0.7.0 migrated the npm CLI from GitHub Packages to npmjs.com as `specorator` (unscoped, public, no `.npmrc`). Trusted publishing via OIDC is deferred per [ADR-0041](docs/adr/0041-defer-npmjs-trusted-publishing.md) — v0.7.x and v0.8.x publish via classic `NPM_TOKEN`. Claude Code is first-class; Codex, Cursor, Aider, Copilot, and Gemini have Markdown-based walkthroughs. Product page: diff --git a/docs/specorator.md b/docs/specorator.md index 11cedcc15..aabf0e0e9 100644 --- a/docs/specorator.md +++ b/docs/specorator.md @@ -1,8 +1,8 @@ # Specorator — Quality-Driven, Agentic Development Workflow -**Version:** 0.7.0 · **Status:** Minor release — npm CLI migrated from GitHub Packages to npmjs.com (public, no auth, OIDC trusted publishing) · **Purpose:** Spec-driven, agentic workflow template +**Version:** 0.8.0 · **Status:** Minor release — Claude Code plugin bundle moves to orphan dist branch + `git-subdir` marketplace source (ADR-0043); plus `/issue:tackle`, `/specorator:onboard`, GitHub MCP, conductor-driven model tiers, `--version` flag · **Purpose:** Spec-driven, agentic workflow template -v0.7.0 migrates the Specorator npm CLI from GitHub Packages (`@luis85/agentic-workflow`, auth-walled) to npmjs.com as `specorator` (unscoped, public, no `.npmrc` configuration required). Published via OIDC Trusted Publishing with `--provenance` attestation; `NODE_AUTH_TOKEN` is no longer required. See [ADR-0040](adr/0040-migrate-npm-publication-to-npmjs-com.md). v0.6.2 was a patch release unblocking the v0.6.x dispatch path. v0.6.1 shipped Specorator as a Claude Code plugin (`claude-plugin/specorator/`) with auto-derived plugin manifest version and verify/release-readiness gates. v0.6.0 introduced the Astro 6 product page replacing `sites/index.html`. v0.5.0 introduced the release workflow, GitHub Release / Package distribution, and fresh-surface package contract; v0.5.1 was the recovery release for the [Immutable Releases incident](https://github.com/Luis85/agentic-workflow/issues/233). +v0.8.0 moves the Claude Code plugin bundle to a long-lived orphan branch `dist/claude-plugin` rebuilt by CI on every push to `main`. The marketplace entry in `.claude-plugin/marketplace.json` switches to a `git-subdir` source pinned to that ref, so the bundle becomes gitignored on `develop`/`main` and PR diffs stop carrying generated-artifact churn. See [ADR-0043](adr/0043-distribute-claude-plugin-bundle-from-orphan-dist-branch.md). v0.8.0 also adds the `/issue:tackle` conductor skill (triage-first issue/PR resolution), `/specorator:onboard` (guided 5-step onboarding series), the GitHub remote MCP server in the project `.mcp.json` default, conductor-driven model-tier injection for subagents, and `specorator --version` / `-v`. v0.7.0 migrated the Specorator npm CLI from GitHub Packages (`@luis85/agentic-workflow`) to npmjs.com as `specorator` (unscoped, public). See [ADR-0040](adr/0040-migrate-npm-publication-to-npmjs-com.md). v0.6.2 was a patch release unblocking the v0.6.x dispatch path. v0.6.1 shipped Specorator as a Claude Code plugin. v0.6.0 introduced the Astro 6 product page. v0.5.0 introduced the release workflow, GitHub Release / Package distribution, and fresh-surface package contract; v0.5.1 was the recovery release for the [Immutable Releases incident](https://github.com/Luis85/agentic-workflow/issues/233). A solution-agnostic, **spec-driven** workflow for building software with humans and AI agents. Treats specifications as the source of truth and code as their artifact. Covers the full SDLC: Product → UX → UI → Engineering → Testing → Quality → Delivery → Operations. diff --git a/package.json b/package.json index 0888637bd..bb20693cf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "specorator", - "version": "0.7.0", + "version": "0.8.0", "description": "Specorator — template for spec-driven, agentic software development. The workflow is the deliverable.", "keywords": [ "agents", From 834b43de4f90b74834afe0707f8dcdac6147fa4e Mon Sep 17 00:00:00 2001 From: Luis Mendez Date: Sun, 10 May 2026 22:21:15 +0200 Subject: [PATCH 2/9] fix(sites): accept superseded workflow-state status in feature tracker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex P2 on PR #478: scripts/lib/workflow-schema.ts treats `superseded` as a valid workflow status, and v0.8.0 sets specs/repo-adoption-track/workflow-state.md to `status: superseded` (ADR-0030 withdrawn). The Astro content schema's StateStatusEnum was missing that value, so the feature-tracker loader silently skipped superseded workflow-state files instead of ingesting them — dropping them from product-page reporting. Co-Authored-By: Claude Opus 4.7 (1M context) --- sites/src/content/schemas.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sites/src/content/schemas.ts b/sites/src/content/schemas.ts index 6acb572a4..77ba5ad94 100644 --- a/sites/src/content/schemas.ts +++ b/sites/src/content/schemas.ts @@ -42,8 +42,13 @@ export const StageEnum = z.enum([ "done", ]); -// "done" and "paused" are valid per scripts/lib/workflow-schema.ts workflowStatuses; -// included here so the site loader accepts all values the script validator allows. +// "done", "paused", and "superseded" are valid per +// scripts/lib/workflow-schema.ts `workflowStatuses`; included here so the +// site loader accepts every value the script validator allows. Without +// `superseded`, workflow-state files set to that status (e.g. +// `specs/repo-adoption-track/workflow-state.md` after ADR-0030 was +// withdrawn in v0.8.0) are silently dropped from the feature tracker +// (Codex P2 on PR #478). export const StateStatusEnum = z.enum([ "pending", "active", @@ -53,6 +58,7 @@ export const StateStatusEnum = z.enum([ "blocked", "done", "paused", + "superseded", ]); export const FeatureTypeEnum = z.enum(["feature", "chore", "bug", "spike", "docs"]); From 0bda418f03e221d317b2cd5e244fcc7d2144db95 Mon Sep 17 00:00:00 2001 From: Luis Mendez Date: Sun, 10 May 2026 22:29:05 +0200 Subject: [PATCH 3/9] chore(release): restore npmjs.com Trusted Publishing (ADR-0044) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trusted Publisher activated on the specorator package on 2026-05-10, closing #411. Switch release.yml step 10 back to OIDC + provenance: - Drop NODE_AUTH_TOKEN env from publish step; OIDC token mints via id-token: write. - Drop the NPM_TOKEN-not-set guard. - Add --provenance to npm publish so every release ships with a sigstore attestation on the npmjs.com package page. - Remove # zizmor: ignore[use-trusted-publishing] suppression — the audit passes by construction now. - Refresh workflow header + permissions comment block. ADR-0044 supersedes ADR-0041 (frontmatter status: superseded; superseded-by: [ADR-0044]). ADR index regenerated. Operator guide §1 (prereqs), §5 (publish step), §5.1 (provenance posture), §7.1 (manual recovery) refreshed: NPM_TOKEN repo secret is decommissioned; manual recovery now requires minting a fresh classic Automation token on npmjs.com web UI and revoking after use. scripts/lib/release-readiness.ts comment block flips id-token: write narrative from "kept across the ADR-0041 fallback for the GitHub Release tarball attestation" to "load-bearing for both OIDC paths (npm publish + Release tarball attestation)". CHANGELOG v0.8.0 entry adds the Trusted Publishing restoration line. Closes #411 Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/release.yml | 80 ++++++------- CHANGELOG.md | 1 + .../0041-defer-npmjs-trusted-publishing.md | 6 +- .../0044-restore-npmjs-trusted-publishing.md | 106 ++++++++++++++++++ docs/adr/README.md | 3 +- docs/release-operator-guide.md | 19 ++-- .../REQUIRED_WORKFLOW_PERMISSIONS.md | 12 +- scripts/lib/release-readiness.ts | 12 +- 8 files changed, 167 insertions(+), 72 deletions(-) create mode 100644 docs/adr/0044-restore-npmjs-trusted-publishing.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ecd9fafda..f9b5edb9b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,10 +7,10 @@ name: Release # check:release-package-contents` (Layer 2 — fresh-surface contract per # ADR-0021 / SPEC-V05-010), `actions/attest-build-provenance` for the # workflow-built tarball, `gh release create` for the canonical `vX.Y.Z` tag -# on `main` per ADR-0020, `npm publish` to npmjs.com via classic NPM_TOKEN -# (ADR-0041 — interim fallback to ADR-0040 Trusted Publishing, tracked in -# issue #411), and `gh release upload` to attach the package tarball as a -# release asset. +# on `main` per ADR-0020, `npm publish --provenance` to npmjs.com via +# Trusted Publishing (ADR-0044 restoring ADR-0040 after the ADR-0041 +# NPM_TOKEN fallback used for v0.7.0 / v0.7.1), and `gh release upload` to +# attach the package tarball as a release asset. # # Inputs and the confirm gate satisfy SPEC-V05-002 (explicit publish # authorisation), SPEC-V05-003 (GitHub Release publication), SPEC-V05-004 @@ -50,16 +50,16 @@ on: required: false default: false -# Least-privilege workflow permissions per ADR-0020 / ADR-0040 / ADR-0041 / +# Least-privilege workflow permissions per ADR-0020 / ADR-0040 / ADR-0044 / # SPEC-V05-002 / NFR-V05-001. `REQUIRED_WORKFLOW_PERMISSIONS` # (scripts/lib/release-readiness.ts) enforces the top-level block as exactly # { contents: write, attestations: write, id-token: write }; job-level # overrides may only narrow, never widen. `contents: write` for # `gh release create` + upload, `attestations: write` to persist GitHub -# Release tarball attestations, `id-token: write` for the -# `actions/attest-build-provenance` step that signs the GitHub Release -# tarball asset (kept across the ADR-0041 fallback so the GitHub Release -# asset retains its provenance even while npm provenance is paused). +# Release tarball attestations, `id-token: write` is now load-bearing for +# both attestation paths: it mints the OIDC token consumed by `npm publish +# --provenance` (ADR-0044) and the OIDC token consumed by +# `actions/attest-build-provenance` (Release tarball asset). # zizmor: suppressed inline. permissions: # zizmor: ignore[excessive-permissions,undocumented-permissions] contents: write # zizmor: ignore[excessive-permissions] @@ -81,9 +81,11 @@ jobs: name: Manual GitHub Release needs: smoke runs-on: ubuntu-latest - # Deployment environment scopes `NPM_TOKEN` to release dispatches per - # GitHub's `secrets-without-environment` advisory and lets the operator - # add required-reviewer / wait-timer protection rules without changing + # Deployment environment matches the npmjs.com Trusted Publisher + # configuration (workflow=`release.yml`, environment=`release`) so the + # OIDC token minted by GitHub for this job authenticates the npm + # publish step. The environment also lets the operator add + # required-reviewer / wait-timer protection rules without changing # the workflow. URL points at the just-published GitHub Release page. environment: name: release @@ -97,13 +99,9 @@ jobs: # `registry-url` writes `~/.npmrc` so `npm publish` resolves the # registry to npmjs.com. The `scope` field is unnecessary because the - # package is unscoped (`specorator`), per ADR-0040. - # - # `NODE_AUTH_TOKEN` is supplied at the publish step (not here) via the - # `NPM_TOKEN` repo secret — a classic Automation token, per ADR-0041 - # (interim fallback). When Trusted Publishing is unblocked (#411) the - # publish step will switch back to OIDC + `--provenance` and the - # `NPM_TOKEN` env will be removed. + # package is unscoped (`specorator`), per ADR-0040. No + # `NODE_AUTH_TOKEN` is set — Trusted Publishing (ADR-0044) supplies + # the auth token via OIDC at the publish step. - name: Setup Node uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: @@ -343,18 +341,19 @@ jobs: --jq '.body' \ || echo '(generate-notes preview unavailable; readiness diagnostics above remain authoritative)' - # Step 10 — publish the package to npmjs.com via classic NPM_TOKEN - # (ADR-0041, interim fallback to ADR-0040). The v0.7.0 release - # dispatch failed at this step with `404 PUT` because npmjs.com - # Trusted Publishing requires the publisher to be pre-registered - # against an existing package, and `specorator` did not exist at - # the time of the dispatch (chicken-and-egg). Subsequent attempts - # to configure Trusted Publisher on the now-existing package are - # currently blocked. `actions/setup-node` wrote `~/.npmrc` pointing - # at `https://registry.npmjs.org`; `NODE_AUTH_TOKEN` (set via the - # `NPM_TOKEN` repo secret — a classic Automation token) authenticates - # the publish. Tracking issue: #411. Re-enable OIDC + `--provenance` - # once Trusted Publisher is configured and verified. + # Step 10 — publish the package to npmjs.com via npmjs.com Trusted + # Publishing (ADR-0044, restoring ADR-0040 after the ADR-0041 + # NPM_TOKEN fallback used for v0.7.0 / v0.7.1). The publisher is + # pre-registered on npmjs.com against this workflow file + # (`release.yml`) on the `release` deployment environment; the + # OIDC token minted via `id-token: write` authenticates the + # publish. `actions/setup-node` wrote `~/.npmrc` pointing at + # `https://registry.npmjs.org`; no `NODE_AUTH_TOKEN` is read. + # `npm publish --provenance` mints a sigstore provenance + # statement that ships with the tarball and is visible on the + # npmjs.com package page under `Provenance`. Tracking issue + # #411 closed when Trusted Publishing was activated on + # 2026-05-10. # # The pre-flight check short-circuits before `npm publish` if # `package.json` drifted from `INPUT_VERSION` between Layer 1 @@ -376,23 +375,12 @@ jobs: # - npm E404 → version genuinely not published, proceed. # - any other failure (transient registry / auth / DNS) → fail # closed. - # - # zizmor's `use-trusted-publishing` audit suggests OIDC trusted - # publishing instead of a token. Trusted Publishing is the - # documented target (ADR-0040) and intended return path; ADR-0041 - # records the deferral. The audit is suppressed at the step - # boundary; revisit when issue #411 closes. - - name: Publish to npmjs.com # zizmor: ignore[use-trusted-publishing] + - name: Publish to npmjs.com if: ${{ ! inputs.dry_run && inputs.publish_package }} env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} INPUT_VERSION: ${{ inputs.version }} TARBALL: ${{ steps.pack.outputs.tarball }} run: | - if [ -z "${NODE_AUTH_TOKEN}" ]; then - echo "::error::NPM_TOKEN repo secret is not set — refusing to publish (ADR-0041 fallback requires a classic Automation token until issue #411 closes)" >&2 - exit 1 - fi actual="$(node -p "require('./package.json').name + '@' + require('./package.json').version")" expected="specorator@${INPUT_VERSION}" if [ "$actual" != "$expected" ]; then @@ -408,9 +396,9 @@ jobs: elif echo "$view_output" | grep -qE "\"code\": *\"E404\"|E404|code E404|404 Not Found"; then # Publish the byte-identical tarball produced in step 5 so the # published archive equals the GitHub Release asset uploaded in - # step 11 (T-V05-013). No `--provenance` flag — Trusted Publishing - # is deferred per ADR-0041 (#411). - npm publish "${TARBALL}" + # step 11 (T-V05-013). `--provenance` mints a sigstore provenance + # statement via the OIDC token (ADR-0044, restoring ADR-0040). + npm publish --provenance "${TARBALL}" else echo "::error::npm view failed with a non-404 error — refusing to publish so EPUBLISHCONFLICT cannot mask a real failure" >&2 echo "$view_output" >&2 diff --git a/CHANGELOG.md b/CHANGELOG.md index bbb1c65b7..e4afd22cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ All notable changes to Specorator are documented here. Format follows [Keep a Ch ### Changed - **Plugin distribution moves to an orphan dist branch.** The Claude Code plugin bundle (`claude-plugin/specorator/{agents,skills,commands,.claude-plugin}/` + `.mcp.json`) is now gitignored on `develop` and `main` and published to the long-lived orphan branch `dist/claude-plugin` by `.github/workflows/publish-claude-plugin.yml` on every push to `main`. The marketplace entry in `.claude-plugin/marketplace.json` switched from a relative-path source to a `git-subdir` source pinned to that ref. Marketplace consumers transparently pick up the new bundle on `/plugin marketplace update`. ADR-0043 records the rationale and considered alternatives. Closes #461 (gitignore-the-bundle) and #474 (install-flow redesign). +- **npmjs.com Trusted Publishing restored** — `release.yml` step 10 reverts to OIDC + `npm publish --provenance`; `NODE_AUTH_TOKEN` env removed; `NPM_TOKEN` repo secret decommissioned; `# zizmor: ignore[use-trusted-publishing]` suppression dropped. Every v0.8.x release ships with a sigstore provenance attestation visible on the npmjs.com package page. ADR-0044 supersedes ADR-0041; closes #411. Operator-guide §1 (prereqs), §5 (publish step), §5.1 (provenance posture), and §7.1 (manual recovery) refreshed for the OIDC happy path. - `npm run check:claude-plugin` is now structural-only on a clean `develop`/`main` checkout — generated-output checks (manifest, `.mcp.json`, agents/skills/commands directories) are conditional on the file being present. The `build:claude-plugin --check` drift check is no longer part of the verify gate. - `.github/workflows/release.yml` now runs `npm run build:claude-plugin` before the readiness gate so the npm tarball still ships the bundle in `claude-plugin/specorator/`. - ADR-0030 (repo-adoption track) withdrawn — superseded by the plugin-packaging path. diff --git a/docs/adr/0041-defer-npmjs-trusted-publishing.md b/docs/adr/0041-defer-npmjs-trusted-publishing.md index caa6bb3d2..3c730b772 100644 --- a/docs/adr/0041-defer-npmjs-trusted-publishing.md +++ b/docs/adr/0041-defer-npmjs-trusted-publishing.md @@ -1,7 +1,7 @@ --- id: ADR-0041 title: Defer npmjs.com Trusted Publishing — fall back to NPM_TOKEN -status: accepted +status: superseded date: 2026-05-09 deciders: - maintainer @@ -10,7 +10,7 @@ consulted: informed: - template adopters supersedes: [] -superseded-by: [] +superseded-by: [ADR-0044] tags: [release, distribution, security] --- @@ -18,7 +18,7 @@ tags: [release, distribution, security] ## Status -Accepted +Superseded by [ADR-0044](0044-restore-npmjs-trusted-publishing.md) on 2026-05-10. npmjs.com Trusted Publisher was activated against `release.yml`; the OIDC + `--provenance` path is restored beginning with the v0.8.0 release dispatch. Tracking issue #411 closed. ## Context diff --git a/docs/adr/0044-restore-npmjs-trusted-publishing.md b/docs/adr/0044-restore-npmjs-trusted-publishing.md new file mode 100644 index 000000000..61a1d1fa0 --- /dev/null +++ b/docs/adr/0044-restore-npmjs-trusted-publishing.md @@ -0,0 +1,106 @@ +--- +id: ADR-0044 +title: Restore npmjs.com Trusted Publishing — re-enable OIDC + provenance +status: accepted +date: 2026-05-10 +deciders: + - Luis Mendez +consulted: + - docs/adr/0040-migrate-npm-publication-to-npmjs-com.md + - docs/adr/0041-defer-npmjs-trusted-publishing.md +informed: + - template adopters +supersedes: [ADR-0041] +superseded-by: [] +tags: [release, distribution, security, supply-chain] +--- + +# ADR-0044 — Restore npmjs.com Trusted Publishing — re-enable OIDC + provenance + +## Status + +Accepted + +## Context + +[ADR-0040](0040-migrate-npm-publication-to-npmjs-com.md) committed Specorator to publishing the `specorator` npm package to npmjs.com via OIDC Trusted Publishing, with `npm publish --provenance` minting a sigstore provenance attestation. The v0.7.0 release dispatch failed at the publish step with `404 PUT` because npmjs.com Trusted Publishing requires the publisher to be pre-registered against an existing package, and `specorator` did not exist on npmjs.com when the dispatch ran. [ADR-0041](0041-defer-npmjs-trusted-publishing.md) recorded the fallback to a classic `NPM_TOKEN` Automation token and tracked the diagnose-and-fix work in [issue #411](https://github.com/Luis85/agentic-workflow/issues/411). + +The fallback shipped v0.7.0, v0.7.1, and the package now exists on npmjs.com with three published versions. The operator subsequently activated the npmjs.com Trusted Publisher configuration on the `specorator` package against this repository's `release.yml` workflow on 2026-05-10, closing #411. The OIDC path is now functional end-to-end. + +This ADR restores ADR-0040's published auth shape (OIDC-only, `--provenance` on every publish) and supersedes ADR-0041's interim fallback. The ADR-0041 NPM_TOKEN secret is decommissioned: removed from the publish step's environment, no longer required by `release.yml`, and revoked from the maintainer's npmjs.com account. The `id-token: write` workflow permission stays — it was kept across the ADR-0041 fallback for the `actions/attest-build-provenance` step that signs the GitHub Release tarball asset, and it is now also required for OIDC token minting on the npm publish step. + +## Decision + +Re-enable npmjs.com Trusted Publishing for the `specorator` package, beginning with the v0.8.0 release dispatch. + +`release.yml` step 10 (`Publish to npmjs.com`) is updated to: + +- Drop the `NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}` environment variable. The OIDC token minted via `id-token: write` authenticates the publish; no long-lived secret is read. +- Drop the leading `if [ -z "${NODE_AUTH_TOKEN}" ]` guard. The token-not-set failure mode no longer applies because the auth path is OIDC. +- Add `--provenance` to `npm publish` so every published version ships with a sigstore provenance attestation visible on the npmjs.com package page (`Provenance` badge under `Provenance` tab). +- Remove the inline `# zizmor: ignore[use-trusted-publishing]` suppression on the publish step. The step is now compliant with zizmor's `use-trusted-publishing` audit by construction. + +`package.json#publishConfig.provenance` stays implicit — the `--provenance` flag on the `npm publish` invocation is sufficient and matches the ADR-0040 §Compliance shape ("`npm publish --provenance` is invoked from the workflow"). The publishConfig override is not added back to keep `npm publish` from a contributor's local checkout (without `--provenance`) from accidentally minting a useless provenance statement. + +The pre-flight `package.json` identity check, `npm view` idempotency probe, and tarball-publish flow from ADR-0041 are preserved unchanged — this ADR changes only the auth mechanism. + +The `NPM_TOKEN` repo secret is removed from `Luis85/agentic-workflow` after the first successful OIDC publish on the `release` environment. Tracking issue #411 is closed. + +## Considered options + +### Option A — Re-enable OIDC + `--provenance` on this PR — chosen + +- Pros: Restores ADR-0040's published shape immediately. Every v0.8.x and onward release ships with npm-side provenance attestations, closing the gap from v0.7.x. Removes the long-lived `NPM_TOKEN` credential and shrinks the auth surface area. +- Cons: First Trusted-Publishing dispatch is the v0.8.0 release. If the npmjs.com Trusted Publisher configuration is misaligned (wrong workflow file name, wrong environment, wrong workflow ref), the publish step fails closed and the operator either fixes the configuration on npmjs.com or re-dispatches from the `NPM_TOKEN` fallback shape. Mitigation: validate first against a `0.8.0-rc.1` pre-release dispatch; only proceed to `0.8.0` if RC publish succeeds. + +### Option B — Stay on NPM_TOKEN through v0.8.x; restore in v0.9.0 + +- Pros: Decouples the workflow change from the v0.8.0 promotion; lets the operator dry-run other auth paths first. +- Cons: Indefinitely defers ADR-0040's compliance footprint. Long-lived `NPM_TOKEN` keeps living in repo secrets. Every additional release without provenance widens the attestation gap on npmjs.com. + +### Option C — Re-enable OIDC, keep `NPM_TOKEN` as a parallel fallback + +- Pros: Belt-and-braces — if OIDC fails, the workflow falls through to NPM_TOKEN. +- Cons: Doubled auth surface. Workflow logic becomes branchy and hard to reason about. zizmor's `use-trusted-publishing` audit still fires on the fallback path. Defeats the simplification ADR-0044 buys. + +## Consequences + +### Positive + +- Every v0.8.x and onward release ships with sigstore provenance attestations on npmjs.com (visible under the package page's `Provenance` tab and via `npm view specorator --json | jq '.dist.attestations'`). +- The `NPM_TOKEN` long-lived credential is removed from the repo secrets surface. +- zizmor's `use-trusted-publishing` audit passes on `release.yml` without an inline suppression on the publish step. +- ADR-0040's §Consequences/Neutral line "NPM_TOKEN repo secret stays as an emergency fallback for one cycle" is honored — the fallback was used for one cycle (v0.7.0 + v0.7.1) and is now removed. + +### Negative + +- First Trusted Publishing dispatch is a real production publish; misconfiguration on npmjs.com would block the publish step. + - Mitigation 1: Validate via a `0.8.0-rc.1` pre-release dispatch before the `0.8.0` final dispatch (`gh workflow run release.yml -f version=0.8.0-rc.1 -f dry_run=false -f prerelease=true -f publish_package=true -f confirm=0.8.0-rc.1`). + - Mitigation 2: If the publish step fails, the operator can revert ADR-0044 with a one-line PR re-adding `NODE_AUTH_TOKEN` + `--provenance=false` and re-dispatch. +- A future Trusted Publisher misalignment on npmjs.com (wrong workflow ref, account change, GitHub repo rename) would block all publishes until the configuration is repaired on the npmjs.com side. Operator-guide §5 documents the configuration shape. + +### Neutral + +- ADR-0041 transitions from `accepted` to `superseded`, with `superseded-by: [ADR-0044]`. ADR-0040 is unaffected — its core decision (npmjs.com as the registry, `specorator` as the package name) was preserved across the deferral. +- `actions/attest-build-provenance` continues to sign the GitHub Release tarball asset. The `id-token: write` workflow permission is now load-bearing for both attestation paths (npm publish + Release asset). +- The bootstrap `specorator-bootstrap` Automation token used to publish v0.7.0 was already revoked in ADR-0041's compliance step. The `NPM_TOKEN` repo secret is decommissioned by ADR-0044. + +## Compliance + +- `.github/workflows/release.yml` step 10 reads no `NODE_AUTH_TOKEN`, runs `npm publish --provenance`, and removes the `# zizmor: ignore[use-trusted-publishing]` suppression. +- `scripts/lib/release-readiness.ts` `REQUIRED_WORKFLOW_PERMISSIONS` continues to enforce `id-token: write` (now load-bearing for both the npm publish OIDC and the Release tarball attestation). +- The first v0.8.0 release dispatch is preceded by a `0.8.0-rc.1` pre-release dispatch as a Trusted Publishing smoke test. +- The `NPM_TOKEN` repo secret on `Luis85/agentic-workflow` is removed after the first successful OIDC publish. +- Tracking issue [#411](https://github.com/Luis85/agentic-workflow/issues/411) is closed. + +## References + +- Supersedes: [ADR-0041](0041-defer-npmjs-trusted-publishing.md) (interim fallback to NPM_TOKEN). +- Restores: [ADR-0040](0040-migrate-npm-publication-to-npmjs-com.md) (npmjs.com Trusted Publishing + `specorator` package name). +- Tracking issue: [#411](https://github.com/Luis85/agentic-workflow/issues/411) — npmjs.com Trusted Publisher configuration unblocked. +- npmjs.com Trusted Publishing docs: . +- sigstore provenance attestations: . + +--- + +> **ADR bodies are immutable.** To change a decision, supersede it with a new ADR; only the predecessor's `status` and `superseded-by` pointer fields may be updated. diff --git a/docs/adr/README.md b/docs/adr/README.md index 20997684c..e5e6d6f19 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -53,9 +53,10 @@ Records of architecturally significant decisions. Format follows Michael Nygard' | [0038](0038-adopt-tailwind-v4-with-vite-plugin.md) | Adopt Tailwind v4 with @tailwindcss/vite and bridge brand tokens via @theme inline | Accepted | | [0039](0039-adopt-hybrid-feature-tracker-data-source.md) | Adopt hybrid feature-tracker data source — issues/ folder primary, GitHub-API snapshot fallback | Accepted | | [0040](0040-migrate-npm-publication-to-npmjs-com.md) | Migrate npm publication from GitHub Packages to npmjs.com | Accepted | -| [0041](0041-defer-npmjs-trusted-publishing.md) | Defer npmjs.com Trusted Publishing — fall back to NPM_TOKEN | Accepted | +| [0041](0041-defer-npmjs-trusted-publishing.md) | Defer npmjs.com Trusted Publishing — fall back to NPM_TOKEN | Superseded | | [0042](0042-adopt-typed-artifact-reader-seam.md) | Adopt a typed-artifact reader seam for frontmatter parsing | Accepted | | [0043](0043-distribute-claude-plugin-bundle-from-orphan-dist-branch.md) | Distribute Claude Code plugin bundle from an orphan dist branch via git-subdir | Accepted | +| [0044](0044-restore-npmjs-trusted-publishing.md) | Restore npmjs.com Trusted Publishing — re-enable OIDC + provenance | Accepted | ## ADR Dispositions diff --git a/docs/release-operator-guide.md b/docs/release-operator-guide.md index 5d1f17ff9..d7587f591 100644 --- a/docs/release-operator-guide.md +++ b/docs/release-operator-guide.md @@ -23,7 +23,7 @@ You should already have: - `specs/version-X-Y-plan/release-notes.md` is finalised. 2. The canonical tag `vX.Y.Z` cut on `main` after the merge (never on the release branch). The workflow uses `gh release create … --verify-tag` and refuses to fall back to auto-tagging. 3. Green v0.4 quality signals available to the readiness check, surfaced through the `RELEASE_*` repository variables (or an explicit operator waiver via `RELEASE_QUALITY_WAIVER`). See `scripts/lib/release-readiness.ts` (`QualitySignals`) for the contract. -4. **`NPM_TOKEN` repo secret set** (a classic Automation token on npmjs.com with publish permission for `specorator`). [ADR-0041](adr/0041-defer-npmjs-trusted-publishing.md) defers the ADR-0040 Trusted Publishing path until [#411](https://github.com/Luis85/agentic-workflow/issues/411) closes. Without `NPM_TOKEN`, the `Publish to npmjs.com` step fails closed before reaching `npm publish`. The workflow keeps `id-token: write` so the GitHub Release tarball asset still gets a sigstore attestation via `actions/attest-build-provenance`; npm-side provenance is paused under the fallback. When Trusted Publishing returns, this prereq reverts to the ADR-0040 OIDC happy path. +4. **npmjs.com Trusted Publisher configured** for the `specorator` package against this repository's `release.yml` workflow on the `release` deployment environment ([ADR-0044](adr/0044-restore-npmjs-trusted-publishing.md), restoring ADR-0040 after the ADR-0041 NPM_TOKEN fallback used for v0.7.0 / v0.7.1; tracking [#411](https://github.com/Luis85/agentic-workflow/issues/411) closed). The `id-token: write` workflow permission mints the OIDC token consumed by `npm publish --provenance`; no long-lived `NPM_TOKEN` secret is required. The `actions/attest-build-provenance` step still uses the same OIDC permission to sign the GitHub Release tarball asset. 5. **Repo Settings → General → Releases → "Immutable releases" is DISABLED.** When the setting is on, GitHub auto-flags every new Release immutable. If asset upload then fails — or the operator deletes the Release — the tag is **permanently burned**: the GitHub API returns HTTP 422 `tag_name was used by an immutable release` to every later attempt to host a Release on that tag, including a fresh draft. The v0.5.0 publish dispatch hit exactly this and forced the v0.5.1 recovery release ([#233](https://github.com/Luis85/agentic-workflow/issues/233); incident timeline in `specs/version-0-5-plan/retrospective.md` §Incident). Verify with `gh api repos/{owner}/{repo}/immutable-releases`. Per the GitHub REST contract the endpoint returns HTTP 404 (`Not Found`) when the setting is **disabled** — that is the safe state. HTTP 200 means the setting is enabled; the JSON body's `enforced_by_owner` field tells you whether the toggle came from this repo or an org-level default. Disable before dispatching, or accept the failure mode knowingly. If any of items 1–4 is missing, **stop**. The readiness check fails closed on those (preferred). Item 5 is owned by the operator: the v0.5.0 retrospective showed the setting is not always operator-controlled (org-level defaults can propagate via `enforced_by_owner: true`), so the readiness check cannot always fail closed on it without blocking legitimate dispatches against repos the operator does not own. Verify by hand before every dispatch. @@ -98,7 +98,7 @@ Only after at least one fully green dry run, request a stable publish. - Confirm gate — refuses to continue unless `confirm == version`. - `actions/attest-build-provenance` — emits a GitHub artifact attestation for the workflow-built release tarball. This happens before the Release is created or the npmjs.com publish step runs, and it does not change the npm registry path. - `gh release create vX.Y.Z --target main --verify-tag --generate-notes ${TARBALL}` — creates the GitHub Release with the candidate tarball attached **in one call** when no Release for the tag exists. When a Release already exists (the two-step CLAR-V05-003 path), the workflow runs `gh release edit … --draft= --prerelease=` to flip flags in place and uploads the asset only if it is not already attached, so a single Release per tag is preserved (#233 prevention B + C). The promote branch refuses to demote an already-published stable Release back to draft or prerelease — that flip would unpublish a consumer-visible release; cut a new `vX.Y.(Z+1)` instead. - - `npm publish` — only when `publish_package: true`; authenticates via the `NPM_TOKEN` repo secret (classic Automation token, ADR-0041 fallback while [#411](https://github.com/Luis85/agentic-workflow/issues/411) is open); idempotent (see §7.1). No `--provenance` flag — npm-side provenance is paused under the fallback. The GitHub Release tarball asset still carries a sigstore attestation from `actions/attest-build-provenance` above. + - `npm publish --provenance` — only when `publish_package: true`; authenticates via npmjs.com Trusted Publishing ([ADR-0044](adr/0044-restore-npmjs-trusted-publishing.md), restoring ADR-0040 after the ADR-0041 NPM_TOKEN fallback used for v0.7.0 / v0.7.1; #411 closed); idempotent (see §7.1). The `--provenance` flag mints a sigstore provenance statement that ships with the tarball and is visible on the npmjs.com package page under `Provenance`. The GitHub Release tarball asset also carries its own sigstore attestation from `actions/attest-build-provenance` above. 3. Verify on `https://github.com/Luis85/agentic-workflow/releases/tag/vX.Y.Z`: - Release notes body matches the dry-run preview. @@ -142,11 +142,11 @@ Release provenance has two surfaces, both produced by the release workflow: | Surface | Posture | Mechanism | |---|---|---| | GitHub Release tarball | Required for non-dry-run releases. | `actions/attest-build-provenance` runs against the packed `.tgz` after the fresh-surface check and confirm gate, before the GitHub Release is created. Verify with `gh attestation verify`. | -| npmjs.com package | **Paused under [ADR-0041](adr/0041-defer-npmjs-trusted-publishing.md)** until [#411](https://github.com/Luis85/agentic-workflow/issues/411) closes. | The publish path runs `npm publish` (no `--provenance`) authenticated by `NPM_TOKEN`. No provenance statement is emitted on the npmjs.com package page. When Trusted Publishing is unblocked, this row reverts to `npm publish --provenance` via OIDC and the statement becomes verifiable with `npm view specorator@X.Y.Z --registry https://registry.npmjs.org --json`. | +| npmjs.com package | Required for non-dry-run releases that publish the package ([ADR-0044](adr/0044-restore-npmjs-trusted-publishing.md), restoring ADR-0040 after the ADR-0041 NPM_TOKEN fallback used for v0.7.0 / v0.7.1; #411 closed). | The publish path runs `npm publish --provenance` authenticated via npmjs.com Trusted Publishing (OIDC, no long-lived token). The sigstore provenance statement is visible on the npmjs.com package page under `Provenance`, and verifiable with `npm view specorator@X.Y.Z --registry https://registry.npmjs.org --json` (look at `dist.attestations`). | -The GitHub Release tarball attestation always binds the artifact to this repository's `.github/workflows/release.yml` workflow run, even under the ADR-0041 fallback. Consumers can verify the GitHub Release tarball chain without trusting maintainer signatures. +Both surfaces bind the artifact to this repository's `.github/workflows/release.yml` workflow run via OIDC. Consumers can verify either chain without trusting maintainer signatures. -Trusted publishing on npmjs.com is the documented target (ADR-0040) and intended return path. The intended one-time setup is: Repo `Luis85/agentic-workflow`, Workflow `release.yml` (bare filename — entering `.github/workflows/release.yml` would cause OIDC authentication to fail), Environment none. Configure once via the npmjs.com web UI when the per-package or account-level Trusted Publisher option is reachable for `specorator`; close [#411](https://github.com/Luis85/agentic-workflow/issues/411) and supersede ADR-0041 with an ADR that re-locks OIDC. +The npmjs.com Trusted Publisher configuration is: Repo `Luis85/agentic-workflow`, Workflow `release.yml` (bare filename — entering `.github/workflows/release.yml` would cause OIDC authentication to fail), Environment `release`. The deployment environment on the workflow's `release` job must match the Trusted Publisher's `Environment` field. If the npmjs.com configuration ever drifts (workflow rename, repo rename, environment removed), the publish step fails closed; repair on the npmjs.com side and re-dispatch. ## 5.2 SBOM posture @@ -194,7 +194,7 @@ Symptom: the GitHub Release exists (the tarball may or may not be attached), but Recovery — rerun the release workflow with the same inputs. As of ADR-0040 the workflow's publish step is idempotent (NFR-V05-005): it queries `npm view` first and skips publish when the version already exists. Trusted publishing handles auth automatically. -If the rerun fails again, fall back to a manual publish from a local checkout of `vX.Y.Z`. This requires the `NPM_TOKEN` repo secret (kept as emergency fallback per ADR-0040): +If the rerun fails again, fall back to a manual publish from a local checkout of `vX.Y.Z`. ADR-0044 removed the long-lived `NPM_TOKEN` repo secret, so this path requires minting a fresh classic Automation token on the npmjs.com web UI just for the recovery, and revoking it immediately after the publish completes: ```bash # Run the whole block as a paste-once unit. @@ -206,8 +206,11 @@ If the rerun fails again, fall back to a manual publish from a local checkout of git fetch --tags origin git checkout vX.Y.Z - # 1. Authenticate npm against npmjs.com using the emergency NPM_TOKEN. - export NPM_TOKEN= + # 1. Authenticate npm against npmjs.com using a freshly-minted classic + # Automation token. Generate at npmjs.com → Account → Access Tokens → + # Generate New Token → Classic Token → Automation. Revoke after the + # publish completes. + export NPM_TOKEN= cat > .npmrc <<'EOF' //registry.npmjs.org/:_authToken=${NPM_TOKEN} EOF diff --git a/docs/scripts/lib/release-readiness/variables/REQUIRED_WORKFLOW_PERMISSIONS.md b/docs/scripts/lib/release-readiness/variables/REQUIRED_WORKFLOW_PERMISSIONS.md index b3bd5753c..c802c13cf 100644 --- a/docs/scripts/lib/release-readiness/variables/REQUIRED_WORKFLOW_PERMISSIONS.md +++ b/docs/scripts/lib/release-readiness/variables/REQUIRED_WORKFLOW_PERMISSIONS.md @@ -19,10 +19,8 @@ check applies to both the workflow-level `permissions:` block and any by a job override (Codex round-3 P1 on PR #158). `attestations: write` and `id-token: write` are required so the release workflow can sign and persist a build-provenance attestation for the packed GitHub Release tarball asset -(#387). The same `id-token: write` permission also unlocks npmjs.com Trusted -Publishing (ADR-0040) when that path is enabled; ADR-0041 currently defers -Trusted Publishing in favour of an `NPM_TOKEN`-authenticated `npm publish` -(tracked in #411), but the GitHub Release tarball still gets a sigstore -attestation, so `id-token: write` stays required. `packages: write` is no -longer required because GitHub Packages publication has been removed -(ADR-0040). +(#387). The same `id-token: write` permission also mints the OIDC token +consumed by `npm publish --provenance` for npmjs.com Trusted Publishing +(ADR-0044, restoring ADR-0040 after the ADR-0041 NPM_TOKEN fallback used +for v0.7.0 / v0.7.1; #411 closed). `packages: write` is no longer required +because GitHub Packages publication has been removed (ADR-0040). diff --git a/scripts/lib/release-readiness.ts b/scripts/lib/release-readiness.ts index 909617d5a..5840598ee 100644 --- a/scripts/lib/release-readiness.ts +++ b/scripts/lib/release-readiness.ts @@ -61,13 +61,11 @@ export const EXPECTED_PACKAGE_REPOSITORY = "https://github.com/Luis85/agentic-wo * by a job override (Codex round-3 P1 on PR #158). `attestations: write` and * `id-token: write` are required so the release workflow can sign and persist * a build-provenance attestation for the packed GitHub Release tarball asset - * (#387). The same `id-token: write` permission also unlocks npmjs.com Trusted - * Publishing (ADR-0040) when that path is enabled; ADR-0041 currently defers - * Trusted Publishing in favour of an `NPM_TOKEN`-authenticated `npm publish` - * (tracked in #411), but the GitHub Release tarball still gets a sigstore - * attestation, so `id-token: write` stays required. `packages: write` is no - * longer required because GitHub Packages publication has been removed - * (ADR-0040). + * (#387). The same `id-token: write` permission also mints the OIDC token + * consumed by `npm publish --provenance` for npmjs.com Trusted Publishing + * (ADR-0044, restoring ADR-0040 after the ADR-0041 NPM_TOKEN fallback used + * for v0.7.0 / v0.7.1; #411 closed). `packages: write` is no longer required + * because GitHub Packages publication has been removed (ADR-0040). */ export const REQUIRED_WORKFLOW_PERMISSIONS: Readonly> = { contents: "write", From 5ee0493857eb0a188f7ad8d00b26e7edfe28db07 Mon Sep 17 00:00:00 2001 From: Luis Mendez Date: Sun, 10 May 2026 22:35:01 +0200 Subject: [PATCH 4/9] fix(plugin): require all generated bundle paths when any is present Codex P2 on PR #478: after the conditional-presence gate (ADR-0043), a partial bundle (e.g. someone deletes claude-plugin/specorator/agents/ after running build:claude-plugin) silently passed check:claude-plugin because each helper short-circuited on its own missing-file branch. Replace per-helper guards with an outer all-or-nothing presence check across the five generated artifacts (plugin.json, .mcp.json, agents/, skills/, commands/). When 0 are present, skip cleanly (clean checkout). When all 5 are present, run the full shape checks. When 1..4 are present, fail closed with a "partial bundle detected" diagnostic naming the missing paths. New test exercises the partial-bundle rejection path. Co-Authored-By: Claude Opus 4.7 (1M context) --- scripts/check-claude-plugin.ts | 39 +++++++++++++++++++++-------- tests/scripts/claude-plugin.test.ts | 37 +++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 11 deletions(-) diff --git a/scripts/check-claude-plugin.ts b/scripts/check-claude-plugin.ts index fbd4ef0d0..2780009af 100644 --- a/scripts/check-claude-plugin.ts +++ b/scripts/check-claude-plugin.ts @@ -31,21 +31,40 @@ const errors: string[] = []; checkMarketplace(); checkPluginReadme(); -// Generated-output checks (per ADR-0043) are conditional on the file being -// present. The bundle is gitignored on develop/main and produced by the CI +// Generated-output checks (per ADR-0043) gate as a single all-or-nothing +// group. The bundle is gitignored on develop/main and produced by the CI // publish workflow; a clean checkout validates without a prior local // `npm run build:claude-plugin`. When a contributor has built the bundle -// locally (e.g. for `claude --plugin-dir` smoke-test), the same checks run -// and catch shape regressions. -checkPluginManifest(); -checkPluginMcpJson(); -checkGeneratedDirectories(); +// locally (e.g. for `claude --plugin-dir` smoke-test), the full shape is +// required — partial bundles (e.g. someone deleted `agents/` after a +// build) fail closed instead of silently passing (Codex P2 on PR #478). +const generatedPaths = collectGeneratedPaths(); +const presentCount = generatedPaths.filter((p) => fs.existsSync(p.path)).length; +if (presentCount > 0 && presentCount < generatedPaths.length) { + for (const p of generatedPaths) { + if (!fs.existsSync(p.path)) { + errors.push(`${p.label} missing — partial bundle detected; run npm run build:claude-plugin to refresh, or delete the bundle entirely`); + } + } +} else if (presentCount === generatedPaths.length) { + checkPluginManifest(); + checkPluginMcpJson(); + checkGeneratedDirectories(); +} failIfErrors(errors, "check:claude-plugin"); -function checkPluginManifest(): void { - if (!fs.existsSync(manifestPath)) return; +function collectGeneratedPaths(): Array<{ path: string; label: string }> { + return [ + { path: manifestPath, label: "claude-plugin/specorator/.claude-plugin/plugin.json" }, + { path: path.join(pluginRoot, ".mcp.json"), label: "claude-plugin/specorator/.mcp.json" }, + { path: path.join(pluginRoot, "agents"), label: "claude-plugin/specorator/agents/" }, + { path: path.join(pluginRoot, "skills"), label: "claude-plugin/specorator/skills/" }, + { path: path.join(pluginRoot, "commands"), label: "claude-plugin/specorator/commands/" }, + ]; +} +function checkPluginManifest(): void { const manifest = readJson(manifestPath, "plugin manifest"); if (!manifest) return; @@ -126,7 +145,6 @@ function checkPluginReadme(): void { function checkPluginMcpJson(): void { const mcpPath = path.join(pluginRoot, ".mcp.json"); - if (!fs.existsSync(mcpPath)) return; const parsed = readJson<{ mcpServers?: unknown }>(mcpPath, "plugin .mcp.json"); if (!parsed) return; if (!parsed.mcpServers || typeof parsed.mcpServers !== "object") { @@ -137,7 +155,6 @@ function checkPluginMcpJson(): void { function checkGeneratedDirectories(): void { for (const rel of ["agents", "skills", "commands"]) { const dir = path.join(pluginRoot, rel); - if (!fs.existsSync(dir)) continue; if (!fs.statSync(dir).isDirectory()) { errors.push(`claude-plugin/specorator/${rel} exists but is not a directory`); continue; diff --git a/tests/scripts/claude-plugin.test.ts b/tests/scripts/claude-plugin.test.ts index 998ac0146..faac8dd31 100644 --- a/tests/scripts/claude-plugin.test.ts +++ b/tests/scripts/claude-plugin.test.ts @@ -154,6 +154,43 @@ test("check:claude-plugin passes on a clean checkout with no generated bundle (A } }); +test("check:claude-plugin rejects a partial bundle (ADR-0043)", () => { + const root = makeFixtureRoot(); + try { + write(root, ".claude-plugin/marketplace.json", JSON.stringify(marketplaceFixture())); + write( + root, + "claude-plugin/specorator/README.md", + ["---", 'title: "Plugin"', 'folder: "claude-plugin/specorator"', 'description: "Plugin package."', "entry_point: true", "---", "", "# Plugin"].join("\n"), + ); + // Three of five generated artifacts present, two missing — a partial + // bundle that the conditional-presence gate (Codex P2 on PR #478) must + // reject rather than silently pass. + write( + root, + "claude-plugin/specorator/.claude-plugin/plugin.json", + JSON.stringify({ + name: "specorator", + version: "0.6.0", + description: "Spec-driven workflow.", + repository: "https://github.com/Luis85/agentic-workflow", + license: "MIT", + }), + ); + write(root, "claude-plugin/specorator/.mcp.json", mcpFixture); + write(root, "claude-plugin/specorator/agents/dev.md", "# Dev\n"); + // skills/ and commands/ deliberately omitted. + + const result = runScript(checkScript, root); + expect(result.status).toBe(1); + expect(result.stderr).toContain("partial bundle detected"); + expect(result.stderr).toContain("skills"); + expect(result.stderr).toContain("commands"); + } finally { + fs.rmSync(root, { recursive: true, force: true }); + } +}); + test("check:claude-plugin rejects a marketplace source that is not git-subdir (ADR-0043)", () => { const root = makeFixtureRoot(); try { From 39668f65c97641d43235d0e3b4859e7093efe0b0 Mon Sep 17 00:00:00 2001 From: Luis Mendez Date: Sun, 10 May 2026 22:40:44 +0200 Subject: [PATCH 5/9] fix(plugin): re-enable bundle drift verification when bundle is present MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex P2 on PR #478: dropping `build:claude-plugin --check` from the package.json `check:claude-plugin` script (per ADR-0043) was correct for the verify gate on a clean develop/main checkout (no committed bundle to drift against), but it removed the contributor-facing drift detection too — a hand-edited or stale locally-built bundle silently passes validation. Restore drift detection inside `check-claude-plugin.ts`, gated on the all-or-nothing bundle-present branch from the prior commit. When all five generated paths are present, spawn `build-claude-plugin --check` and propagate its diagnostics. When the bundle is absent (the typical develop/main case), the drift step is skipped — the verify-gate behavior promised by ADR-0043 is preserved. Resolve the build script via this file's on-disk location rather than repoRoot so the test harness's SPECORATOR_ROOT redirection still finds the real script next to it. New test exercises the drift rejection path; the existing "validates manifest, marketplace, and generated directories" test now invokes build-claude-plugin from a seeded fixture before check, which is a more realistic shape for a successful run. Co-Authored-By: Claude Opus 4.7 (1M context) --- scripts/check-claude-plugin.ts | 41 +++++++++++++++++++ tests/scripts/claude-plugin.test.ts | 63 +++++++++++++++++++---------- 2 files changed, 82 insertions(+), 22 deletions(-) diff --git a/scripts/check-claude-plugin.ts b/scripts/check-claude-plugin.ts index 2780009af..a388740e3 100644 --- a/scripts/check-claude-plugin.ts +++ b/scripts/check-claude-plugin.ts @@ -1,7 +1,11 @@ import fs from "node:fs"; import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { spawnSync } from "node:child_process"; import { failIfErrors, parseSimpleYaml, readText, repoRoot } from "./lib/repo.js"; +const scriptsDir = path.dirname(fileURLToPath(import.meta.url)); + type PluginManifest = { name?: unknown; version?: unknown; @@ -50,10 +54,47 @@ if (presentCount > 0 && presentCount < generatedPaths.length) { checkPluginManifest(); checkPluginMcpJson(); checkGeneratedDirectories(); + // Bundle is fully built — also verify it has not drifted from canonical + // .claude/{agents,skills,commands}/ + .mcp.json sources. Per ADR-0043 the + // verify gate does not require a built bundle, so the drift check no + // longer runs unconditionally from the package.json `check:claude-plugin` + // script. But when a contributor HAS built the bundle locally (e.g. for + // `claude --plugin-dir` smoke-test), shape-only validation lets stale or + // hand-edited bundles pass even though they will diverge from what the CI + // publish workflow rebuilds (Codex P2 on PR #478). Run the byte-level + // comparison here only when the full bundle is present. + checkBundleDrift(); } failIfErrors(errors, "check:claude-plugin"); +function checkBundleDrift(): void { + // Resolve the build script via the same `repoRoot` the structural checks + // use (honours `SPECORATOR_ROOT` for the test harness). Leave `cwd` + // unset so node + tsx resolve against the parent process's cwd — that + // is the actual install root in both production and the test harness, + // which is where `node_modules/tsx` lives. `SPECORATOR_ROOT` propagates + // through `env` so the spawned build-claude-plugin reads sources from + // the fixture rather than the real repo. + // Resolve the build script via this file's on-disk location, not via + // `repoRoot`. `repoRoot` honours `SPECORATOR_ROOT` and points at the + // test fixture; the actual `build-claude-plugin.ts` always sits next to + // this file in the real install. + const buildScript = path.join(scriptsDir, "build-claude-plugin.ts"); + const result = spawnSync(process.execPath, ["--import", "tsx", buildScript, "--check"], { + encoding: "utf8", + env: process.env, + windowsHide: true, + }); + if (result.status === 0) return; + const message = (result.stderr || result.stdout || "").trim(); + if (message) { + errors.push(`claude-plugin/specorator/ has drifted from canonical .claude sources; run npm run build:claude-plugin to refresh:\n${message}`); + } else { + errors.push("claude-plugin/specorator/ has drifted from canonical .claude sources; run npm run build:claude-plugin to refresh"); + } +} + function collectGeneratedPaths(): Array<{ path: string; label: string }> { return [ { path: manifestPath, label: "claude-plugin/specorator/.claude-plugin/plugin.json" }, diff --git a/tests/scripts/claude-plugin.test.ts b/tests/scripts/claude-plugin.test.ts index faac8dd31..c6942deba 100644 --- a/tests/scripts/claude-plugin.test.ts +++ b/tests/scripts/claude-plugin.test.ts @@ -106,36 +106,36 @@ test("build:claude-plugin --check reports plugin manifest drift when version is test("check:claude-plugin validates manifest, marketplace, and generated directories", () => { const root = makeFixtureRoot(); try { - write(root, ".claude-plugin/marketplace.json", JSON.stringify(marketplaceFixture())); - write( - root, - "claude-plugin/specorator/.claude-plugin/plugin.json", - JSON.stringify({ - name: "specorator", - version: "0.6.0", - description: "Spec-driven workflow.", - repository: "https://github.com/Luis85/agentic-workflow", - license: "MIT", - }), - ); - write( - root, - "claude-plugin/specorator/README.md", - ["---", 'title: "Plugin"', 'folder: "claude-plugin/specorator"', 'description: "Plugin package."', "entry_point: true", "---", "", "# Plugin"].join("\n"), - ); - write(root, "claude-plugin/specorator/agents/dev.md", "# Dev\n"); - write(root, "claude-plugin/specorator/skills/verify/SKILL.md", "# Verify\n"); - write(root, "claude-plugin/specorator/commands/spec/start.md", "# Start\n"); - write(root, "claude-plugin/specorator/.mcp.json", mcpFixture); + seedCheckFixture(root); + expect(runScript(buildScript, root).status).toBe(0); const result = runScript(checkScript, root); - expect(result.status).toBe(0); + expect(result.status, `stdout:\n${result.stdout}\nstderr:\n${result.stderr}`).toBe(0); expect(result.stdout).toContain("check:claude-plugin: ok"); } finally { fs.rmSync(root, { recursive: true, force: true }); } }); +test("check:claude-plugin rejects a drifted bundle (Codex P2 on PR #478)", () => { + const root = makeFixtureRoot(); + try { + seedCheckFixture(root); + expect(runScript(buildScript, root).status).toBe(0); + // Hand-edit one generated file so the bundle drifts from canonical + // sources. The all-or-nothing presence gate accepts this (all 5 + // generated paths still present), but the byte-level drift check + // re-runs build:claude-plugin --check and surfaces the divergence. + write(root, "claude-plugin/specorator/agents/dev.md", "# Dev (hand-edited)\n"); + + const result = runScript(checkScript, root); + expect(result.status).toBe(1); + expect(result.stderr).toContain("drifted from canonical .claude sources"); + } finally { + fs.rmSync(root, { recursive: true, force: true }); + } +}); + test("check:claude-plugin passes on a clean checkout with no generated bundle (ADR-0043)", () => { const root = makeFixtureRoot(); try { @@ -216,6 +216,25 @@ test("check:claude-plugin rejects a marketplace source that is not git-subdir (A } }); +function seedCheckFixture(root: string): void { + // Canonical sources — what build:claude-plugin reads. + write(root, "package.json", JSON.stringify({ name: "fixture", version: "9.9.9" })); + write(root, ".claude/agents/dev.md", "# Dev\n"); + write(root, ".claude/skills/verify/SKILL.md", "# Verify\n"); + write(root, ".claude/commands/spec/start.md", "# Start\n"); + write(root, ".mcp.json", mcpFixture); + // Plugin-shape committed files that build:claude-plugin does NOT touch. + write(root, ".claude-plugin/marketplace.json", JSON.stringify(marketplaceFixture())); + write( + root, + "claude-plugin/specorator/README.md", + ["---", 'title: "Plugin"', 'folder: "claude-plugin/specorator"', 'description: "Plugin package."', "entry_point: true", "---", "", "# Plugin"].join("\n"), + ); + // Placeholder plugin.json so build:claude-plugin's compareManifest sees the + // generated tree's parent dir already exists. + write(root, "claude-plugin/specorator/.claude-plugin/plugin.json", "{}\n"); +} + function marketplaceFixture() { return { name: "specorator-marketplace", From f1eefbf81158cb2b455a799fbd2043ff3a25c63c Mon Sep 17 00:00:00 2001 From: Luis Mendez Date: Sun, 10 May 2026 23:03:17 +0200 Subject: [PATCH 6/9] chore(release): bump 0.8.0 -> 0.8.0-rc.1 (TP smoke test) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Release-candidate dispatch slot for the v0.8.0 cycle. Smoke-tests the restored npmjs.com Trusted Publishing path (ADR-0044, supersedes ADR-0041) before the v0.8.0 final dispatch — exercises the OIDC + --provenance shape end-to-end on a low-stakes version. Surface content unchanged from v0.8.0: - README badge + status banner reframed as RC; references ADR-0044 and acknowledges v0.7.x shipped via the ADR-0041 NPM_TOKEN fallback. - docs/specorator.md version line updated; v0.8.x publish-via-OIDC narrative replaces the earlier "deferred per ADR-0041" line. - CHANGELOG adds a minimal [v0.8.0-rc.1] section above the planned [v0.8.0] entry. Post-RC: bump back to 0.8.0 in a follow-up PR, tag, dispatch v0.8.0 final. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 6 ++++++ README.md | 4 ++-- docs/specorator.md | 2 +- package.json | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4afd22cf..8320dbf8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ All notable changes to Specorator are documented here. Format follows [Keep a Ch --- +## [v0.8.0-rc.1] — 2026-05-10 + +Release candidate for the v0.8.0 cycle. Smoke-tests npmjs.com Trusted Publishing on the `specorator` package after [ADR-0044](docs/adr/0044-restore-npmjs-trusted-publishing.md) restored the OIDC + `--provenance` path (supersedes ADR-0041). The first successful RC dispatch confirms `release.yml` mints an OIDC token, `npmjs.com` accepts the publish, and the package page surfaces a sigstore provenance attestation. Surface content is identical to the v0.8.0 final entry below. + +--- + ## [v0.8.0] — 2026-05-10 ### Changed diff --git a/README.md b/README.md index 58b11b1a8..f9a49ff29 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # Specorator — Agentic Development Workflow -![Version](https://img.shields.io/badge/version-v0.8.0-blue) ![License](https://img.shields.io/badge/license-MIT-green) +![Version](https://img.shields.io/badge/version-v0.8.0-rc.1-blue) ![License](https://img.shields.io/badge/license-MIT-green) [![Verify](https://github.com/Luis85/agentic-workflow/actions/workflows/verify.yml/badge.svg?branch=main)](https://github.com/Luis85/agentic-workflow/actions/workflows/verify.yml) [![gitleaks](https://github.com/Luis85/agentic-workflow/actions/workflows/gitleaks.yml/badge.svg?branch=main)](https://github.com/Luis85/agentic-workflow/actions/workflows/gitleaks.yml) [![typos](https://github.com/Luis85/agentic-workflow/actions/workflows/typos.yml/badge.svg?branch=main)](https://github.com/Luis85/agentic-workflow/actions/workflows/typos.yml) [![zizmor](https://github.com/Luis85/agentic-workflow/actions/workflows/zizmor.yml/badge.svg?branch=main)](https://github.com/Luis85/agentic-workflow/actions/workflows/zizmor.yml) **Build software the right way with AI.** Specorator is a spec-driven workflow template: humans decide what to build, specialist agents handle how, and every requirement, decision, task, test, and release note stays traceable. -> **Status:** v0.8.0 — Claude Code plugin bundle moves to a long-lived orphan branch (`dist/claude-plugin`) rebuilt by CI on every push to `main`; the marketplace entry now uses a `git-subdir` source pinned to that ref ([ADR-0043](docs/adr/0043-distribute-claude-plugin-bundle-from-orphan-dist-branch.md)). Bundle is gitignored on `develop`/`main`, so PR diffs stop carrying generated-artifact churn. New `/issue:tackle` conductor skill, `/specorator:onboard` guided issue series, GitHub remote MCP server in the project default, conductor-driven model-tier injection for subagents, and `specorator --version` flag. v0.7.0 migrated the npm CLI from GitHub Packages to npmjs.com as `specorator` (unscoped, public, no `.npmrc`). Trusted publishing via OIDC is deferred per [ADR-0041](docs/adr/0041-defer-npmjs-trusted-publishing.md) — v0.7.x and v0.8.x publish via classic `NPM_TOKEN`. Claude Code is first-class; Codex, Cursor, Aider, Copilot, and Gemini have Markdown-based walkthroughs. +> **Status:** v0.8.0-rc.1 — release-candidate dispatch for the v0.8.0 cycle. Smoke-tests the restored npmjs.com Trusted Publishing path ([ADR-0044](docs/adr/0044-restore-npmjs-trusted-publishing.md), supersedes ADR-0041) before the v0.8.0 final dispatch; surface content matches v0.8.0 (orphan `dist/claude-plugin` branch + `git-subdir` marketplace source per [ADR-0043](docs/adr/0043-distribute-claude-plugin-bundle-from-orphan-dist-branch.md), `/issue:tackle`, `/specorator:onboard`, GitHub remote MCP server in the project default, conductor-driven model-tier injection, `specorator --version` flag). v0.7.0 migrated the npm CLI from GitHub Packages to npmjs.com as `specorator` (unscoped, public, no `.npmrc`); v0.7.x shipped via the ADR-0041 NPM_TOKEN fallback. v0.8.x publishes via OIDC + `npm publish --provenance`. Claude Code is first-class; Codex, Cursor, Aider, Copilot, and Gemini have Markdown-based walkthroughs. Product page: diff --git a/docs/specorator.md b/docs/specorator.md index aabf0e0e9..1975531ed 100644 --- a/docs/specorator.md +++ b/docs/specorator.md @@ -1,6 +1,6 @@ # Specorator — Quality-Driven, Agentic Development Workflow -**Version:** 0.8.0 · **Status:** Minor release — Claude Code plugin bundle moves to orphan dist branch + `git-subdir` marketplace source (ADR-0043); plus `/issue:tackle`, `/specorator:onboard`, GitHub MCP, conductor-driven model tiers, `--version` flag · **Purpose:** Spec-driven, agentic workflow template +**Version:** 0.8.0-rc.1 · **Status:** Release candidate — smoke-tests npmjs.com Trusted Publishing (ADR-0044) ahead of the v0.8.0 final dispatch. Surface content matches v0.8.0: orphan-dist plugin distribution (ADR-0043), `/issue:tackle`, `/specorator:onboard`, GitHub MCP, conductor-driven model tiers, `--version` flag · **Purpose:** Spec-driven, agentic workflow template v0.8.0 moves the Claude Code plugin bundle to a long-lived orphan branch `dist/claude-plugin` rebuilt by CI on every push to `main`. The marketplace entry in `.claude-plugin/marketplace.json` switches to a `git-subdir` source pinned to that ref, so the bundle becomes gitignored on `develop`/`main` and PR diffs stop carrying generated-artifact churn. See [ADR-0043](adr/0043-distribute-claude-plugin-bundle-from-orphan-dist-branch.md). v0.8.0 also adds the `/issue:tackle` conductor skill (triage-first issue/PR resolution), `/specorator:onboard` (guided 5-step onboarding series), the GitHub remote MCP server in the project `.mcp.json` default, conductor-driven model-tier injection for subagents, and `specorator --version` / `-v`. v0.7.0 migrated the Specorator npm CLI from GitHub Packages (`@luis85/agentic-workflow`) to npmjs.com as `specorator` (unscoped, public). See [ADR-0040](adr/0040-migrate-npm-publication-to-npmjs-com.md). v0.6.2 was a patch release unblocking the v0.6.x dispatch path. v0.6.1 shipped Specorator as a Claude Code plugin. v0.6.0 introduced the Astro 6 product page. v0.5.0 introduced the release workflow, GitHub Release / Package distribution, and fresh-surface package contract; v0.5.1 was the recovery release for the [Immutable Releases incident](https://github.com/Luis85/agentic-workflow/issues/233). diff --git a/package.json b/package.json index bb20693cf..44dae8534 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "specorator", - "version": "0.8.0", + "version": "0.8.0-rc.1", "description": "Specorator — template for spec-driven, agentic software development. The workflow is the deliverable.", "keywords": [ "agents", From 0ee1f94b615837adcb69860215b2e02d4e89273d Mon Sep 17 00:00:00 2001 From: Luis Mendez Date: Sun, 10 May 2026 23:30:45 +0200 Subject: [PATCH 7/9] fix(release): drop smoke job permissions override; inherit top-level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The v0.8.0-rc.1 dispatch (run 25639883562) failed Layer 1 readiness with RELEASE_READINESS_WORKFLOW_PERMISSIONS — `jobs.smoke.permissions.contents` was `read` but the readiness check enforces strict equality with the top-level `contents: write` value (scripts/lib/release-readiness.ts diagnosticsForPermissions, line ~852). Drop the job-level `permissions:` block so smoke inherits the top-level { contents: write, attestations: write, id-token: write } block. The reusable smoke-test workflow is read-only in practice (npm pack + install + CLI smoke); the inherited write scopes are unused. Comment block in release.yml header already says "job-level overrides may only narrow, never widen" — the actual readiness check enforces equality, not subset. The narrowing-allowed semantics is documented as intent (line ~775) but the implementation is exact-match. Either the comment is wrong or the check is too strict; aligning the workflow with the implementation unblocks v0.8.0-rc.1 / v0.8.0 dispatch immediately. A follow-up may relax the readiness check. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/release.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f9b5edb9b..9659cacd2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -74,8 +74,15 @@ jobs: smoke: name: Smoke test (release gate) uses: ./.github/workflows/smoke-test.yml - permissions: - contents: read + # No job-level `permissions:` block — `scripts/lib/release-readiness.ts` + # `diagnosticsForPermissions` enforces strict equality between job-level + # and top-level permission values (line ~852: "is `` but must be + # ``"). A `contents: read` override here failed Layer 1 + # readiness on the v0.8.0-rc.1 dispatch (run 25639883562). The smoke job + # therefore inherits the top-level `{ contents: write, attestations: + # write, id-token: write }` block. The reusable smoke-test workflow is + # read-only in practice (npm pack + install + CLI smoke); the inherited + # write scopes are unused. release: name: Manual GitHub Release From 44707251c901afec4021090c70b7affdc526ea49 Mon Sep 17 00:00:00 2001 From: Luis Mendez Date: Sun, 10 May 2026 23:40:07 +0200 Subject: [PATCH 8/9] fix(release): pass --tag next when publishing a pre-release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The v0.8.0-rc.1 dispatch (run 25640431367, second attempt) failed at the Publish step with: npm error You must specify a tag using --tag when publishing a prerelease version. `npm publish` refuses to default a semver prerelease (`-rc.1`) to the `latest` dist-tag because that would silently demote stable consumers who pin `latest`. Without `--tag`, prereleases fail closed. Parameterise the publish step on `inputs.prerelease`: - prerelease=true → `npm publish --provenance --tag next ` - prerelease=false → `npm publish --provenance ` (defaults to `latest`) This is workflow plumbing — every other step (Layer 1+2 readiness, build:claude-plugin, tarball provenance attestation, Release create with asset upload) succeeded on the previous dispatch, so the Trusted-Publishing config on npmjs.com is reachable end-to-end. This fix unblocks the last gate before the OIDC publish actually runs. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/release.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9659cacd2..84d12245d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -386,6 +386,7 @@ jobs: if: ${{ ! inputs.dry_run && inputs.publish_package }} env: INPUT_VERSION: ${{ inputs.version }} + INPUT_PRERELEASE: ${{ inputs.prerelease }} TARBALL: ${{ steps.pack.outputs.tarball }} run: | actual="$(node -p "require('./package.json').name + '@' + require('./package.json').version")" @@ -394,6 +395,15 @@ jobs: echo "::error::package.json identity (${actual}) does not match expected (${expected}) — refusing to publish (ADR-0040)" >&2 exit 1 fi + # Pre-release versions must publish under a non-`latest` dist-tag. + # `npm publish` refuses to default a prerelease to `latest` and + # exits with "You must specify a tag using --tag when publishing + # a prerelease version." `inputs.prerelease == true` → publish + # under `next`; stable releases → default `latest` (no `--tag`). + publish_args=("--provenance") + if [ "${INPUT_PRERELEASE}" = "true" ]; then + publish_args+=("--tag" "next") + fi set +e view_output="$(npm view "specorator@${INPUT_VERSION}" version --json 2>&1)" view_exit=$? @@ -405,7 +415,7 @@ jobs: # published archive equals the GitHub Release asset uploaded in # step 11 (T-V05-013). `--provenance` mints a sigstore provenance # statement via the OIDC token (ADR-0044, restoring ADR-0040). - npm publish --provenance "${TARBALL}" + npm publish "${publish_args[@]}" "${TARBALL}" else echo "::error::npm view failed with a non-404 error — refusing to publish so EPUBLISHCONFLICT cannot mask a real failure" >&2 echo "$view_output" >&2 From c3700a303bf4b9374248bb2dc88f47f1f82ba058 Mon Sep 17 00:00:00 2001 From: Luis Mendez Date: Mon, 11 May 2026 00:13:27 +0200 Subject: [PATCH 9/9] chore(release): bump 0.8.0-rc.1 -> 0.8.0 (TP validated) v0.8.0-rc.1 dispatch (run 25640962728) succeeded end-to-end on the fourth attempt after the npmjs.com Trusted Publisher record was completed (Environment field was empty; now set to `release` to match release.yml's environment.name). The RC published to npmjs.com under the `next` dist-tag with a sigstore provenance attestation (https://registry.npmjs.org/-/npm/v1/attestations/specorator@0.8.0-rc.1). Cut the v0.8.0 final: - package.json: 0.8.0-rc.1 -> 0.8.0 - README badge + status banner: restore v0.8.0 framing, mention TP restoration alongside ADR-0043 plugin distribution as headlines. - docs/specorator.md version line: restore v0.8.0 framing. - CHANGELOG: both [v0.8.0-rc.1] (RC validation record) and [v0.8.0] (release content) entries retained. Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 4 ++-- docs/specorator.md | 2 +- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f9a49ff29..1615f481a 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # Specorator — Agentic Development Workflow -![Version](https://img.shields.io/badge/version-v0.8.0-rc.1-blue) ![License](https://img.shields.io/badge/license-MIT-green) +![Version](https://img.shields.io/badge/version-v0.8.0-blue) ![License](https://img.shields.io/badge/license-MIT-green) [![Verify](https://github.com/Luis85/agentic-workflow/actions/workflows/verify.yml/badge.svg?branch=main)](https://github.com/Luis85/agentic-workflow/actions/workflows/verify.yml) [![gitleaks](https://github.com/Luis85/agentic-workflow/actions/workflows/gitleaks.yml/badge.svg?branch=main)](https://github.com/Luis85/agentic-workflow/actions/workflows/gitleaks.yml) [![typos](https://github.com/Luis85/agentic-workflow/actions/workflows/typos.yml/badge.svg?branch=main)](https://github.com/Luis85/agentic-workflow/actions/workflows/typos.yml) [![zizmor](https://github.com/Luis85/agentic-workflow/actions/workflows/zizmor.yml/badge.svg?branch=main)](https://github.com/Luis85/agentic-workflow/actions/workflows/zizmor.yml) **Build software the right way with AI.** Specorator is a spec-driven workflow template: humans decide what to build, specialist agents handle how, and every requirement, decision, task, test, and release note stays traceable. -> **Status:** v0.8.0-rc.1 — release-candidate dispatch for the v0.8.0 cycle. Smoke-tests the restored npmjs.com Trusted Publishing path ([ADR-0044](docs/adr/0044-restore-npmjs-trusted-publishing.md), supersedes ADR-0041) before the v0.8.0 final dispatch; surface content matches v0.8.0 (orphan `dist/claude-plugin` branch + `git-subdir` marketplace source per [ADR-0043](docs/adr/0043-distribute-claude-plugin-bundle-from-orphan-dist-branch.md), `/issue:tackle`, `/specorator:onboard`, GitHub remote MCP server in the project default, conductor-driven model-tier injection, `specorator --version` flag). v0.7.0 migrated the npm CLI from GitHub Packages to npmjs.com as `specorator` (unscoped, public, no `.npmrc`); v0.7.x shipped via the ADR-0041 NPM_TOKEN fallback. v0.8.x publishes via OIDC + `npm publish --provenance`. Claude Code is first-class; Codex, Cursor, Aider, Copilot, and Gemini have Markdown-based walkthroughs. +> **Status:** v0.8.0 — Claude Code plugin bundle moves to a long-lived orphan branch (`dist/claude-plugin`) rebuilt by CI on every push to `main`; the marketplace entry now uses a `git-subdir` source pinned to that ref ([ADR-0043](docs/adr/0043-distribute-claude-plugin-bundle-from-orphan-dist-branch.md)). Bundle is gitignored on `develop`/`main`, so PR diffs stop carrying generated-artifact churn. New `/issue:tackle` conductor skill, `/specorator:onboard` guided issue series, GitHub remote MCP server in the project default, conductor-driven model-tier injection for subagents, and `specorator --version` flag. npmjs.com Trusted Publishing restored ([ADR-0044](docs/adr/0044-restore-npmjs-trusted-publishing.md), supersedes ADR-0041); every v0.8.x release ships with a sigstore provenance attestation visible on the npmjs.com package page. v0.7.0 migrated the npm CLI from GitHub Packages to npmjs.com as `specorator` (unscoped, public, no `.npmrc`). Claude Code is first-class; Codex, Cursor, Aider, Copilot, and Gemini have Markdown-based walkthroughs. Product page: diff --git a/docs/specorator.md b/docs/specorator.md index 1975531ed..6880e3066 100644 --- a/docs/specorator.md +++ b/docs/specorator.md @@ -1,6 +1,6 @@ # Specorator — Quality-Driven, Agentic Development Workflow -**Version:** 0.8.0-rc.1 · **Status:** Release candidate — smoke-tests npmjs.com Trusted Publishing (ADR-0044) ahead of the v0.8.0 final dispatch. Surface content matches v0.8.0: orphan-dist plugin distribution (ADR-0043), `/issue:tackle`, `/specorator:onboard`, GitHub MCP, conductor-driven model tiers, `--version` flag · **Purpose:** Spec-driven, agentic workflow template +**Version:** 0.8.0 · **Status:** Minor release — Claude Code plugin bundle moves to orphan dist branch + `git-subdir` marketplace source (ADR-0043); npmjs.com Trusted Publishing restored with sigstore provenance (ADR-0044, supersedes ADR-0041); plus `/issue:tackle`, `/specorator:onboard`, GitHub MCP, conductor-driven model tiers, `--version` flag · **Purpose:** Spec-driven, agentic workflow template v0.8.0 moves the Claude Code plugin bundle to a long-lived orphan branch `dist/claude-plugin` rebuilt by CI on every push to `main`. The marketplace entry in `.claude-plugin/marketplace.json` switches to a `git-subdir` source pinned to that ref, so the bundle becomes gitignored on `develop`/`main` and PR diffs stop carrying generated-artifact churn. See [ADR-0043](adr/0043-distribute-claude-plugin-bundle-from-orphan-dist-branch.md). v0.8.0 also adds the `/issue:tackle` conductor skill (triage-first issue/PR resolution), `/specorator:onboard` (guided 5-step onboarding series), the GitHub remote MCP server in the project `.mcp.json` default, conductor-driven model-tier injection for subagents, and `specorator --version` / `-v`. v0.7.0 migrated the Specorator npm CLI from GitHub Packages (`@luis85/agentic-workflow`) to npmjs.com as `specorator` (unscoped, public). See [ADR-0040](adr/0040-migrate-npm-publication-to-npmjs-com.md). v0.6.2 was a patch release unblocking the v0.6.x dispatch path. v0.6.1 shipped Specorator as a Claude Code plugin. v0.6.0 introduced the Astro 6 product page. v0.5.0 introduced the release workflow, GitHub Release / Package distribution, and fresh-surface package contract; v0.5.1 was the recovery release for the [Immutable Releases incident](https://github.com/Luis85/agentic-workflow/issues/233). diff --git a/package.json b/package.json index 44dae8534..bb20693cf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "specorator", - "version": "0.8.0-rc.1", + "version": "0.8.0", "description": "Specorator — template for spec-driven, agentic software development. The workflow is the deliverable.", "keywords": [ "agents",