The plugin is covered by four CI workflows (see .github/workflows/):
| Workflow | What it runs |
|---|---|
| Plugin Tests | rspec — plugin_spec.rb, spec/lib, spec/requests (Guardian, permissions, the message endpoints, serialization, reply-approval enforcement, moderator-note notifications, the prompt trust-level caps, and the first-post checklist) |
| Category Save Tests | rspec — spec/saves (category-save regression suite) |
| QUnit Tests | test/javascripts/unit — the pure-logic matrix suites (precheck-prompt resolution incl. trust-level caps, footer message, first-post checklist gate) |
| Frontend System Tests | spec/system — Capybara/Playwright end-to-end specs; uploads the screenshots below as the ui-screenshots artifact |
Every screenshot below is produced by a passing system spec, so they double as a visual regression record. Each one is explained under its own heading.
These features are covered by request specs and QUnit unit tests; their end-to-end screenshots are not yet part of the gallery below:
| Feature | Tests |
|---|---|
| Moderator-note pop-up notifications | spec/requests/mod_messages_spec.rb — notifications go to other staff, not the actor or regular users |
| Clickable links in the prompt dialogs | exercised by the prompt system specs |
| Prompt trust-level caps | spec/requests/mod_messages_spec.rb (persistence, clamping) + test/javascripts/unit/precheck-prompt-test.js (trust-level matrix) |
| First-post checklist | spec/requests/checklist_spec.rb (read/edit/accept gating, version bump, trust-level cap, button label, serializer audience), test/javascripts/unit/first-post-checklist-test.js (gate matrix), and the system spec below |
| Per-topic prompt checklist | spec/requests/topic_prompt_checklist_spec.rb (staff CRUD with mode/statement/frequency/max_tl, /checklist/owed?topic_id=…, per-topic-per-user acceptance with clamp, version bump, topic_view serializer, statement-mode payload, frequency=every_reply always returns, max_tl cap filters higher-TL non-staff, legacy reply-prompt pre-fill + migration) and spec/system/topic_prompt_checklist_spec.rb (staff opens the wrench-menu "Prompt Checklist", adds items, saves; another user replies and is prompted; accepts; second reply is NOT prompted; version-bump re-prompts; statement-mode modal renders without checkboxes; frequency: every_reply re-prompts on the second reply; max_tl cap skips higher-TL users; Moderator Actions modal no longer carries the Before-reply prompt section) — screenshots 171–186 |
| Moderator-notes header pip & title prefix | spec/requests/mod_note_header_indicators_spec.rb (mod_note_unread_count across states + MessageBus publishes on note set/reply and on notes-feed/seen), test/javascripts/unit/mod-note-unread-title-test.js (pure applyUnreadPrefix / stripUnreadPrefix matrix), and spec/system/mod_note_header_indicators_spec.rb (header pip + (N) title prefix render for staff with unread notes, clear after the shield tab is opened, never render for a regular user) — screenshots 190-192 |
The starting point — a regular topic as a moderator sees it, before any moderator message is set.
Opening the topic admin menu reveals the Moderator Actions button that staff use to manage this plugin's per-topic content.
The modal opened from that menu, before anything is entered.
A moderator types the pinned footer message into the modal.
The modal with both the footer message and the before-reply prompt entered.
After saving, the footer message appears in a banner at the bottom of the topic.
A topic loaded with a footer message already set — the state before an edit.
Re-opening the modal pre-fills it with the topic's current values, ready to edit.
The modal with the footer message changed to a new value.
The footer banner now shows the edited message — updated live, without a reload.
A topic with a footer message, about to be cleared.
The moderator empties the footer message field in the modal.
After saving the empty field, the footer banner is gone.
A non-staff member viewing the topic sees the footer banner just the same.
HTML in the footer message (here, bold text) is rendered — it is admin/moderator-trusted content.
The footer message keeps showing even after the topic is closed.
The Before-new-topic prompt field added to a category's Settings tab.
A moderator types the prompt for that category.
The prompt saved, with the green "Saved" confirmation.
A user starts a new topic in the category that has a prompt set.
On submit, the moderator's message appears as a confirmation before the topic posts.
Choosing "Go back" returns to the composer with the content intact.
Choosing "Post anyway" submits the topic as normal.
In a category that has no prompt set, a new topic posts directly with no dialog.
A regular user opens a topic that has a before-reply prompt set.
The user opens the reply composer and writes a reply.
On submit, the moderator's reply prompt appears as a confirmation.
"Go back" keeps the composer open with the reply intact.
"Post anyway" submits the reply.
A topic with no reply prompt configured — replies post directly.
The before-reply dialog shown in the full reply flow.
The reply is posted once the user confirms.
A post's admin (moderator actions) menu, showing the Pin to Bottom button.
After pinning, the post is shown at the bottom of the topic.
A topic showing both a moderator footer message and a pinned post.
A pinned post in place, about to be unpinned.
After "Unpin from Bottom", the pinned copy is gone.
The pinned post shown at the end of the post stream as a regular-looking post with a pin badge and a jump-to-original button.
When the pinned post is already the last post, only the in-stream pin badge shows — no duplicate copy.
A non-last post gets the in-stream badge plus the copy at the bottom.
Same behaviour for the third-to-last post — badge plus bottom copy.
And for a post well up the thread (tenth-to-last) — badge plus bottom copy.
The Require approval for replies checkbox ticked in the topic modal. While on, replies route to the review queue.
A staff-only note shown like a post — the moderator's avatar and name — never visible to regular users.
The private note hidden from a regular user
The same topic as a non-staff user: the private note is absent (it is not even sent to non-staff).
The note shows a relative timestamp ("how long ago") and a small Reply button.
Clicking Reply opens a box for a moderator to add to the note thread.
The reply, posted — the note is now a staff-only thread, each entry with its author and time.
Staff get a shield tab in the user menu, next to the bell, with an unread-count badge.
Opening the tab lists the moderator notes across topics — each entry shows the topic and the note, and links to the topic.
A staff-only shield pip rendered in the page header, carrying the same unread count as the shield tab. Visible whenever the user menu is closed and mod_note_unread_count > 0. Clicking it opens the menu on the shield tab.
document.title is prefixed with the unread count, mirroring the bell. The prefix is added by wrapping the global title setter so route transitions never drop it.
Opening the shield tab marks the feed as seen (POST /notes-feed/seen updates mod_notes_seen_at and publishes a reset on the dedicated MessageBus channel). The pip and the (N) prefix both disappear without a hard refresh.
The config modal, opened from the First-post checklist link in the sidebar's Community section. With no items yet, it shows the "inactive" notice — nothing is shown to users until at least one item is added.
A moderator has added two checklist items (each with optional link), chosen the trust-level audience, and set custom accept-button text.
After Save checklist, the green "Saved" confirmation appears and the items round-trip back into the editor.
A trust-level-0 user who tries to post for the first time is shown the checklist modal before the post goes through. The accept button is disabled until every box is ticked.
Once the user ticks every item, the accept button (with its staff-set label) becomes enabled.
After the user accepts the checklist, their reply is posted to the topic as normal.
The same user's next post goes straight through — the checklist is shown only until it is accepted once.
With the audience set to "Up to members (TL0 to TL2)", a trust-level-1 user is also shown the checklist.
Each save bumps the checklist version, shown in the editor.
A user who already accepted an older version is shown the checklist again after staff publish a new version.
The editor lists every acceptance — the user, the version they accepted, and when — with a count and a current-version filter.
A staff member's reply composer shows the whisper (eye) toolbar button.
Clicking the whisper button opens a modal to pick which user(s) the whisper goes to.
The moderator has chosen the recipients of the whisper.
With a whisper armed, the composer shows a pill naming the recipients and tints to signal the reply will be a whisper.
The posted whisper renders with a full-width banner listing who it was whispered to.
A targeted user sees the whisper post and its banner in the topic.
A user who is not in the whisper's audience never sees the post.
Any staff member — not just the author or targets — can see every whisper in the topic.
Once staff have whispered to a user, that user's reply composer auto-arms a staff-only whisper-back.
A user's whisper-back posts with a banner showing it is addressed to staff.
A non-staff user who has never been whispered to cannot start a whisper.
The mod_whisper_enabled site setting, on by default.
When the feature is off, a former whisper is visible to everyone like a normal post.
A non-staff member has no topic admin menu and no Moderator Actions button — the moderator features are staff-only.
The main /categories page as a moderator — the plugin's master switch lets staff see and curate every category.
The categories page header as a moderator sees it, with the standard navigation.
The category Settings tab — the home of the per-category new-topic prompt field.
The category General tab as a moderator.
The category Security tab as a moderator.
The topic list inside a category with several topics.
A topic header showing the category badge it belongs to.
A footer message with multiple paragraphs and a markdown link, cooked as Markdown.
A footer message that includes an inline markdown link to external guidelines.
The footer rendered as Discourse's official-notice box, with the shield icon and "Moderator message" label.
The footer message still pinned to the bottom of a topic with many replies.
The Moderator Actions modal with only the footer message field filled in.
The Moderator Actions modal with only the reply-prompt field filled in.
The footer message still rendered on a closed topic, alongside Discourse's "closed" banner.
The Moderator Actions modal with the reply-prompt audience combo-box set to "Up to new (TL0)".
The Moderator Actions modal with the reply-prompt audience combo-box set to "Up to members (TL0-TL2)".
The modal with a multi-line reply prompt — bullet-style guidance plus a URL.
Re-opening the modal shows the previously-saved reply prompt prefilled.
The user-facing reply confirmation dialog with a clickable URL inside its body.
A TL4 leader replying to a topic whose reply prompt is capped at TL1 — no dialog appears, the reply posts directly.
The category Settings live preview rendering a markdown bold plus a linkified URL.
The live preview honouring line breaks in the input.
The new-topic prompt audience combo-box set to "Up to basic (TL0-TL1)".
The new-topic prompt audience combo-box set to "Up to new (TL0)".
Re-opening the category Settings tab shows the previously-saved prompt and its cap.
The live preview area when the prompt field is cleared.
A topic with both a footer message and a bottom-pinned post, viewed by a regular user.
The pin badge added to the original post in the post stream.
The bottom-pinned copy includes a jump-to-original anchor link.
A topic showing both a bottom-pinned post and a staff-only private note.
The post admin menu showing the Pin to Bottom option for a post that is already pinned.
A regular user's view of a topic that has both a bottom-pinned post and a footer message.
The Moderator Actions modal opened on a fresh topic — the approval checkbox is unchecked.
The Moderator Actions modal with the approval checkbox just ticked.
The same modal after toggling the checkbox off again.
Re-opening the modal on a topic that already requires approval — the checkbox is pre-ticked.
The modal with the approval checkbox ticked plus the footer/reply-prompt fields filled in.
A regular user viewing a topic that requires reply approval — no extra UI is surfaced to them.
A private moderator note configured to render at the top of the topic.
A private moderator note configured to render at the bottom of the topic.
A private note plus a three-message staff-only reply thread.
A note reply showing its inline edit and delete buttons.
The note's reply composer open with text being drafted.
A regular user viewing a topic that has a private note — no DOM node for the note exists.
An anonymous visitor viewing the same topic — the private note is absent.
The user menu shield tab showing an unread badge for newly-arrived moderator notes.
The moderator-notes panel listing several notes from across the forum.
The notes panel scrolled to a single-entry state.
The notes panel as it looks after mod_notes_seen_at has been recorded.
The notes panel when there are no moderator notes anywhere on the forum.
Clicking a note entry lands the staff member on its topic.
The checklist editor's inactive notice when no items have been configured yet.
The editor populated with a checklist whose accept-button label has been overridden.
The editor with the audience combo-box set to "Up to basic (TL0-TL1)".
The user-facing checklist modal showing its "Last updated" line.
The user-facing checklist modal with the staff-set custom accept-button label.
The acceptance audit log populated with five entries across multiple versions and users.
The editor showing a targeted checklist alongside the global one.
A TL2 user prompted with the checklist because the audience cap is TL2.
A staff whisper post with a single targeted user in its banner.
A staff whisper post with three targeted users listed in its banner.
A staff-only whisper post (no user targets) — banner indicates the whisper is to staff.
A whisper targeted at a group, with the group name shown in the banner.
The composer's armed-whisper pill showing a single chosen recipient.
The whisper add-participant modal with a user picked, ready to confirm.
A non-participant user viewing the topic — the whisper post is absent.
A whisper recipient's view of the topic — the banner is visible.















































































































































