webjs ships per-package, per-version changelog files.
changelog/
README.md this file
core/
0.6.0.md
0.5.0.md
…
server/
0.7.1.md
…
cli/
0.7.0.md
…
ts-plugin/
0.4.0.md
…
ui/
0.2.0.md
…
The website (website/app/changelog/page.ts) reads every
<pkg>/<version>.md at SSR time, sorts by date descending, and
renders the unified release feed.
The model is "a version bump produces a changelog file". When a
package's package.json version field changes in a commit, the
entry script:
- Identifies the prior version of the same package on
main. - Walks the commits between the prior version's bump and the new
bump that touched files under
packages/<pkg>/. - Filters to conventional-commit prefixes that matter to users:
feat:,fix:,breaking:,perf:. - Groups them under
Breaking/Features/Performance/Fixesand writes a<version>.mdfile with frontmatter (package,version,date,commit_count) plus a bulleted list of changes (PR link + commit SHA + body excerpt).
chore:, refactor:, test:, docs:, style:, build:, ci:
commits never appear in the changelog: those changes don't change the
package's user-facing contract.
---
package: "@webjsdev/core"
version: 0.6.0
date: 2026-05-21
commit_count: 4
---
# @webjsdev/core 0.6.0
## Breaking
- **Title of the change** ([#NN](https://github.com/webjsdev/webjs/pull/NN)) [`abcd123`](https://github.com/webjsdev/webjs/commit/abcd123)
First four lines of the commit body, indented two spaces.
## Features
…
## Fixes
…Frontmatter fields:
| Field | Required | Meaning |
|---|---|---|
package |
yes | The fully-qualified npm name (@webjsdev/<pkg>). |
version |
yes | Semver string of the new release. |
date |
yes | Date of the version-bump commit, YYYY-MM-DD. |
commit_count |
yes | How many qualifying commits this version shipped. |
A single script runs in both modes:
node scripts/backfill-changelog.jsIt walks every package's package.json history, finds version
bumps, and writes a <pkg>/<version>.md for any version that does
not yet have one. Files that already exist are left alone, so
hand-curated entries survive subsequent runs (CI re-runs are safe).
Going forward, the same script runs:
- on every CI build of
main, so a forgotten version bump still produces an entry without manual intervention; - locally when an agent edits a
packages/<pkg>/package.jsonto bump the version (the post-commit hook.hooks/post-version-bumpruns the script).
The AGENTS.md rule that AI agents follow on every code-commit:
When you bump a
packages/<pkg>/package.jsonversion, runnode scripts/backfill-changelog.js(or just commit; CI will regenerate). The generatedchangelog/<pkg>/<version>.mdfile should be reviewed and edited in-place for clarity, especially forbreakingentries that need migration notes. Then commit the changelog file alongside the version bump.
The .github/workflows/release.yml workflow watches for new
changelog/**.md files added in any push to main. For each new
file it runs scripts/publish-release.js, which parses the
frontmatter, composes a release tag of the shape <pkg>@<version>
(e.g. core@0.6.0) with title @webjsdev/<pkg> <version> and the
markdown body as release notes, and calls gh release create.
The publish step is idempotent: it skips any tag that already
exists, so workflow retries and force-pushes are safe. Hand-editing
a changelog/<pkg>/<version>.md after the release was published
does NOT update the release on GitHub. If you need to amend a
published release, edit it on the GitHub Releases UI directly (or
delete the release tag and let a fresh workflow run recreate it).
A commit that touches more than one package (e.g. a refactor that
moves code from core to server) appears in every affected
package's release file when each of those packages bumps its
version. That is the right shape: the same change is a user-facing
event for every package that carries it.
The pre-0.7.1 history was backfilled in one pass. The files in
changelog/<pkg>/ are auto-generated but can (and should) be
hand-edited to add migration notes, examples, or links to docs.
Subsequent re-runs of scripts/backfill-changelog.js will not
overwrite hand edits because the script skips files that already
exist.