feat(platform): in-product changelog notification for non-admin users#1576
Conversation
Close the gap for users who don't watch GitHub releases — operators and end users now learn about upgrades inside the product rather than depending on external notification channels. - One-shot toast surfaces the upgrade once per new version - "Current version · What's new" link in the UserButton dropdown links out to the matching GitHub release; clicking clears the red dot - Red dot on the UserButton persists until the release is viewed - New per-user userNotificationState table tracks both the last-toasted and last-seen changelog versions separately so the toast never repeats while the red dot keeps reminding until explicitly acknowledged Companion process docs: - docs/release-notes-format.md — 8-section classification spec with the new Security and Model & Provider categories that back the release notes the "What's new" link points to - docs/security-advisories.md — severity matrix and timeline for coordinated disclosures via GitHub Security Advisories Dev-mode vite plugin now injects TALE_VERSION so the client can read window.__ENV__.TALE_VERSION; the production server path already did.
Avatar-only red dot disappears when the dropdown opens, leaving the user to hunt for which entry corresponds to the unread notice. Mirror the indicator next to the "What's new" link so the two dots cross-reference each other visually — the avatar dot draws attention to open the menu, the link dot identifies the specific entry inside.
…tests UserButton now calls useChangelogNotification (which uses useQuery from convex/react), and the dashboard layout now renders ChangelogToastTrigger. Neither existing test harness ships a ConvexProvider, so both suites broke with "Could not find Convex client." Mock the hook in user-button.test.tsx and the trigger component in dashboard-layout.test.tsx, matching the pattern used for TwoFactor banners.
📝 WalkthroughWalkthroughThis PR implements a changelog notification system that tracks when users have viewed release notes. It adds documentation for release note formatting and security advisory processes, creates a database table to store per-user notification acknowledgment state (lastSeenChangelogVersion and lastToastedVersion), provides server functions for querying and updating this state via Convex mutations and queries, and updates the frontend UI to display the current version with a "what's new" link, show a red dot badge when unseen versions exist, and conditionally trigger a toast notification on app mount when a new version is detected. Translations are added for English, French, and German. Estimated code review effort🎯 3 (Moderate) | ⏱️ ~30 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/release-notes-format.md`:
- Around line 1-5: This doc is missing the required Mintlify frontmatter (title
and description) and the H1 is in Title Case; add YAML frontmatter at the top
with a title and description (the Mintlify-required "title" and "description"
keys) and change the H1 "# Release Notes Format" to sentence case (e.g., "#
Release notes format") so headings follow the sentence case guideline; ensure
the frontmatter title matches the document title (use the sentence-cased form)
and save.
In `@docs/security-advisories.md`:
- Around line 1-3: Add the required Mintlify frontmatter to the top of this doc
by inserting a YAML block containing at minimum title and description (e.g.,
title: "Security advisory process" and a short description) and change the
existing H1 "# Security Advisory Process" to sentence case ("# Security advisory
process") so the heading and metadata follow the project's documentation
guidelines; ensure the frontmatter appears before any content and the H1 text
matches the title casing convention.
In `@services/platform/app/components/user-button.tsx`:
- Around line 393-397: The red-dot indicator (hasUnseenVersion) is aria-hidden
and therefore invisible to assistive tech; update the UserButton trigger so the
unseen-update state is exposed non-visually by adding an accessible cue when
hasUnseenVersion is true (for example, append a short phrase to the trigger's
aria-label like "new update available" or add a visually-hidden sr-only text
node and/or an aria-live region that announces the update). Modify the component
that renders hasUnseenVersion within UserButton (the span with the red-dot) to
keep the visual dot but also provide the non-visual announcement via
aria-label/aria-describedby or sr-only text tied to the trigger so screen
readers receive the same status without changing the visual appearance.
In `@services/platform/messages/de.json`:
- Around line 4499-4502: The "toast.description" string uses formal "Sie" while
the locale uses informal voice; update the value for the "description" key under
"toast" to the informal form (e.g., "Sieh dir die Änderungen in dieser Version
an." or "Schau dir die Änderungen in dieser Version an.") so the "toast" object
("title", "description", "action") remains consistently in the du/dein voice.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: d52ab7a1-e1ef-4229-8dbd-1f129ab06460
⛔ Files ignored due to path filters (1)
services/platform/convex/_generated/api.d.tsis excluded by!**/_generated/**
📒 Files selected for processing (13)
docs/release-notes-format.mddocs/security-advisories.mdservices/platform/app/components/user-button.tsxservices/platform/app/features/changelog/components/changelog-toast-trigger.tsxservices/platform/app/hooks/use-changelog-notification.tsservices/platform/app/routes/dashboard/$id.tsxservices/platform/convex/schema.tsservices/platform/convex/users/notification_state.tsservices/platform/convex/users/schema.tsservices/platform/messages/de.jsonservices/platform/messages/en.jsonservices/platform/messages/fr.jsonservices/platform/vite-plugins/inject-env.ts
| # Release Notes Format | ||
|
|
||
| Authoritative format for GitHub Release notes on `tale-project/tale`. The `/release` slash command drafts notes against this spec, and the in-product "What's new" link takes users directly to these releases. | ||
|
|
||
| ## Why this spec exists |
There was a problem hiding this comment.
Add the required Mintlify frontmatter and normalize the H1.
This new doc starts without the mandatory title/description frontmatter, and the H1 is still in title case.
Suggested fix
+---
+title: Release notes format
+description: Authoritative format for GitHub release notes and the sections each release must include.
+---
+
-# Release Notes Format
+# Release notes formatAs per coding guidelines, ALWAYS add Mintlify frontmatter (title and description) to every doc file and USE sentence case for headings in documentation.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # Release Notes Format | |
| Authoritative format for GitHub Release notes on `tale-project/tale`. The `/release` slash command drafts notes against this spec, and the in-product "What's new" link takes users directly to these releases. | |
| ## Why this spec exists | |
| --- | |
| title: Release notes format | |
| description: Authoritative format for GitHub release notes and the sections each release must include. | |
| --- | |
| # Release notes format | |
| Authoritative format for GitHub Release notes on `tale-project/tale`. The `/release` slash command drafts notes against this spec, and the in-product "What's new" link takes users directly to these releases. | |
| ## Why this spec exists |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/release-notes-format.md` around lines 1 - 5, This doc is missing the
required Mintlify frontmatter (title and description) and the H1 is in Title
Case; add YAML frontmatter at the top with a title and description (the
Mintlify-required "title" and "description" keys) and change the H1 "# Release
Notes Format" to sentence case (e.g., "# Release notes format") so headings
follow the sentence case guideline; ensure the frontmatter title matches the
document title (use the sentence-cased form) and save.
| # Security Advisory Process | ||
|
|
||
| How Tale coordinates, files, and publishes security-relevant fixes. |
There was a problem hiding this comment.
Add the required Mintlify frontmatter and normalize the H1.
This new doc is also missing the mandatory title/description frontmatter, and the H1 should be sentence case.
Suggested fix
+---
+title: Security advisory process
+description: How Tale files, coordinates, and publishes security advisories for patched releases.
+---
+
-# Security Advisory Process
+# Security advisory processAs per coding guidelines, ALWAYS add Mintlify frontmatter (title and description) to every doc file and USE sentence case for headings in documentation.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/security-advisories.md` around lines 1 - 3, Add the required Mintlify
frontmatter to the top of this doc by inserting a YAML block containing at
minimum title and description (e.g., title: "Security advisory process" and a
short description) and change the existing H1 "# Security Advisory Process" to
sentence case ("# Security advisory process") so the heading and metadata follow
the project's documentation guidelines; ensure the frontmatter appears before
any content and the H1 text matches the title casing convention.
| {hasUnseenVersion && ( | ||
| <span | ||
| className="absolute -top-0.5 -right-0.5 size-2 rounded-full bg-red-500" | ||
| aria-hidden="true" | ||
| /> |
There was a problem hiding this comment.
Expose unseen-update state with a non-visual cue on the trigger.
The red dot is visual-only (aria-hidden="true"), so assistive tech users won’t get equivalent status from the trigger.
♿ Suggested fix
<div className="relative">
<UserCircle className="text-muted-foreground size-5 shrink-0" />
{hasUnseenVersion && (
- <span
- className="absolute -top-0.5 -right-0.5 size-2 rounded-full bg-red-500"
- aria-hidden="true"
- />
+ <>
+ <span
+ className="absolute -top-0.5 -right-0.5 size-2 rounded-full bg-red-500"
+ aria-hidden="true"
+ />
+ <span className="sr-only">{t('userButton.whatsNew')}</span>
+ </>
)}
</div>As per coding guidelines, **/*.{ts,tsx,css} says “AVOID using color alone to convey information” and **/*.{ts,tsx} requires accessibility Level AA.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| {hasUnseenVersion && ( | |
| <span | |
| className="absolute -top-0.5 -right-0.5 size-2 rounded-full bg-red-500" | |
| aria-hidden="true" | |
| /> | |
| {hasUnseenVersion && ( | |
| <> | |
| <span | |
| className="absolute -top-0.5 -right-0.5 size-2 rounded-full bg-red-500" | |
| aria-hidden="true" | |
| /> | |
| <span className="sr-only">{t('userButton.whatsNew')}</span> | |
| </> | |
| )} |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@services/platform/app/components/user-button.tsx` around lines 393 - 397, The
red-dot indicator (hasUnseenVersion) is aria-hidden and therefore invisible to
assistive tech; update the UserButton trigger so the unseen-update state is
exposed non-visually by adding an accessible cue when hasUnseenVersion is true
(for example, append a short phrase to the trigger's aria-label like "new update
available" or add a visually-hidden sr-only text node and/or an aria-live region
that announces the update). Modify the component that renders hasUnseenVersion
within UserButton (the span with the red-dot) to keep the visual dot but also
provide the non-visual announcement via aria-label/aria-describedby or sr-only
text tied to the trigger so screen readers receive the same status without
changing the visual appearance.
| "toast": { | ||
| "title": "Aktualisiert auf v{version}", | ||
| "description": "Sehen Sie sich die Änderungen in dieser Version an.", | ||
| "action": "Anzeigen" |
There was a problem hiding this comment.
Keep the German voice consistent.
"Sehen Sie sich ..." switches to formal Sie, while the surrounding product copy in this locale uses du/dein. That will feel inconsistent in the same user-menu/toast flow.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@services/platform/messages/de.json` around lines 4499 - 4502, The
"toast.description" string uses formal "Sie" while the locale uses informal
voice; update the value for the "description" key under "toast" to the informal
form (e.g., "Sieh dir die Änderungen in dieser Version an." or "Schau dir die
Änderungen in dieser Version an.") so the "toast" object ("title",
"description", "action") remains consistently in the du/dein voice.
Summary
userNotificationStatetable (independent of Better Auth's auto-generated schema, following theuserPasswordMetadataprecedent) that trackslastToastedVersionandlastSeenChangelogVersionseparately — the toast never repeats for a version, while the red dot persists until the user explicitly views the release.docs/release-notes-format.mddefines an 8-section classification spec (adding 🔒 Security and 🤖 Model & Provider categories) that backs the release notes the "What's new" link points to;docs/security-advisories.mddocuments the severity matrix and timeline for GitHub Security Advisories.TALE_VERSIONso the client can readwindow.__ENV__.TALE_VERSION(production server path already did this).Design notes
userId.Out of scope
Test plan
TALE_VERSION=1.5.0 npm run dev --workspace=@tale/platformand log in as an existing user → toast "Upgraded to v1.5.0" fires, red dot appears on avatar and beside "What's new" in dropdown, ConvexuserNotificationStaterow getslastToastedVersion: "1.5.0",lastSeenChangelogVersionemptygithub.com/tale-project/tale/releases/tag/v1.5.0, both red dots disappear, row getslastSeenChangelogVersion: "1.5.0"TALE_VERSION=1.6.0, restart dev, refresh → toast fires for v1.6.0, red dots returnTALE_VERSION→ no toast, no red dot, no "What's new" line in dropdown (graceful degradation)TALE_VERSIONto older) → no toast, no red dot (isNewerreturns false)labelprop shows red dot on avatar and same dropdown contentnpm run typecheck --workspace=@tale/platformpassesnpm run lint --workspace=@tale/platformpassesSummary by CodeRabbit
Release Notes