Changelog System
The changelog system gives your app a public-facing update feed. Users can browse releases, like entries, and track what they’ve read. Admins manage entries through the admin DataTable UI or directly via Claude Code using the built-in MCP tools.
v4 note: The context was renamed from
ChangelogUpdates(v3) toPetalPro.Changelogin v4. The primary schema is nowPetalPro.Changelog.Update.
What’s included
-
Public changelog page at
/changelog(also accessible from the sidebar) -
Per-entry slug URLs (
/changelog/:slug) with SEO meta descriptions - Like/unlike on individual entries (authenticated users)
- Read tracking — the sidebar notification bell shows unread count
- Optional site-wide banner that can be attached to any published entry
- Notification and email dispatch on publish (opt-in per entry)
-
Admin DataTable at
/admin/changelogwith full Flop pagination, sorting, and filtering - MCP tools for managing entries via Claude Code
Schemas
PetalPro.Changelog.Update
The primary content record:
| Field | Type | Notes |
|---|---|---|
title |
string |
Required. Auto-generates slug. |
slug |
string | URL-safe, unique, auto-derived from title. |
summary |
string | Short teaser shown in lists and meta descriptions. |
content |
string | Full content as EditorJS JSON. |
status |
enum |
:draft, :published, :archived |
category |
enum |
:new, :improve, :fix |
published_at |
utc_datetime | Set automatically on publish. |
show_banner |
boolean | Promotes this entry to the site-wide banner. |
banner_style |
enum |
:info, :success, :warning, :error |
banner_cta_text |
string | Button label for the banner. |
banner_cta_url |
string | Button destination for the banner. |
banner_expires_at |
utc_datetime | Auto-hides banner after this time. |
send_notification |
boolean | Send in-app notification to all users on publish. |
send_email |
boolean | Send email to all users on publish. |
PetalPro.Changelog.Like
Tracks which users liked which updates. Unique constraint on [:changelog_update_id, :user_id].
PetalPro.Changelog.Read
Tracks whether a user has read an update and how (read_via: :changelog_page or :banner). Used to power the unread count in the notification bell.
Context module
PetalPro.Changelog is the public interface. Key functions:
# Admin: list all updates (optional status filter)
Changelog.list_changelog_updates()
Changelog.list_changelog_updates(%{status: :draft})
# Public: list published updates with like counts and user like status
Changelog.list_public_changelog_updates(%{}, current_user)
# Fetch by slug (raises if not found or not published)
Changelog.get_published_changelog_update_by_slug!("my-update-slug")
# CRUD
Changelog.create_changelog_update(attrs)
Changelog.update_changelog_update(update, attrs)
Changelog.publish_changelog_update(update, published_by_user)
Changelog.unpublish_changelog_update(update)
Changelog.archive_changelog_update(update)
Changelog.delete_changelog_update(update)
# Read tracking
Changelog.mark_as_read(update, user, :changelog_page)
Changelog.get_unread_count(user)
# Likes
Changelog.toggle_like(update, user)
Changelog.get_like_count(update)
# Banner
Changelog.get_active_banner()
Publishing dispatches an Oban job (ChangelogNotificationWorker) if send_notification or send_email is set on the entry. Only one banner can be active at a time — publishing a second banner-enabled entry returns a validation error.
Admin DataTable
PetalProWeb.AdminChangelogLive at /admin/changelog uses the DataTable component backed by Flop. Features:
- Sort by title, category, status, published date
- Filter by status
- Inline publish/unpublish/archive/delete actions
- Per-entry read count and like count stats
- Link to the public entry
Public page and SEO
The public changelog lives at /changelog. Individual entries are at /changelog/:slug. The summary field populates the <meta name="description"> tag for each entry page, so write it like you mean it — search engines and unfurl previews both use it.
MCP tools (Claude Code integration)
Petal Pro exposes three changelog tools via its admin MCP server at POST /api/mcp. These are available to Claude Code and Claude Desktop when connected with a bearer token.
list_changelog_updates
Lists updates with optional status filter and limit.
list_changelog_updates(status: "draft", limit: 10)
Returns: id, title, slug, status, category, show_banner, published_at, author.
manage_changelog
Performs a single action on an existing update identified by slug or UUID.
manage_changelog(identifier: "my-update-slug", action: "get")
manage_changelog(identifier: "my-update-slug", action: "publish", publisher_email: "admin@example.com")
manage_changelog(identifier: "my-update-slug", action: "unpublish")
manage_changelog(identifier: "my-update-slug", action: "archive")
manage_changelog(identifier: "my-update-slug", action: "delete")
manage_changelog(identifier: "my-update-slug", action: "update", title: "New title", category: "fix")
The get action returns full details including read count, read percentage, and like count. The publish action requires publisher_email — it must belong to an admin user.
create_changelog_update
Creates a new entry in :draft status.
create_changelog_update(
title: "Better dark mode",
content: "{\"blocks\":[{\"type\":\"paragraph\",\"data\":{\"text\":\"We overhauled dark mode...\"}}]}",
category: "improve",
summary: "Dark mode now respects system preference on first visit.",
send_notification: true
)
Content must be an EditorJS JSON string. category defaults to "new". author_email defaults to the first admin user if omitted.
Creating a changelog entry from Claude Code
> Use the create_changelog_update tool to draft a changelog entry titled "Faster search"
in the "improve" category. Summary: "Search results now load 3x faster."
Then publish it using my email: matt@example.com
Claude will call create_changelog_update to draft the entry, then manage_changelog with action: "publish" to go live. The whole flow takes seconds without touching the admin UI.