Skip to content

feat(releases): add changelog and release feeds#337

Open
bntvllnt wants to merge 6 commits into
mainfrom
feat/issue-260-changelog
Open

feat(releases): add changelog and release feeds#337
bntvllnt wants to merge 6 commits into
mainfrom
feat/issue-260-changelog

Conversation

@bntvllnt
Copy link
Copy Markdown
Collaborator

@bntvllnt bntvllnt commented May 12, 2026

Summary

  • Promoted root CHANGELOG.md into the canonical Keep a Changelog source, added cliff.toml, and updated release docs/workflow so GitHub Release notes come from the changelog.
  • Added /changelog, /releases, /rss.xml, /atom.xml, and /docs/changelog redirect, backed by a shared changelog/release parser with GitHub Releases fallback behavior.
  • Added release discoverability across root feed alternates, sitemap, llms routes, header What's new menu, footer links, landing release strip, and package changelog notes.
  • Stabilized release feeds so undated preview/unreleased changelog entries are not emitted as RSS/Atom items with request-time/current timestamps.

Closes #260

Validation

  • pnpm -F @vllnt/ui-registry exec eslint app/rss.xml/route.ts app/atom.xml/route.ts PASS
  • pnpm -F @vllnt/ui-registry exec tsc --noEmit --project tsconfig.json PASS
  • deterministic feed smoke via pnpm exec tsx against app/rss.xml/route.ts and app/atom.xml/route.ts PASS (rssPreviewItems=0, atomPreviewEntries=0, no current-year preview feed entries)
  • pnpm build PASS
  • pnpm test:once PASS (216 files, 1215 tests)
  • git diff --check HEAD~1..HEAD PASS

Notes: local full pnpm lint is blocked before reaching this change by an existing ESLint plugin runtime error in apps/registry/app/report/report-bug-form.tsx (jsx-a11y/label-has-associated-control: TypeError: (0 , _minimatch.default) is not a function). Touched RSS/Atom route lint passes. Vitest emits existing React duplicate-key warnings while passing.

Design Deviations

None.

@bntvllnt bntvllnt added documentation Improvements or additions to documentation enhancement New feature or request discoverability Site/library discoverability labels May 12, 2026
@bntvllnt bntvllnt self-assigned this May 12, 2026
Comment thread apps/registry/app/rss.xml/route.ts Outdated
@bntvllnt
Copy link
Copy Markdown
Collaborator Author

PR review fallback (formal REQUEST_CHANGES unavailable because GitHub rejects requesting changes on my own PR):

Reviewed PR #337 at 3d18139.

Blocking finding:

  • apps/registry/app/rss.xml/route.ts:39 — RSS/Atom feed items use current request time for undated release records. The current root CHANGELOG.md has [Unreleased], which becomes the 0.3.0 preview release with no date; generated RSS shows that preview item with a current pubDate, and Atom shows the same via updated. This makes the release feeds non-deterministic and advertises unpublished preview notes as newly published on every rebuild/revalidation. I left a line comment: feat(releases): add changelog and release feeds #337 (comment)

Validation evidence:

  • git diff --check origin/main...HEAD: PASS
  • pnpm -F @vllnt/ui-registry exec tsc --noEmit: PASS
  • changelog/release helper smoke: 13 entries, 14 releases, first v0.3.0 with firstDate null
  • RSS/Atom route smoke: HTTP 200, expected content-types, XML parses
  • Evidence files: /tmp/pr337-review/final-validation.txt, /tmp/pr337-review/rss.xml, /tmp/pr337-review/atom.xml

Copy link
Copy Markdown
Collaborator Author

@bntvllnt bntvllnt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — feed timestamp remediation at 48e3dd0

Review mode: Standard — focused remediation touches two public feed route handlers (rss.xml and atom.xml).

BLOCKING

  • None.

WARN

  • None.

VERIFIED CLEAN

  • Prior blocker is resolved for emitted feed entries: apps/registry/app/rss.xml/route.ts and apps/registry/app/atom.xml/route.ts now filter releases with release.date === undefined before building RSS <item> / Atom <entry> nodes, so the undated Unreleased / 0.3.0 preview record is not emitted with a request-time timestamp.
  • The route-local date helpers no longer use Date.now() fallback; the remediation replaces value ?? Date.now() with a deterministic value ?? 0, and the only calls for item/entry dates are after the dated-release filter.
  • The change stays in the requested remediation scope: only apps/registry/app/rss.xml/route.ts and apps/registry/app/atom.xml/route.ts changed in commit 48e3dd0.
  • PR metadata is current for this remediation: body mentions the feed stabilization, Closes #260 is present, and live checks for head 48e3dd0 are green.

VALIDATION

  • Ran on detached review worktree pinned to 48e3dd090f3d530eb6e80c8955d11070f262e1d9.
  • pnpm -F @vllnt/ui-registry exec eslint app/rss.xml/route.ts app/atom.xml/route.ts PASS.
  • pnpm -F @vllnt/ui-registry exec tsc --noEmit --project tsconfig.json PASS.
  • Focused feed smoke importing both route handlers PASS: rssPreviewItems=0, atomPreviewEntries=0, rssCurrentTimestampPreview=false, atomCurrentTimestampPreview=false, rssContainsPreviewText=false, atomContainsPreviewText=false.
  • gh pr checks 337 --json name,state,bucket,... PASS for all six live checks at this head: CodeQL, issue link, Quality Gates, codebase health, JavaScript/TypeScript analysis, Actions analysis.

No approval submitted; this is a focused COMMENT review only.

@vllnt-pilot vllnt-pilot Bot had a problem deploying to Preview · pr-337-storybook May 18, 2026 17:15 Failure
@vllnt-pilot
Copy link
Copy Markdown

vllnt-pilot Bot commented May 18, 2026

Preview ready · Updated 2026-05-20T14:42:27Z

Service Status Preview
ui-registry Ready https://pr-337-ui-registry.preview.vllnt.ai
Inspect
  • Tailnet-only by default (not reachable from public internet)
  • Cert: real Let's Encrypt wildcard
  • Reply with /clean to destroy this preview now

@vllnt vllnt deleted a comment from vllnt-pilot Bot May 18, 2026
@vllnt vllnt deleted a comment from vercel Bot May 18, 2026
…m author

- Replace Promise.any race in readChangelog() with ordered for-of fallback
  (root CHANGELOG.md takes precedence over packages/ui/CHANGELOG.md)
- Remove hardcoded "0.3.0" version for Unreleased entries; keep label as
  "Unreleased" to avoid silently misrepresenting the next release
- Drop stale v0-3-0 anchor special-case from getLatestReleaseRecords()
- Add feed-level <author><name>vllnt</name></author> to Atom 1.0 feed
  (RFC 4287 §4.1.1 compliance)
- feedUpdatedAt now returns string|undefined; omit <updated>/<lastBuildDate>
  when no dated release exists, falling back to BUILD_TIME env var
- Replace inline /breaking/i.test() in githubRecordToReleaseRecord with
  countBreakingChangesFromNotes() for consistent bullet-count semantics
- Use latest parsed changelog entry date for JSON-LD dateModified in
  changelog page instead of new Date()
</div>

<div className="mt-10 space-y-6">
{releases.map((release, index) => (
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This maps every getReleaseRecords() entry into a public release card and marks index === 0 as latest. Because the root changelog starts with ## [Unreleased], the page will render unpublished/canary-only notes as the public “What’s new” release and include them in the JSON-LD ItemList.

Please keep [Unreleased] on /changelog, but filter it out for /releases (and other release consumers) so only dated/versioned releases are shown as releases.

fi

- name: Generate changelog
- name: Read changelog release notes
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This release job only reads an already-bumped changelog section and fails if maintainers have not manually created it. Issue #260 asks for the Conventional Commits → CHANGELOG.md → GitHub Releases pipeline with a CI release step that bumps/generates the changelog on release tag.

Either add the git-cliff/generation step here, or explicitly narrow the PR/issue scope so the release-automation acceptance criterion is not treated as satisfied.

try {
return await readFile(candidate, "utf8");
} catch {
// try next candidate
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repo rules ban inline // comments in shipped code. This retry/fallback behavior is simple enough to express without the comment, or it should be refactored into a helper name if it needs durable explanation.

priority: 0.8,
},
function getRegistryItems(): readonly RegistryItem[] {
return (registry as { readonly items: readonly RegistryItem[] }).items;
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds/moves a registry type assertion on a changed line, and the same pattern now appears in the changed llms routes. The repo’s code-style non-negotiables forbid as assertions in shipped code; please type/check the registry import at one boundary instead of asserting in each consumer.

Copy link
Copy Markdown
Collaborator Author

@bntvllnt bntvllnt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — 4 findings (4 blocking, 0 warn)

I would submit REQUEST_CHANGES, but GitHub rejects that verdict because this review account is treated as the PR author (Review Can not request changes on your own pull request). Treat this COMMENT review as a request-changes-equivalent: the inline comments above are blocking until fixed.

BLOCKING

  • C1 — /releases renders Unreleased as the latest release

    • Evidence: apps/registry/lib/changelog.ts:285-287 turns every root changelog heading into a release record, including the leading ## [Unreleased]; apps/registry/app/releases/page.tsx:201-205 renders every record and marks index === 0 as the latest card. The current root CHANGELOG.md:11-20 starts with [Unreleased] and describes canary-only / not-yet-published work, so /releases will show that as a public “What’s new” release and include it in the JSON-LD ItemList.
    • Fix: keep [Unreleased] on /changelog, but filter it out of release consumers (/releases, JSON-LD, landing strip, llms release notes; feeds already filter dated records).
  • S1 — release automation still depends on a manually pre-bumped changelog

    • Evidence: .github/workflows/publish.yml:123-148 only reads an already-existing CHANGELOG.md version section and fails if it is missing; docs/RELEASING.md:25-26 tells maintainers to manually move entries in both changelog files before dispatch.
    • Fix: either implement the automated git-cliff/release-tag changelog generation path requested by #260, or explicitly narrow the PR/issue/design-deviation story so that acceptance criterion is not treated as satisfied.
  • T1 — changed shipped code violates the repo ban on inline comments

    • Evidence: apps/registry/lib/changelog.ts:161 adds // try next candidate.
    • Fix: remove the comment or encode the behavior in a helper name.
  • T2 — changed shipped code keeps/introduces forbidden type assertions

    • Evidence: apps/registry/app/sitemap.ts:21, apps/registry/app/llms.txt/route.ts:50, and apps/registry/app/llms-full.txt/route.ts:34 all use (registry as { ... }).items on changed lines.
    • Fix: type/check the registry import once at the boundary instead of asserting in each consumer.

VERIFIED CLEAN

  • Closes #260 is present in the live PR body.
  • Live GitHub checks are green at head 61b0c226a88a0fe8c014d0379326d559a4db35a1.
  • git diff --stat origin/main...HEAD is non-empty and matches the PR’s claimed release/discoverability scope.
  • git diff --check origin/main...HEAD passes.
  • Feed timestamp remediation from the prior review remains intact: RSS/Atom item generation filters to dated release records before emitting entries.

VALIDATION

  • Reviewed every changed file in the PR against origin/main...HEAD.
  • Ran: gh pr view 337 --repo vllnt/ui --json ....
  • Ran: gh pr checks 337 --repo vllnt/ui --json ... — all 9 checks reported pass/success.
  • Ran: git diff --check origin/main...HEAD — PASS.
  • Submitted 4 inline file comments: release filtering, release workflow automation gap, inline comment rule, registry type assertion rule.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

discoverability Site/library discoverability documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: CHANGELOG.md + /changelog page + /rss.xml feed

1 participant