Skip to content

feat(changelog): make releases legible to agents - #81

Open
SPIKESPIGEL404 wants to merge 1 commit into
mainfrom
claude/changelog-geo-surfaces
Open

feat(changelog): make releases legible to agents#81
SPIKESPIGEL404 wants to merge 1 commit into
mainfrom
claude/changelog-geo-surfaces

Conversation

@SPIKESPIGEL404

Copy link
Copy Markdown
Contributor

Four gaps, all on the surfaces agents actually read. The plumbing here is already good — robots.ts names GPTBot, ClaudeBot, OAI-SearchBot, PerplexityBot and Google-Extended by hand, there's llms.txt, llms-full.txt, a git-dated sitemap, RSS and Atom. The changelog just wasn't wired into any of it.

1. llms.txt never mentioned the changelog

The index that tells an assistant what exists listed docs, API reference and recipes. llms-full.txt bundled source.getPages() — docs only. So an agent asked "what's new", "is X supported yet", or "what changed between versions" had no route to an answer.

Adds a ## Changelog section, and appends the release history to the ingestion bundle. The docs say what BitRouter does; only the changelog says what it does now.

2. /changelog.md

RSS and Atom are the developer channel. Assistants fetch Markdown — and a feed carries only the descriptions, never the notes. This serves the whole history as one file, newest first, an ## per release with its date, permalink and breaking flag.

Formatting lives in lib/changelog.ts beside the other pure presentation logic so it's unit-tested; bodies come from fumadocs, which needed includeProcessedMarkdown on the changelog collection. The sync's provenance comment ({/* Auto-generated… Edit freely */}) is stripped — build bookkeeping, invisible on the rendered page, and otherwise the first thing an agent would read about a release.

3. Structured data and article metadata on entries

Entry pages had title, description and canonical, and nothing else. No openGraph, so a shared release link carried no date and no article type. No JSON-LD, on a site that emits it for the organization, the docs and every FAQ.

Adds both. The release node states only version facts (softwareVersion, datePublished, releaseNotes) and carries its own @id — the root layout already publishes the canonical BitRouter SoftwareApplication, and without a distinct id the two merge into one node with contradictory app-level properties. "Which version added X" is a question structured data answers deterministically and prose does not.

4. Teach the sync the folded release format — the time-sensitive one

From v1.0.0-alpha.28 the source repo folds per-PR change files into the release body (bitrouter/bitrouter#820): it leads with ### Breaking changes / ### Added sections holding #### <title> and prose, and the generated bullets move into a collapsed All commits block.

Left alone, the old parser would have regressed on the better input:

  • firstFeatureLine took the first - bullet, which now lives inside the collapsed block — so description (the index headline, the <meta> description, and the RSS summary) would have become a random commit subject while the curated title was ignored.
  • deriveTags matched Fixes, which does not match Fixed. Tags would have gone empty.

Parsing moves to lib/release-notes.mjs, tested against both shapes. Releases whose PRs were all labelled no-changelog still arrive bullets-only and still work. Tags and the breaking flag now read the raw body, so a curated entry is still tagged from the commit groups in its collapsed list and git-cliff's inline [**breaking**] marker isn't lost with them.

Verified against the exact body #820 will publish:

description : "Active policy_table routing only accepts key_strategy: agent_trace."
tags        : ["features","fixes"]
breaking    : true
body        : curated prose only, no commit list

Verification

pnpm build passes (/changelog.md prerenders static, 25 releases, ~22KB), 148 tests pass (21 new), lint:docs and lint:changelog clean. Built HTML confirmed to carry og:type=article, article:published_time, and the release JSON-LD. Pre-existing tsc errors in components/ai/search.tsx and components/markdown.tsx are untouched by this branch.

Not done

  • Per-entry OG images — every changelog link still shares the root social card.
  • The [#anchor] suffixes fumadocs appends to headings in processed Markdown show up in /changelog.md, exactly as they already do throughout llms-full.txt. Fixing that would change the docs bundle too, so it's left alone here.

🤖 Generated with Claude Code

Four gaps, all on the surfaces agents actually read.

llms.txt never mentioned the changelog. The index that tells an assistant what
exists listed docs, API reference and recipes, and llms-full.txt bundled
`source.getPages()` — docs only. So an agent asked "what's new", "is X supported
yet", or "what changed between versions" had no route to an answer, on a site
that otherwise goes out of its way to welcome them (robots.ts names GPTBot,
ClaudeBot, PerplexityBot and the rest by hand). Add a Changelog section, and
append the release history to the ingestion bundle.

Serve /changelog.md. RSS and Atom are the developer channel; assistants fetch
Markdown, and a feed carries only descriptions, never the notes. One file,
newest first, an `##` per release with its date, permalink and breaking flag.
Formatting lives in lib/changelog.ts next to the other pure presentation logic,
so it is unit-tested; the entry bodies come from fumadocs, which needed
`includeProcessedMarkdown` on the changelog collection. The sync's provenance
comment is stripped — build bookkeeping, invisible on the page, and it would
otherwise be the first thing an agent read about a release.

Entry pages had title, description and canonical, and nothing else: no
openGraph, so a shared release link carried no date and no article type, and no
structured data, on a site that emits JSON-LD for the org, the docs and every
FAQ. Add both. The release node states only version facts and carries its own
@id — the root layout already publishes the canonical BitRouter
SoftwareApplication, and without a distinct id the two would merge into one node
with contradictory app-level properties.

Teach the sync to read the folded release format. From v1.0.0-alpha.28 the
source repo folds per-PR change files into the release body
(bitrouter/bitrouter#820), so it leads with `### Breaking changes` / `### Added`
sections holding `#### <title>` and prose, and the generated bullets move into a
collapsed "All commits" block. Left alone the old parser would have regressed on
the better input: `firstFeatureLine` took the first `- ` bullet, which now lives
inside the collapsed block, so `description` — the index headline, the meta
description and the RSS summary — would have become a random commit subject
while the curated title was ignored. Tags matched `Fixes`, which does not match
`Fixed`. Parsing moves to lib/release-notes.mjs and is tested against both
shapes; releases whose PRs were all labelled `no-changelog` still arrive
bullets-only and still work. Tags and the breaking flag now read the raw body,
so a curated entry is still tagged from the commit groups in its collapsed list
and git-cliff's inline `[**breaking**]` marker is not lost with them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant