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
130 changes: 127 additions & 3 deletions .changeset/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,129 @@
# Changesets

Add one Markdown changeset for every user-visible package change. Version PRs
and npm publication are handled by the standard Changesets workflow. Pull
request canaries use `pnpm preview:publish` and pkg.pr.new.
This repository versions its published packages with
[Changesets](https://github.com/changesets/changesets). A changeset is a small
Markdown file in this directory that names the packages a pull request
changes, the bump each one needs, and a user-facing summary. `changeset
version` folds pending changesets into `CHANGELOG.md` and `package.json`
versions; `changeset publish` (when enabled) publishes the result.

## Which packages get changesets

| Package | Path | Status |
| ----------------------- | ------------------------------ | ---------------------------------------- |
| `agent-bundle` | `packages/agent-bundle` | publishable |
| `@agent-bundle/runtime` | `packages/rsc-runtime` | publishable |
| `create-agent-bundle` | `packages/create-agent-bundle` | publishable |
| `agent-bundle-workbench`| `packages/workbench` | private, ignored |
| `@agent-bundle-example/*`, `@agent-bundle/rsc-agent-runtime-demo` | `examples/*` | private, ignored |
| `@agent-bundle/docs` | `website` | private, ignored |

Private packages are listed under `ignore` in `config.json` and are also
excluded by `privatePackages.version: false`, so `pnpm changeset` never
prompts for them and a changeset must never name them. The workspace root
(`agent-bundle-workspace`) is not a workspace package and needs no entry. A
change that only touches private packages, `docs/**`, `agent-patterns/**`,
CI, or a publishable package's `tests/**` directory needs no changeset
(`changedFilePatterns` in `config.json` exempts `tests/**`).

## Rules

- **One changeset per pull request** that changes a publishable package's
shipped surface (`src/**`, `bin/**`, `templates/**`, `package.json`,
`README.md`, build config). Name every affected package in that one file.
Split a PR instead of writing several changesets for it; the rare exception
is a PR that intentionally ships two independent user-facing changes.
- **Semver before 1.0**: `minor` = breaking change (removed or renamed
exports, CLI flags, config keys, diagnostic codes, on-disk formats, or
changed defaults that require consumer action); `patch` = everything else,
including new features. Do not use `major` until the first `1.0.0`; after
1.0 the usual semver meanings apply.
- **Summary style**: user-facing, imperative, one paragraph. Lead with what
changes for the consumer, name the affected command, export, or config key,
and mention diagnostic codes (`AB` + four digits, see `docs/diagnostics.md`)
when a diagnostic is added, removed, or reworded. Do not describe the implementation, the
review thread, or internal refactors that leave behavior unchanged. End
with the pull request reference in parentheses, `(#123)`, when the PR
number is known.
- **Never edit `CHANGELOG.md` or a publishable `package.json` `version` by
hand.** The "Version Packages" pull request is machine-owned; it is
regenerated on every push to `main` and any manual edit is overwritten.
- **`skip-changeset` label**: a PR that changes publishable files but ships
no observable change (comments, formatting, type-only refactors) may carry
the `skip-changeset` label instead of a changeset. The `Changeset present`
check honours the label; reviewers should challenge its use.

## Writing a changeset

Run `pnpm changeset` and follow the prompts, or create
`.changeset/<slug>.md` by hand:

```md
---
"agent-bundle": patch
"create-agent-bundle": patch
---

`agent-bundle doctor` now reports `AB6026` when a Cursor hook manifest points
at a missing built script instead of failing silently; scaffolded projects
pick up the same check. (#123)
```

Use a descriptive kebab-case slug (the generated random names are fine
too). Commit the file with the change it describes.

## Enforcement

`.github/workflows/changeset.yml` runs `pnpm changeset status
--since=origin/main` on every pull request. It fails when a publishable
package changed (per `changedFilePatterns`) and the PR adds no
`.changeset/*.md`. It is skipped for the `skip-changeset` label and for the
machine-owned `changeset-release/main` branch of this repository (not for a
fork or contributor branch of the same name). Docs-only PRs pass automatically
because they change no publishable package.

## Versioning decisions

- `agent-bundle` and `@agent-bundle/runtime` version **independently**
(`fixed` and `linked` are empty). `agent-bundle` declares
`@agent-bundle/runtime` as an *optional* peer with range `*`, and
`@agent-bundle/runtime` does not depend on `agent-bundle`, so neither
package needs to move when the other does. Preview tarballs pin the peer to
the same commit (`docs/preview-packages.md`), which is a preview concern,
not a version-coupling one. Revisit if the peer range ever becomes exact.
- `create-agent-bundle` versions independently; its templates pin
`agent-bundle` explicitly rather than through a workspace range.
- `updateInternalDependencies: "patch"` with
`bumpVersionsWithWorkspaceProtocolOnly: true`: only `workspace:` ranges
between publishable packages would trigger dependent patch bumps, and
there are none today.
- `access` stays `"restricted"` at the repository level until the release
owner decides the npm package names and access policy
(`docs/preview-packages.md`). `@agent-bundle/runtime` and
`create-agent-bundle` already override it with `publishConfig.access`.

## Release flow

1. PRs merge to `main` with their changesets.
2. `.github/workflows/release.yml` runs `changesets/action` on every push to
`main`. While changesets are pending it pushes `changeset-release/main`
and opens or refreshes the **Version Packages** pull request, which
deletes the consumed changesets, bumps versions, and writes `CHANGELOG.md`
entries. Review it, never edit it. GitHub does not start workflows for
events created with the built-in `GITHUB_TOKEN`, so for that PR to get
PR CI the repository needs a `CHANGESETS_GITHUB_TOKEN` secret (fine-grained
PAT or GitHub App installation token with `contents: write` and
`pull-requests: write` on this repository); the workflow falls back to
`GITHUB_TOKEN`, in which case close and reopen the PR to trigger CI.
3. Merging Version Packages pushes a `Version Packages` commit to `main`
with no pending changesets. With publishing disabled (the default) the
workflow then runs the release gates (`pnpm check:release`) and stops;
nothing reaches npm.
4. Publishing is opt-in: set the repository variable
`AGENT_BUNDLE_NPM_PUBLISH=true` and the `NPM_TOKEN` secret. The action
then runs `pnpm release` (`pnpm check:release && changeset publish`) with
npm provenance (`NPM_CONFIG_PROVENANCE=true`, `id-token: write`) and
creates GitHub releases and tags.

Until publishing is enabled, installable previews come from pkg.pr.new
(`pnpm preview:publish`, `docs/preview-packages.md`).
8 changes: 7 additions & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@
"access": "restricted",
"baseBranch": "main",
"bumpVersionsWithWorkspaceProtocolOnly": true,
"changedFilePatterns": ["**", "!tests/**"],
"commit": false,
"fixed": [],
"ignore": [],
"ignore": [
"agent-bundle-workbench",
"@agent-bundle-example/*",
"@agent-bundle/rsc-agent-runtime-demo",
"@agent-bundle/docs"
],
"linked": [],
"privatePackages": {
"tag": false,
Expand Down
62 changes: 62 additions & 0 deletions .github/workflows/changeset.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Changeset

# Every pull request that changes a publishable package must ship a
# `.changeset/*.md` entry (see .changeset/README.md and AGENTS.md). This
# lives outside ci.yml on purpose: toggling the `skip-changeset` label must
# re-evaluate only this check, not re-run the heavy CI matrix.
on:
pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]

permissions:
contents: read

concurrency:
group: changeset-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
changeset:
name: Changeset present
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
# `changeset status --since=origin/main` diffs against the merge-base
# with main, so the PR merge commit needs enough history to reach it.
- uses: actions/checkout@v7
with:
fetch-depth: 0
persist-credentials: false
- uses: pnpm/setup@v2
with:
cache: true
install: false
runtime: node@22.19.0
- run: pnpm install --frozen-lockfile
- name: Require a changeset for publishable package changes
env:
SKIP_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'skip-changeset') }}
# Only the machine-owned release branch of this repository is exempt;
# a fork or contributor branch that happens to share the name is not.
IS_RELEASE_BRANCH: >-
${{ github.event.pull_request.head.ref == 'changeset-release/main' &&
github.event.pull_request.head.repo.full_name == github.repository }}
run: |
set -euo pipefail

if [ "$SKIP_LABEL" = "true" ]; then
echo "::notice::skip-changeset label present; not requiring a changeset."
exit 0
fi
if [ "$IS_RELEASE_BRANCH" = "true" ]; then
echo "::notice::Version Packages branch; changesets are consumed here, not added."
exit 0
fi

# Exit 1 when a publishable package changed (per changedFilePatterns
# in .changeset/config.json; tests/** is exempt) and this PR adds no
# .changeset/*.md. Docs-only and private-package changes need none.
if ! pnpm changeset status --since=origin/main --verbose; then
echo "::error::This PR changes a publishable package (agent-bundle, @agent-bundle/runtime, create-agent-bundle) without a changeset. Run 'pnpm changeset' (or add .changeset/<slug>.md) — see .changeset/README.md. For a genuinely no-op change, apply the 'skip-changeset' label."
exit 1
fi
73 changes: 56 additions & 17 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ on:
branches:
- main

# id-token is required for npm package provenance once publishing starts; it is
# inert while the NPM_TOKEN gate below keeps the publish steps skipped.
# contents: write and pull-requests: write let changesets/action push the
# changeset-release/main branch and open/update the "Version Packages" PR.
# id-token: write is required for npm package provenance once publishing is
# enabled; it is inert while PUBLISH_ENABLED below is false. The repository
# setting "Allow GitHub Actions to create and approve pull requests" must
# also be on, or the PR step fails after the branch is pushed.
permissions:
contents: write
id-token: write
Expand All @@ -17,36 +21,71 @@ concurrency:

jobs:
release:
name: Version Packages / release
runs-on: ubuntu-latest
timeout-minutes: 60
env:
# npm publishing is opt-in and off by default: previews ship through
# pkg.pr.new (docs/preview-packages.md) until the package-name and
# access decisions are made. Set the repository variable
# AGENT_BUNDLE_NPM_PUBLISH=true *and* the NPM_TOKEN secret to let the
# action run `pnpm release` (release gates + `changeset publish`) when
# the Version Packages PR merges. While off, the action still opens and
# updates the Version Packages PR, and merging that PR only runs the
# release gates (`pnpm check:release`) so the tree stays publishable.
PUBLISH_ENABLED: ${{ vars.AGENT_BUNDLE_NPM_PUBLISH == 'true' && secrets.NPM_TOKEN != '' }}
steps:
# The action pushes and opens the PR through the GitHub API with the
# token passed below, so the checkout must not persist GITHUB_TOKEN.
- uses: actions/checkout@v7
# No npm release is cut yet; skip publishing until an NPM_TOKEN secret
# exists instead of failing every push to main. Previews stay on
# pkg.pr.new (docs/preview-packages.md).
- id: gate
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: echo "enabled=${{ env.NPM_TOKEN != '' }}" >> "$GITHUB_OUTPUT"
- if: steps.gate.outputs.enabled == 'true'
uses: pnpm/setup@v2
with:
persist-credentials: false
- uses: pnpm/setup@v2
with:
cache: true
install: false
runtime: node@22.19.0
- if: steps.gate.outputs.enabled == 'true'
run: pnpm install --frozen-lockfile
- run: pnpm install --frozen-lockfile
# Release gates run packed Playwright tests; reuse the runner image's
# Chrome (same rationale as the CI Verify job).
- name: Ensure branded Chrome for Playwright
run: |
if command -v google-chrome >/dev/null 2>&1; then
echo "Using preinstalled $(google-chrome --version)"
else
pnpm exec playwright install --with-deps chrome
fi
# changesets/action v2 no longer writes .npmrc from an NPM_TOKEN env
# variable; expose the token via npm config so `changeset publish`
# (pnpm publish) can authenticate. The ${NPM_TOKEN} placeholder is kept
# literal here and expanded by npm/pnpm from the step environment.
- if: steps.gate.outputs.enabled == 'true'
- if: env.PUBLISH_ENABLED == 'true'
run: echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}' >> "$HOME/.npmrc"
- if: steps.gate.outputs.enabled == 'true'
- id: changesets
uses: changesets/action@v2
Comment thread
ScriptedAlchemy marked this conversation as resolved.
with:
publish-script: pnpm release
version-script: pnpm version-packages
github-token: ${{ secrets.GITHUB_TOKEN }}
# Empty publish-script means "version only": the action opens or
# refreshes the Version Packages PR and never publishes.
publish-script: ${{ env.PUBLISH_ENABLED == 'true' && 'pnpm release' || '' }}
commit-message: Version Packages
pr-title: Version Packages
# Events created with the built-in GITHUB_TOKEN never start other
# workflows, so a Version Packages PR opened with it gets no PR CI.
# Provide CHANGESETS_GITHUB_TOKEN (a fine-grained PAT or GitHub App
# installation token with contents: write + pull-requests: write on
# this repository) to have CI run on that PR; without it the action
# still works and the PR can be closed/reopened to trigger CI.
github-token: ${{ secrets.CHANGESETS_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_CONFIG_PROVENANCE: true
# Publishing disabled: when the Version Packages PR lands (no pending
# changesets, "Version Packages" merge commit), prove the versioned
# tree still passes the pre-publish gates that `pnpm release` would run.
- name: Release gates (publish disabled)
if: >-
env.PUBLISH_ENABLED != 'true' &&
steps.changesets.outputs.has-changesets == 'false' &&
startsWith(github.event.head_commit.message, 'Version Packages')
run: pnpm check:release
29 changes: 29 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,35 @@
- Never accept or capture a Workbench route while its loading state is still visible.
- Browser acceptance must cover populated state plus the documented stale-diagnostic and repair flow.

## Changesets

- Every PR that changes a publishable package (`packages/agent-bundle`,
`packages/rsc-runtime`, `packages/create-agent-bundle` — anything except
`tests/**`) must include exactly one changeset: `pnpm changeset` or a
hand-written `.changeset/<slug>.md`. Private packages (`packages/workbench`,
`examples/*`, `website`) are ignored and never named in a changeset.
- Pre-1.0 semver: `minor` = breaking, `patch` = everything else (features
included). No `major` before 1.0.
- Summary: user-facing, imperative, names the command/export/config key,
mentions diagnostic codes, ends with `(#PR)`. Not an implementation note.
- The `Changeset present` CI check fails without one; the `skip-changeset`
label is the escape hatch for genuinely no-op changes only.
- The "Version Packages" PR is machine-owned. Never edit `CHANGELOG.md` or a
publishable `package.json` `version` by hand. Details: `.changeset/README.md`.

## Pull requests

- Every PR: CI green first, then wait for the automated reviewer
(`chatgpt-codex-connector`) to finish. It does not re-review on its own
after a rebase or force-push: after every push, post an `@codex review` PR
comment if no fresh review appears within a few minutes, and confirm its
summary comment cites the current head SHA before merging.
- Address every review thread — fix it in the same PR or reply with a precise
reason — and reply on every thread. After each push, re-check for new
threads and repeat until there are none. Only then merge.
- PRs are squash-merged. Review threads left on an already-merged PR must
still be answered, in a follow-up PR.

## Vendored repos

- `repos/` is **read-only reference material**. Do not edit, format, or import from `repos/**`.
Expand Down
18 changes: 17 additions & 1 deletion docs/preview-packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ Nothing is published to npm yet, deliberately: the current package names are
placeholders, and npm publishing is deferred until the final name is chosen
(it will then use [npm package provenance](https://docs.npmjs.com/generating-provenance-statements);
the publish step exports `NPM_CONFIG_PROVENANCE=true` and runs the packed
release gates before `changeset publish`). Before enabling that path, the
release gates before `changeset publish`, and only runs at all when the
`AGENT_BUNDLE_NPM_PUBLISH` repository variable is `true` — see "How an npm
release will flow" below). Before enabling that path, the
release owner must resolve the repository-wide `"access": "restricted"`
policy for `agent-bundle`, which does not currently override it with
`publishConfig.access`. Until then
Expand Down Expand Up @@ -62,6 +64,20 @@ before the peer rewrite landed (PR #46, fixing #45) still carry the original
package, so pair-installing those older shas with npm still requires
`--legacy-peer-deps`.

## How an npm release will flow

Versioning is driven by Changesets (`.changeset/README.md`). Every PR that
changes a publishable package carries a `.changeset/*.md`; on each push to
`main`, `.github/workflows/release.yml` runs `changesets/action`, which keeps
a machine-owned **Version Packages** pull request up to date with the pending
bumps and `CHANGELOG.md` entries. Merging that PR versions the packages but,
by default, publishes nothing: the workflow only runs the release gates
(`pnpm check:release`). Publishing turns on when the repository variable
`AGENT_BUNDLE_NPM_PUBLISH` is `true` *and* the `NPM_TOKEN` secret exists;
the action then runs `pnpm release` (`pnpm check:release && changeset
publish`) with npm provenance. Until then, previews below are the only
installable artifacts.

## Where previews come from

`.github/workflows/package-preview.yml` runs
Expand Down
Loading