Mod Triage Board — Project Story
About the project
Mod Triage Board is a Reddit Devvit mod tool that turns the modqueue into a shared moderation workspace. Moderators see items in Unclaimed, In Progress, and Resolved columns, can claim ownership, invite collaborator mods into active investigations, leave internal notes, and skim a lightweight activity history—so two mods don’t burn time on the same report, and handoffs stay visible.
It is deliberately not a full ticketing system or a second Reddit. The goal is narrower: less duplicate moderator effort on the reports you were already going to open anyway.
What inspired it
Moderation queues are full of coordination problems disguised as content problems.
“Is anyone on this?”, “I’m halfway through—don’t nuke it”, “Who resolved this and why?” Native mod tools are strong at actions (remove, approve, modmail) but thin at shared state across a team.
The spark was treating the modqueue as a triage lane: one visible owner, optional partner, clear state, and a place for mod-only context without turning the sub into Jira.
Ship something moderators could demo in one minute and use for real the next.
Hackathon scope made that bar credible—and collaborators made it human: a second mod can join an active item without hijacking ownership, then resolve alongside the owner when they’re ready.
How we built it
Platform and shape
- Runtime: Devvit Web — server and custom post webview are first-class, no ad-hoc Node servers.
- Server: Hono mounted the way the Devvit Web template expects (
createServer/getServerPortfrom@devvit/web/server), with routes under/api(webview) and/internal(menu, triggers). - Client: React 19 + TypeScript + Tailwind, built with Vite; the board is a single tall custom post surface tuned for touch and small viewports (safe areas, scroll behavior).
- Persistence: Devvit Redis stores per-item JSON (workflow state, owner, collaborator, notes, mod activity history). The queue source of truth remains Reddit’s modqueue—we don’t fork reports into a private database for the MVP.
Workflow and rules
State machine is intentionally small: unclaimed → in_progress → resolved, plus release and reopen. Resolve on an in-progress item is restricted to the owner or an assigned collaborator so random mods can’t close someone else’s active investigation by accident.
SLA as a visual nudge
Card urgency increases visually as items age past a configurable SLA threshold. The goal is to help moderators notice neglected items—not to enforce strict operational guarantees. Stepped styling (warning → overdue-style) is a hint, not a contract: mods still decide what “critical” means in context.
Dev vs production
Menu entries for seeding a test modqueue live only in a generated playtest manifest (devvit.playtest.json merged from devvit.seed-menu.partial.json), while devvit.json stays clean for devvit upload. Seed/clear handlers stay server-guarded (*_dev subreddits or an explicit env flag) so production subs can’t be spammed even if someone probed endpoints.
What we learned
Devvit rewards boring architecture. Fighting the template (extra HTTP servers, import-time side effects) costs more than it pays. Keeping Redis writes in handlers and the install trigger minimal made playtests reliable.
The webview is a real browser—and also not one. Clipboard + host toasts behave differently than
window.openor in-frame navigation toreddit.com. On embedded Android, opening Reddit inside Reddit is fragile; copy permalink plus a short preview in the drawer beat “open here” for MVP trust.Coordination UX is mostly information design. Badges for owner and collaborator, a drawer for permalink + preview + notes, and a resolved column that doesn’t pretend every old item is equally important (
partitionResolvedItems/ date grouping) did more for clarity than new features would have.TypeScript at the boundary. Shared types for
ModBoardItemand queue payloads kept the webview and server aligned; small pure helpers (modboardDomain) stayed easy to test with Vitest.
Challenges we faced
| Challenge | What we tried | Outcome |
|---|---|---|
| Embedded client navigation | Opening Reddit URLs from the webview | Dropped for MVP; copy link + user opens in their browser |
| Duplicate “Close” on mobile | Header + sticky footer both visible on small screens | Hide header close below sm; keep sticky close for thumb reach |
| Resolve permissions | Anyone with mod perms could resolve | Owner or collaborator only for in-progress owned items; API returns 400 on violation |
| Manifest dev menu in prod | Seed items in devvit.json |
Split manifest: prod devvit.json, merged playtest config for npm run dev |
| Install / trigger flakiness | Heavy OnAppInstall |
Kept trigger minimal (JSON success only), per Devvit debugging guidance |
Where it could go next
Deeper in-board context (full body, report reasons from API) was prototyped and rolled back when it cluttered the hackathon story—the collaboration layer stayed the hero. A future pass could bring that back as a single focused panel, not two surfaces.
Closing
Mod Triage Board is a bet that visibility beats sophistication for small teams working the same queue: who owns it, who’s collaborating, how long it’s been open, and what the last mod did. If that saves one duplicate pass per shift, it earned its place on the menu.
Built With
- devvit
- html
- node.js
- reactui
- redis
- tailwind
- tsx
- typescript
- vite7
Log in or sign up for Devpost to join the conversation.