Skip to content
Open
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
82 changes: 82 additions & 0 deletions .agents/skills/document-change/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
name: document-change
description: Write the change file that every agentlink pull request must include. Use before opening a pull request, when CI fails the "Change documented" check, or when asked what version a change will release as.
---

# Documenting a change

agentlink's changelog is never written by hand. Each pull request leaves a
**change file** in `.changeset/`, and the release compiles every pending file
into `CHANGELOG.md`, derives the version from them, and deletes them.

CI fails a pull request that adds none. That check is the only thing standing
between a behaviour change and a release that cannot explain itself, so treat
"add the change file" as part of writing the code, not paperwork after it.

## Write the file

`knope document-change` prompts for the type and summary. Writing the file
directly is equally fine — it is `.changeset/<short-kebab-name>.md`:

```md
---
default: minor
---

# `agentlink status` explains why a path is blocked

Previously a blocked path reported only that it was blocked. It now names the
file it collided with and the command that resolves it.
```

The `#` heading is the entry a reader sees in the release notes; everything
below it is the detail. A heading with no body is rendered as a plain bullet,
which is the right shape for a one-line fix.

## Choosing the type

The front-matter key is the type. It picks both the changelog section and the
version bump:

| Type | Section | Use for |
|---|---|---|
| `major` | Breaking changes | An existing workspace stops working, or needs a migration |
| `minor` | Added | A new command, flag, provider or capability |
| `changed` | Changed | Different behaviour that breaks nothing |
| `deprecated` | Deprecated | Still works, will be removed |
| `removed` | Removed | Gone |
| `patch` | Fixed | A bug fix |
| `security` | Security | A vulnerability or a hardening change |

**agentlink is pre-1.0**, where semver treats the whole crate as unstable:
`minor` and below all land as a patch bump (0.0.2 → 0.0.3) and `major` moves the
minor (0.0.2 → 0.1.0). Pick the type that describes the change honestly and let
the version follow; do not inflate the type to force a version.

A pull request may add several change files, and should when it makes more than
one change worth announcing. The release takes the largest bump among them.

## When not to write one

Refactors, tests, CI and documentation change nothing a user of the released
binary would notice. Those carry the **`no changelog`** label on the pull
request, which is what the CI check looks for. Reach for the label when the
answer to *"would someone reading the release notes care?"* is genuinely no —
not when writing the note is inconvenient.

`feat(providers): add <agent>` always needs one: a new provider is exactly the
kind of thing people upgrade to get.

## Verifying

`knope prepare-release --dry-run` prints the version and the changelog section
the pending files produce, without touching anything. CI runs the same command,
so malformed front matter fails on the pull request rather than at release.

## What happens next

Nothing else is manual. Merging to `main` opens a release pull request that
bumps the version, compiles `CHANGELOG.md` and empties `.changeset/`; merging
*that* tags the release and publishes binaries, the GitHub release, crates.io
and npm in one run. Never edit `CHANGELOG.md` or a version in `Cargo.toml` by
hand — the release pull request owns both, and a manual edit is overwritten.
Empty file added .changeset/.gitkeep
Empty file.
11 changes: 11 additions & 0 deletions .changeset/knope-releases.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
default: changed
---

# Releases are cut from change files, not from hand-edited version numbers

Every pull request now documents its own user-visible changes in a
`.changeset/` file, and CI refuses a pull request that adds none. Merging to
`main` opens a release pull request that compiles those files into
`CHANGELOG.md`, bumps the workspace version and empties `.changeset/`; merging
*that* tags the release and runs the whole publish pipeline in one go.
37 changes: 37 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,43 @@ jobs:
- uses: Swatinem/rust-cache@v2
- run: cargo test -p agentlink-domain registry

# Releases are compiled from the change files in .changeset/, so a pull request
# that changes behaviour without leaving one ships invisibly — it would reach
# users with nothing in the changelog to explain it. This is the only thing
# standing between that and a release, so it is a required check.
#
# Work with genuinely nothing to announce (refactors, CI, tests, docs) carries
# the `no changelog` label instead.
change-file:
name: Change documented
if: >-
github.event_name == 'pull_request'
&& github.head_ref != 'release'
&& !contains(github.event.pull_request.labels.*.name, 'no changelog')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Require a change file
run: |
git fetch --quiet origin "${{ github.base_ref }}"
added=$(git diff --name-only --diff-filter=A \
"origin/${{ github.base_ref }}...HEAD" -- '.changeset/*.md')
if [ -z "$added" ]; then
echo "::error::This pull request adds no change file. Run 'knope document-change', or add the 'no changelog' label if it changes nothing a user would notice. See CONTRIBUTING.md#documenting-a-change."
exit 1
fi
echo "Documented by:"
echo "$added"
- uses: knope-dev/action@v2.1.2
with:
version: 0.23.0
# Malformed front matter fails the release, long after the pull request
# that introduced it. Parsing it here reports it to the author instead.
- name: Check it parses
run: knope prepare-release --dry-run

deny:
name: Licenses and advisories
runs-on: ubuntu-latest
Expand Down
65 changes: 65 additions & 0 deletions .github/workflows/prepare-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: Prepare release

# Every push to main refreshes the release pull request, so the next release is
# always visible as a diff: the version bump, the compiled changelog and the
# change files it consumed. Merging that pull request is what releases — see
# release.yml, which runs the whole pipeline in a single go.
on:
push:
branches: [main]

permissions:
contents: read

concurrency:
group: prepare-release
cancel-in-progress: false

jobs:
prepare-release:
name: Refresh the release pull request
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
# PrepareRelease reads tags and history to work out the last release.
fetch-depth: 0
# Kept on deliberately: Knope pushes the `release` branch itself, so
# this job needs working git credentials, and they must outrank the
# default token for the reason given at the push step below.
token: ${{ secrets.RELEASE_PAT }}

# Change files are the only source of a release, so their absence is the
# honest signal that there is nothing to prepare. Checking it here keeps
# Knope's own "nothing to release" case a real error rather than something
# routinely swallowed by continue-on-error.
- name: Is there anything to release?
id: pending
run: |
if ls .changeset/*.md >/dev/null 2>&1; then
echo "found=true" >> "$GITHUB_OUTPUT"
else
echo "No change files — nothing to release."
echo "found=false" >> "$GITHUB_OUTPUT"
fi

- name: Configure git
if: steps.pending.outputs.found == 'true'
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"

- uses: knope-dev/action@v2.1.2
if: steps.pending.outputs.found == 'true'
with:
version: 0.23.0

# Needs a token that can push a branch and open a pull request. It must be
# a PAT rather than GITHUB_TOKEN: GitHub deliberately does not run
# workflows on pull requests opened by GITHUB_TOKEN, which would leave the
# release pull request sitting with no CI on it.
- name: Open or refresh the release pull request
if: steps.pending.outputs.found == 'true'
run: knope prepare-release --verbose
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_PAT }}
55 changes: 0 additions & 55 deletions .github/workflows/release-plz.yml

This file was deleted.

Loading