Skip to content

feat(platform): in-product changelog notification for non-admin users#1576

Merged
larryro merged 3 commits into
mainfrom
feat/changelog-notification
Apr 19, 2026
Merged

feat(platform): in-product changelog notification for non-admin users#1576
larryro merged 3 commits into
mainfrom
feat/changelog-notification

Conversation

@larryro
Copy link
Copy Markdown
Collaborator

@larryro larryro commented Apr 19, 2026

Summary

  • Close the gap for operators and end users who don't watch GitHub releases — surface upgrade notices inside the product via a one-shot toast, a red-dot indicator on the UserButton, and a "Current version · What's new" link in the user dropdown that opens the matching GitHub release.
  • Add a per-user userNotificationState table (independent of Better Auth's auto-generated schema, following the userPasswordMetadata precedent) that tracks lastToastedVersion and lastSeenChangelogVersion separately — the toast never repeats for a version, while the red dot persists until the user explicitly views the release.
  • Introduce companion process docs: docs/release-notes-format.md defines 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.md documents the severity matrix and timeline for GitHub Security Advisories.
  • Dev-mode vite plugin now injects TALE_VERSION so the client can read window.__ENV__.TALE_VERSION (production server path already did this).

Design notes

  • Entry point is in the user dropdown, not a standalone page. Version is deployment-wide so rendering changelog content inside the platform would just duplicate GitHub. The UserButton header shows "Current version: v{version} · What's new" where the version tells users which version they're on and the link is the action.
  • No role filtering — all logged-in users see the same indicator. Platform changes affect everyone, not just admins.
  • Per-user, not per-org — switching orgs does not re-trigger the toast. Version state lives on the user, tracked via userId.
  • No one-shot migration: existing users will see the toast exactly once on the release that ships this feature. That is accurate — the feature itself is what is new.

Out of scope

  • In-product changelog rendering page, email notifications, NotificationBell integration, role-based filtering. All deferred until there's concrete demand.

Test plan

  • TALE_VERSION=1.5.0 npm run dev --workspace=@tale/platform and 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, Convex userNotificationState row gets lastToastedVersion: "1.5.0", lastSeenChangelogVersion empty
  • Refresh → toast does NOT re-fire, red dot persists
  • Click "What's new" link in dropdown → new tab opens github.com/tale-project/tale/releases/tag/v1.5.0, both red dots disappear, row gets lastSeenChangelogVersion: "1.5.0"
  • Bump to TALE_VERSION=1.6.0, restart dev, refresh → toast fires for v1.6.0, red dots return
  • Close toast (×) without clicking View → red dots remain, toast does not re-fire on refresh
  • Switch between orgs (A has multiple memberships) → no re-trigger; state is per-user
  • Unset TALE_VERSION → no toast, no red dot, no "What's new" line in dropdown (graceful degradation)
  • Downgrade (set TALE_VERSION to older) → no toast, no red dot (isNewer returns false)
  • Mobile navigation → UserButton with label prop shows red dot on avatar and same dropdown content
  • npm run typecheck --workspace=@tale/platform passes
  • npm run lint --workspace=@tale/platform passes

Summary by CodeRabbit

Release Notes

  • New Features
    • Version information is now displayed in the user menu with a "What's new" link for quick access to release notes
    • Visual notification badges indicate when new versions are available
    • Toast notifications automatically prompt users to review changes in newly released versions with direct access to release notes

larryro added 3 commits April 19, 2026 16:29
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.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 19, 2026

📝 Walkthrough

Walkthrough

This 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)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(platform): in-product changelog notification for non-admin users' directly and accurately summarizes the main change: introducing in-product changelog notifications for non-admin users via toast, red-dot indicator, and dropdown link.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/changelog-notification

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between 5f2264e and c216049.

⛔ Files ignored due to path filters (1)
  • services/platform/convex/_generated/api.d.ts is excluded by !**/_generated/**
📒 Files selected for processing (13)
  • docs/release-notes-format.md
  • docs/security-advisories.md
  • services/platform/app/components/user-button.tsx
  • services/platform/app/features/changelog/components/changelog-toast-trigger.tsx
  • services/platform/app/hooks/use-changelog-notification.ts
  • services/platform/app/routes/dashboard/$id.tsx
  • services/platform/convex/schema.ts
  • services/platform/convex/users/notification_state.ts
  • services/platform/convex/users/schema.ts
  • services/platform/messages/de.json
  • services/platform/messages/en.json
  • services/platform/messages/fr.json
  • services/platform/vite-plugins/inject-env.ts

Comment on lines +1 to +5
# 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
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

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 format

As 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.

Suggested change
# 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.

Comment on lines +1 to +3
# Security Advisory Process

How Tale coordinates, files, and publishes security-relevant fixes.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

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 process

As 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.

Comment on lines +393 to +397
{hasUnseenVersion && (
<span
className="absolute -top-0.5 -right-0.5 size-2 rounded-full bg-red-500"
aria-hidden="true"
/>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
{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.

Comment on lines +4499 to +4502
"toast": {
"title": "Aktualisiert auf v{version}",
"description": "Sehen Sie sich die Änderungen in dieser Version an.",
"action": "Anzeigen"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

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.

@larryro larryro merged commit 23030e7 into main Apr 19, 2026
26 checks passed
@larryro larryro deleted the feat/changelog-notification branch April 19, 2026 08:56
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