Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/remove-release-audit-gate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"agent-bundle": patch
---

Remove the release audit gate: the `audit:release` script that ran `scripts/audit-packed-release.mjs` (external consumer install, npm advisory and signature checks, CycloneDX SBOM, LICENSE/NOTICE tarball checks) is replaced by `lint:release`, which runs publint and attw only, and the packaged README no longer states that the release gate fails on missing license files (#487)
28 changes: 5 additions & 23 deletions docs/local-ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,29 +189,11 @@ attempt, so one edit is exactly one build.

## Infrastructure failures and their retry policy

Two failure shapes in the hosted Release gates are registry or runner
infrastructure, not the tree under test. Neither gets a code-level retry,
and neither is a reason to weaken the gate; the policy is to re-run the job
once the cause has cleared, then treat a repeat as a real signal.

- **`npm audit signatures` → `EATTESTATIONVERIFY`** (from
`scripts/audit-packed-release.mjs`, reached through `pnpm audit:release`).
Example (CI run 33584654855, 2026-09-02, Release gates on Node 22.19):
`@modelcontextprotocol/server@2.0.0 failed to verify attestation:
Unexpected end of JSON input`. npm fetched a truncated attestation bundle
from `registry.npmjs.org` for a dependency this repository does not
publish; the same pinned version and integrity verify on every later run
of the same gate without any lockfile change. The audit deliberately
installs against live registry metadata (no `--prefer-offline`), so a
registry-side transient reaches it unfiltered. Policy: read the JSON in the
step log first; if the `invalid` entry names a third-party package with an
unchanged pinned version and a parse-shaped message (`Unexpected end of
JSON input`, `Unexpected token`, a 5xx), re-run the failed job (`gh run
rerun <id> --failed`). If the same package fails twice in a row, or the
message is a genuine signature mismatch (`EATTESTATIONSIGNATURE`,
`EINTEGRITY`), stop and investigate the dependency before merging: that is
the supply-chain check doing its job. Do not add retries around the audit
command and do not relax `--json` parsing to tolerate the error.
One failure shape in the hosted Release gates is registry or runner
infrastructure, not the tree under test. It gets no code-level retry, and it
is not a reason to weaken the gate; the policy is to re-run the job once the
cause has cleared, then treat a repeat as a real signal.

- **Runner network stalls during `npm install`** in the packed pool. The
pool's consumer installs are cache-backed per worker
(`rstest.worker-isolation.ts`): each worker pays for one cold download of
Expand Down
47 changes: 12 additions & 35 deletions docs/superpowers/plans/2026-08-24-public-examples-pnpm-workspace.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,7 @@
- `.github/workflows/ci.yml`: Corepack/frozen pnpm install, product gates, and example gate.
- `.github/workflows/package-preview.yml`: pnpm build with pkg.pr.new publishing only the product package.
- `.github/workflows/native-host-smoke.yml`: pnpm setup and unchanged opt-in native-host commands.
- `scripts/audit-packed-release.mjs`: create one external npm consumer, install the packed tarball, then run npm dependency, audit, signature, and CycloneDX checks.
- `scripts/audit-packed-sbom.mjs`: removed after its behavior is folded into `audit-packed-release.mjs`.
- `packages/agent-bundle/tests/release-audit.test.ts`: prove the package tarball installs outside the workspace and excludes examples/workspace dependencies.
- packed consumer tests: prove the package tarball installs outside the workspace and excludes examples/workspace dependencies.
- `packages/agent-bundle/tests/workspace-contract.test.ts`: prove pnpm selects both product packages and all private examples.

### Public examples
Expand All @@ -64,20 +62,16 @@
**Files:**
- Create: `pnpm-workspace.yaml`
- Create: `pnpm-lock.yaml`
- Create: `scripts/audit-packed-release.mjs`
- Create: `packages/agent-bundle/tests/workspace-contract.test.ts`
- Modify: `package.json`
- Modify: `.github/workflows/ci.yml`
- Modify: `.github/workflows/package-preview.yml`
- Modify: `.github/workflows/native-host-smoke.yml`
- Modify: `packages/agent-bundle/tests/release-audit.test.ts`
- Delete: `package-lock.json`
- Delete: `scripts/audit-packed-sbom.mjs`

**Interfaces:**
- Consumes: current root npm scripts, existing package workspaces, and the installed-tarball assertions in `release-audit.test.ts`.
- Consumes: current root npm scripts, existing package workspaces, and the installed-tarball assertions in the packed consumer tests.
- Produces: canonical root commands `pnpm build`, `pnpm test`, `pnpm check`, `pnpm check:release`, and a workspace containing `packages/*` plus future `examples/*`.
- Produces: `scripts/audit-packed-release.mjs` that exits nonzero if any external npm production check fails and prints the validated CycloneDX document as JSON on stdout.

- [ ] **Step 1: Add the failing workspace membership test**

Expand Down Expand Up @@ -132,40 +126,23 @@ In `package.json`, remove `workspaces`, add `"packageManager": "pnpm@11.23.0"`,
"test:packed:native:claude": "pnpm build && AGENT_BUNDLE_PACKED_NATIVE_CLAUDE_SMOKE=1 pnpm test:packed:native",
"test:packed:native:codex": "pnpm build && AGENT_BUNDLE_PACKED_NATIVE_CODEX_SMOKE=1 pnpm test:packed:native",
"pack:dry-run": "pnpm build && npm pack ./packages/agent-bundle --dry-run --json",
"audit:release": "pnpm lint:package && attw --pack --profile esm-only packages/agent-bundle && node scripts/audit-packed-release.mjs",
"check:release": "pnpm pack:dry-run && pnpm audit:release && pnpm test:packed"
"lint:release": "pnpm lint:package && attw --pack --profile esm-only packages/agent-bundle",
"check:release": "pnpm pack:dry-run && pnpm lint:release && pnpm test:packed"
}
}
```

- [ ] **Step 4: Move every npm production check into one external consumer**
- [ ] **Step 4: Tighten the tarball regression**

Implement `scripts/audit-packed-release.mjs` by retaining the current temporary-directory, pack, install, and SBOM validation logic from `audit-packed-sbom.mjs`, then run these commands with `cwd` set to the temporary consumer:

```js
await execFile('npm', ['ls', '--omit=dev', '--json'], { cwd: consumerRoot });
await execFile('npm', ['audit', '--omit=dev', '--json'], { cwd: consumerRoot });
await execFile('npm', ['audit', 'signatures', '--json'], { cwd: consumerRoot });
const { stdout } = await execFile(
'npm',
['sbom', '--omit=dev', '--sbom-format', 'cyclonedx'],
{ cwd: consumerRoot, maxBuffer: 32 * 1024 * 1024 },
);
```

Keep the existing checks for CycloneDX format, root component, installed `agent-bundle` dependency closure, and absence of workspace/`.pnpm` paths. Always remove the temp root in `finally`. Print only the validated SBOM JSON so the existing test can parse stdout deterministically.

- [ ] **Step 5: Tighten the tarball regression**

In `packages/agent-bundle/tests/release-audit.test.ts`, change repository-owned root commands to `corepack pnpm ...`, keep `npm pack` and the temporary consumer's `npm install`, and add:
In the packed consumer tests, change repository-owned root commands to `corepack pnpm ...`, keep `npm pack` and the temporary consumer's `npm install`, and add:

```ts
expect(files.some(({ path }) => path.startsWith('examples/'))).toBe(false);
expect(packageManifest.dependencies?.['agent-bundle']).toBeUndefined();
expect(JSON.stringify(packageManifest)).not.toContain('workspace:');
```

- [ ] **Step 6: Migrate GitHub Actions to the pinned manager**
- [ ] **Step 5: Migrate GitHub Actions to the pinned manager**

In all three workflows, add `corepack enable` before install and replace `npm ci` with:

Expand All @@ -176,24 +153,24 @@ In all three workflows, add `corepack enable` before install and replace `npm ci

Replace repository script invocations with `pnpm <script>`. Keep the native matrix environment variables, Node versions, self-hosted/manual restrictions, and `pkg-pr-new publish ... './packages/agent-bundle'` scope unchanged.

- [ ] **Step 7: Generate the sole lockfile and verify GREEN**
- [ ] **Step 6: Generate the sole lockfile and verify GREEN**

Run:

```bash
corepack pnpm install
corepack pnpm install --frozen-lockfile
pnpm build
npx rstest --config rstest.config.ts packages/agent-bundle/tests/workspace-contract.test.ts packages/agent-bundle/tests/release-audit.test.ts
pnpm audit:release
npx rstest --config rstest.config.ts packages/agent-bundle/tests/workspace-contract.test.ts
pnpm lint:release
```

Expected: all commands PASS; the workspace test lists only the root and two product packages at this boundary; the external consumer checks pass; `git status --short` shows `package-lock.json` deleted and exactly one new `pnpm-lock.yaml`.

- [ ] **Step 8: Commit**
- [ ] **Step 7: Commit**

```bash
git add package.json pnpm-workspace.yaml pnpm-lock.yaml .github/workflows scripts packages/agent-bundle/tests/workspace-contract.test.ts packages/agent-bundle/tests/release-audit.test.ts package-lock.json
git add package.json pnpm-workspace.yaml pnpm-lock.yaml .github/workflows scripts packages/agent-bundle/tests/workspace-contract.test.ts package-lock.json
git commit -m "build: adopt canonical pnpm workspace"
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ pnpm test:packed
pnpm lint:package
```

Expected: pack dry-run, publint, ATTW, dependency/audit/signature/SBOM checks, and packed Workbench tests all pass.
Expected: pack dry-run, publint, ATTW, and packed Workbench tests all pass.

- [ ] **Step 4: Audit every design requirement against current evidence**

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,8 @@ CI path adds `pnpm examples:check`; full test and release gates remain separate.
The release boundary stays package-manager-neutral:

- `publint` and `attw` inspect the built package;
- the package tarball is installed into an external temporary npm consumer;
- npm dependency, signature, audit, and CycloneDX SBOM checks run against that
clean production consumer rather than pnpm's workspace store;
- the package tarball is installed into external temporary npm consumers by
the packed test pool rather than pnpm's workspace store;
- packed browser tests continue exercising the installed tarball, not a
workspace link.

Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@
"release": "pnpm check:release && changeset publish",
"preview:publish": "pkg-pr-new publish --previewVersion --peerDeps --no-compact --no-template './packages/agent-bundle' './packages/rsc-runtime' './packages/create-agent-bundle'",
"pack:dry-run": "pnpm build && npm pack ./packages/agent-bundle --dry-run --json",
"audit:release": "pnpm lint:package && attw --pack --profile esm-only packages/agent-bundle && node scripts/audit-packed-release.mjs",
"check:release": "pnpm pack:dry-run && pnpm audit:release && pnpm test:packed:release",
"check:release:ci": "pnpm pack:dry-run && pnpm audit:release && pnpm test:packed",
"lint:release": "pnpm lint:package && attw --pack --profile esm-only packages/agent-bundle",
"check:release": "pnpm pack:dry-run && pnpm lint:release && pnpm test:packed:release",
"check:release:ci": "pnpm pack:dry-run && pnpm lint:release && pnpm test:packed",
"example:hooks": "pnpm build && pnpm --filter @agent-bundle-example/hooks-and-scripts dev",
"example:audiobook": "pnpm build && pnpm --filter @agent-bundle-example/audiobook-curator dev",
"example:mcp-app": "pnpm build && pnpm --filter @agent-bundle-example/mcp-app dev",
Expand Down
5 changes: 2 additions & 3 deletions packages/agent-bundle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1005,10 +1005,9 @@ then Hooks and Scripts, then the interactive MCP App.

Run the complete local delivery gate with `pnpm check && pnpm check:release`.
`pnpm check:release` is release-only: its exact package-script components are
`pnpm pack:dry-run`, `pnpm audit:release`, and `pnpm test:packed:release`, and it does not replace
`pnpm pack:dry-run`, `pnpm lint:release`, and `pnpm test:packed:release`, and it does not replace
`pnpm check`. `pnpm release` runs that release gate before `changeset publish`.
Native Claude/Codex smokes stay intentionally opt-in and skipped in ordinary CI.
npm publishing is deferred until the release owner picks the final package name/scope;
pkg.pr.new previews are the interim channel, and the first npm release will use npm
package provenance (`publishConfig.provenance` is already set). `pnpm audit:release` fails if any
publishable tarball lacks `LICENSE`, `NOTICE`, or the `"license": "Apache-2.0"` manifest field.
package provenance (`publishConfig.provenance` is already set).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add a changeset for the shipped README change

This edit changes packages/agent-bundle/README.md, which is included in the package's published files, but the commit adds no .changeset/*.md. The stated skip-changeset rationale only accounts for the deleted root scripts; removing a release guarantee from packaged documentation is observable and therefore does not qualify as a genuinely no-op change. Add exactly one patch changeset for agent-bundle rather than relying on the escape hatch.

AGENTS.md reference: AGENTS.md:L97-L107

Useful? React with 👍 / 👎.

Loading
Loading