Files
openchamber/.agents/skills/changelog-authoring/SKILL.md
T
Bohdan Triapitsyn d9b9c8edab feat(changelog): one source file per release, generated outputs
Release notes now live in changelog/<version>.md (front matter with
version and date, then ## App and ## VS Code sections grouped into
New, Improvements, Fixes, Misc) plus changelog/unreleased.md for what
has not shipped. `bun run changelog:build` renders CHANGELOG.md,
packages/vscode/CHANGELOG.md, and changelog/index.json from them;
`changelog:check` fails when the outputs are behind and runs in CI and
in release:prepare. `oc-dev create-release` promotes unreleased.md to
the versioned file dated today and rebuilds.

The existing history was split mechanically: every bullet kept, sorted
into groups by keyword, five hand-typed headers with one-digit days
normalised to YYYY-MM-DD (the update dialog matched none of them). The
generated files keep today's release headers, which the update dialog,
the release workflow, and the website match by regex.

Claude-Session: https://claude.ai/code/session_01VqV56Hez25hTxXH4ipJfzH
2026-09-05 15:58:23 +03:00

8.0 KiB

name, description, license, compatibility
name description license compatibility
changelog-authoring Use only when the maintainer explicitly asks to update the changelog — then draft the OpenChamber `[Unreleased]` entries (main app and VS Code extension) summarizing changes since the latest git tag. MIT opencode

Gate

The changelog is written once per release, by the maintainer, as one story. Both CHANGELOG.md files stay untouched by every other task; a fix or a merged PR lands without a changelog line. Proceed only when the current message asks to update the changelog.

Write changelog/unreleased.md and nothing else. CHANGELOG.md, packages/vscode/CHANGELOG.md, and changelog/index.json are generated from changelog/*.md; after editing run bun run changelog:build and commit the source with the regenerated files (changelog/README.md describes the file format, bun run changelog:check verifies the outputs). The version header, date, and file promotion happen at release time through oc-dev create-release, never by hand.

unreleased.md holds two sections:

  • ## App — Web, Desktop, Mobile/PWA, shared UI.
  • ## VS Code — the extension only, written separately (see below).

The shape of a release

Every release section is grouped. Under the version header come, in this order and only when non-empty:

### New
- **VS Code: comments on code.** Select lines, click the `+` in the gutter ... (thanks to @felipegenef)
- Project actions in worktrees: a session in a worktree can use the parent project's saved actions (thanks to @mattv8).

### Improvements
- Chat: Markdown tables are readable again, columns take the width their content needs (thanks to @ChangeHow).

### Fixes
- Chat: huge patches in tool cards open without freezing the page (thanks to @karimodm).

### Misc
- Bundled OpenCode updated to 1.19.

Where a change goes:

  • New — something the user could not do before: a feature, a surface, a language.
  • Improvements — something they could do works better or reads clearer now.
  • Fixes — something was broken and showed a wrong result; the bullet names the symptom.
  • Misc — bundled tool versions, packaging, platform support, retirements. Rarely more than a few lines.

The generator emits the groups in this order whatever order the source lists them, drops empty ones, and writes the ## [x.y.z] - YYYY-MM-DD header that the update dialog, the release workflow, and the website match by regex.

The bullet

The reader is a user of the app. They never opened the code, they will not open the PR, and they give a bullet about five seconds. Write for that reader:

  • One sentence, two at most. A bold highlight may take three. Under about 200 characters for a regular bullet.
  • Name what they see or do. The screen, the button, the gesture, the outcome. When a fix removed a symptom, name the symptom in plain words ("froze the page", "sent the comment by accident", "stopped at the first page").
  • Plain words only. Anything a user would have to look up is out: store, cache, route, payload, reconcile, authoritative, lifecycle, runtime, listing, revision, PTY, bridge. Internal component names are out unless the user sees them on screen.
  • State the new behaviour directly. Say what happens now. Framing it against the old way ("X, not Y", "instead of Y", "rather than Y") is the tell of machine-written notes; drop the second half and let the sentence stand.
  • Area prefix, then the fact. Chat:, Settings/Providers:, Git:, Mobile:, Server:, CLI:, VS Code: in the main file. A highlight bolds its prefix: - **Project actions:** ....
  • Details stay in the PR. No commit hashes, file paths, numbers that do not change what the user does, or mechanism. One concrete number is fine when it is the whole point ("more than 200 sessions").

Worked example, same change:

Weak: a saved action counts as running exactly while its command runs. When the command exits, the stop icon goes away even if the terminal tab stays open, and a second client can start the action again. Every client shows the same run, and the sidebar marks a directory that has one. Sessions in a linked worktree can use the parent project's actions; they run in the worktree by default.

Good: the running state of a saved action is reliable now. It shows as running only while the command is really running, every device sees the same state, and the sidebar shows which project has something running. Worktrees can use the parent project's actions.

The weak version is accurate and lost the reader at the first comma. The good version keeps the three things a user notices and drops the mechanics that explain them.

Load .agents/skills/communication-style/SKILL.md and run its pattern scan over the finished sections; its rules on em dashes, hedging, and puffery apply here unchanged.

Gather

Determine the base and read everything through HEAD:

BASE=$(git describe --tags --abbrev=0 2>/dev/null || git rev-list --max-parents=0 HEAD)
git log --oneline "$BASE"..HEAD
git diff --stat "$BASE"..HEAD

A squashed merge (subject ending in (#123)) or a Merge pull request #123 commit hides the real change behind a terse subject. Read the PR: gh pr view <number> --json number,title,body,author,mergedAt. Distill its intent into the bullet shape above; the body's own wording is reviewer-facing and stays there. When gh cannot fetch it, use the commit and diff and say what remains uncertain.

Follow-ups fold in. A maintainer commit that completes or reworks a merged PR (a "complete ... follow-ups" commit, a reshaping merge) has no bullet of its own; its user-visible effect goes into the bullet of the PR it finished, written as one behaviour. A change with no user-visible effect (tooling, CI, tests, docs, dead-code removal, internal guards) gets no bullet at all.

Gathering is complete when every user-visible change has evidence, a known platform reach, and a contributor identity where one exists.

Order and highlights

  • Inside each group, sort by user impact: breaking changes, then what most users meet daily, then the rest, visual polish last.
  • Bold the prefix of the strongest one to three bullets in the whole release and put each at the top of its group. A highlight introduces a capability, changes a common workflow, or fixes something severe. Size of the diff and effort spent are not reasons to bold.
  • A change that is both a feature and a fix (a reworked area) gets one bullet in the group that describes what the user gains most; a second bullet only when the two halves are things a user would look for separately.
  • Rank each changelog on its own; a main-app highlight is not automatically a VS Code highlight.

VS Code section

An entry belongs here only when the extension actually mounts the surface: trace from packages/vscode/webview/main.tsx → VSCodeApp → VSCodeLayout, which mounts a subset of the shared UI, and read the surface map in packages/vscode/src/DOCUMENTATION.md. Server-side changes have no entry here; the extension runs no OpenChamber server. Prefixes drop the VS Code: part. Bullets are written separately from the App section rather than tagged, so reachability is a decision made per bullet. When reachability is uncertain, leave the entry out; a false entry becomes a bug report.

Credit

End the bullet with (thanks to @username) using the GitHub login from the PR or commit. The repo owner btriapitsyn gets no credit line.

Done when

Read each finished section top to bottom and check every bullet:

  • A user could point at it in the app within five seconds of reading it.
  • It is one or two sentences (three for a highlight) in plain words, with no mechanism and no contrast against the old behaviour.
  • It sits in the group its wording claims (a Fix names a symptom, a New names a capability) and no higher than the bullets above it in impact.
  • Empty groups are absent; present groups appear in the order New, Improvements, Fixes, Misc.
  • It appears only in the section whose runtime receives it.
  • Its contributor is credited.
  • bun run changelog:build ran and the regenerated files are staged with the source.