feat(releases): add changelog and release feeds#337
Conversation
|
PR review fallback (formal REQUEST_CHANGES unavailable because GitHub rejects requesting changes on my own PR): Blocking finding:
Validation evidence:
|
bntvllnt
left a comment
There was a problem hiding this comment.
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.tsandapps/registry/app/atom.xml/route.tsnow filter releases withrelease.date === undefinedbefore building RSS<item>/ Atom<entry>nodes, so the undatedUnreleased/0.3.0 previewrecord is not emitted with a request-time timestamp. - The route-local date helpers no longer use
Date.now()fallback; the remediation replacesvalue ?? Date.now()with a deterministicvalue ?? 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.tsandapps/registry/app/atom.xml/route.tschanged in commit48e3dd0. - PR metadata is current for this remediation: body mentions the feed stabilization,
Closes #260is present, and live checks for head48e3dd0are 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.tsPASS.pnpm -F @vllnt/ui-registry exec tsc --noEmit --project tsconfig.jsonPASS.- 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.
|
Preview ready · Updated 2026-05-20T14:42:27Z
Inspect
|
…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) => ( |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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.
bntvllnt
left a comment
There was a problem hiding this comment.
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-287turns every root changelog heading into a release record, including the leading## [Unreleased];apps/registry/app/releases/page.tsx:201-205renders every record and marksindex === 0as the latest card. The current rootCHANGELOG.md:11-20starts with[Unreleased]and describes canary-only / not-yet-published work, so/releaseswill show that as a public “What’s new” release and include it in the JSON-LDItemList. - 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).
- Evidence:
-
S1 — release automation still depends on a manually pre-bumped changelog- Evidence:
.github/workflows/publish.yml:123-148only reads an already-existingCHANGELOG.mdversion section and fails if it is missing;docs/RELEASING.md:25-26tells 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.
- Evidence:
-
T1 — changed shipped code violates the repo ban on inline comments- Evidence:
apps/registry/lib/changelog.ts:161adds// try next candidate. - Fix: remove the comment or encode the behavior in a helper name.
- Evidence:
-
T2 — changed shipped code keeps/introduces forbidden type assertions- Evidence:
apps/registry/app/sitemap.ts:21,apps/registry/app/llms.txt/route.ts:50, andapps/registry/app/llms-full.txt/route.ts:34all use(registry as { ... }).itemson changed lines. - Fix: type/check the registry import once at the boundary instead of asserting in each consumer.
- Evidence:
VERIFIED CLEAN
Closes #260is present in the live PR body.- Live GitHub checks are green at head
61b0c226a88a0fe8c014d0379326d559a4db35a1. git diff --stat origin/main...HEADis non-empty and matches the PR’s claimed release/discoverability scope.git diff --check origin/main...HEADpasses.- 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.
Summary
CHANGELOG.mdinto the canonical Keep a Changelog source, addedcliff.toml, and updated release docs/workflow so GitHub Release notes come from the changelog./changelog,/releases,/rss.xml,/atom.xml, and/docs/changelogredirect, backed by a shared changelog/release parser with GitHub Releases fallback behavior.Closes #260
Validation
pnpm -F @vllnt/ui-registry exec eslint app/rss.xml/route.ts app/atom.xml/route.tsPASSpnpm -F @vllnt/ui-registry exec tsc --noEmit --project tsconfig.jsonPASSpnpm exec tsxagainstapp/rss.xml/route.tsandapp/atom.xml/route.tsPASS (rssPreviewItems=0,atomPreviewEntries=0, no current-year preview feed entries)pnpm buildPASSpnpm test:oncePASS (216 files, 1215 tests)git diff --check HEAD~1..HEADPASSNotes: local full
pnpm lintis blocked before reaching this change by an existing ESLint plugin runtime error inapps/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.