Inspiration

Three signals that arrived at the same time. The 2026 CHI modqueue study (arXiv 2509.07314) reported that 84% of moderators leave the queue to re-check user history and reread rules on every nontrivial decision. Modmail floods after every controversial action because removal explanations are templated and impersonal. And subreddit wiki rules go stale: enforcement evolves, the wording does not, and new mods inherit ambiguity that nobody audits. We wanted one app, not three. The shared piece is the rules themselves. If the app reads them once from the wiki, three different moderator pain points collapse into the same infrastructure: a per-rule retrieval layer, an LLM that always cites verbatim, and explicit human approval before any visible action.

What it does

ModSense reads your subreddit's wiki rules and helps moderators apply them consistently. Three integrated capabilities run on one shared infrastructure:

  1. Contextual triage. Every new comment, every new post, and every AutoMod-filtered item is evaluated against the verbatim text of the sub's rules. Gemini returns a structured verdict with a cited rule, a verbatim quote, brief reasoning, and a confidence level. When confidence meets the configured threshold, ModSense writes a Reddit ModNote on the user. Never auto-removes.
  2. Restorative removal. A moderator menu action opens a form pre-populated by Gemini with the cited rule (verbatim from wiki), a personalized explanation referencing the user's specific comment, and a short compliant rewrite. The mod can edit any field, then submit. The comment is removed and a respectful modmail is sent with an appeal CTA.
  3. Rule-drift audit. Every Monday at 06:00 UTC (or on demand from the subreddit menu), ModSense analyzes the past seven days of removals against each rule and writes a markdown report to wiki/modsense/drift-report. The report flags alignment or drift, names specific gaps, and proposes concrete wording changes.

How we built it

  • TypeScript on Devvit Web 0.13, the new Hono based server runtime that shipped the day before this hackathon ended. Vite bundles the server to a single CommonJS file. Redis stores parsed rules plus a sorted set of removals keyed by timestamp. Triggers (onAppInstall, onAutomoderatorFilterPost, onAutomoderatorFilterComment, onCommentSubmit, onPostSubmit, onModAction) route to internal POST endpoints. The scheduler runs the weekly drift audit every Monday at 06:00 UTC.
  • Gemini 2.5 Flash drives the LLM work with responseSchema for structured output and thinkingBudget: 0 so the model emits valid JSON without burning the output budget on chain of thought. gemini-embedding-001 computes rule embeddings stored in a Redis hash, reserved for future top-k retrieval once a sub has thirty plus rules. The drift audit batches all rules into a single Gemini call, both to stay inside Devvit's per-installation fetch quota and because the holistic prompt produces better cross-rule comparison.
  • Anti-hallucination: rule quotes are pulled from cached wiki content by id, never generated. The system prompt forbids paraphrasing. Restorative removal opens a moderator-editable form before any action. Triage only writes ModNotes, never auto-removes.

Challenges we ran into

  • Devvit 0.13.0 shipped one day before the hackathon ended. The release restructured the project layout from Blocks to Hono on Vite. Official docs were a mix of 0.12 and 0.13 conventions during the migration window. We resolved this by reading the type definitions in node_modules directly: @devvit/shared-types/schemas/config-file.v1.d.ts is the authoritative source for what devvit.json accepts, and the bundled JSON schema catches every typo at upload time.
  • Gemini's "thinking" mode silently consumed the output token budget, producing truncated JSON that failed our schema parse. Setting thinkingBudget: 0 in generationConfig fixed it. We also added finishReason surfacing to error messages so future failures are obvious.
  • text-embedding-004 returned 404 on v1beta. The current model name is gemini-embedding-001. One letter difference, fifteen minutes lost. Trust the error response, not the older documentation.
  • Devvit's per-installation HTTP fetch quota throttled the drift audit when we made one Gemini call per rule. Batching all rule analyses into a single call solved it cleanly.
  • Reddit's settings JSON schema has two string types. GlobalStringSetting allows isSecret: true, but the subreddit-scoped StringSetting does not. We wanted the Gemini API key to be per-subreddit so each mod team brings their own free key and pays their own quota, but isSecret is unavailable in that scope. We documented the trade off (the key is visible to all mods of the sub, like a shared community resource) rather than centralizing cost on the app developer.
  • New private subs default to wiki disabled. The API created our wiki pages successfully but the browser refused to render them. We added a clear install-time log instructing mods to enable wiki, plus updateWikiPageSettings({ listed: true }) so the pages appear in the index as soon as wiki is on.

Accomplishments that we're proud of

Three working pillars on shared infrastructure rather than three separate apps. The rule-drift audit specifically is net-new functionality to the Reddit ecosystem. No PRAW bot and no existing Devvit app audits whether wiki rules still describe enforcement reality. The first real run produced output like this:

"This rule shows drift. The rule text focuses on personal attacks, slurs, and harassment, but the enforcement includes general negative sentiment towards the subreddit itself. To align the rule with enforcement, consider adding a clause about maintaining a positive or constructive tone regarding the community."

  • That is meta-moderation, surfaced automatically from a week of mod actions, with a concrete wording suggestion attached.
  • Verbatim rule citations that cannot be hallucinated. The system prompt forbids paraphrasing, the cached wiki content is the source of truth, and the rule id makes the lookup deterministic. We watched Gemini quote rules word for word across triage, restorative removal, and drift audit. Never invented one.
  • Per-subreddit Gemini key. Each mod team brings their own free aistudio.google.com key, so cost stays distributed and any installer can verify the app independently without depending on the developer's quota.
  • Reliable triggers, idempotent handlers, and graceful degradation. Every trigger uses a Redis NX-set dedupe key with TTL. Gemini calls retry with backoff on 429, 503, and 504. The drift audit falls back to placeholder text per rule when the LLM is unavailable rather than failing the whole report.
  • End to end type safety. Zero TypeScript errors at build time. Trigger payload types come from @devvit/web/shared, Reddit API types from @devvit/reddit, form field types from @devvit/shared-types, and the structured Gemini responses are typed via responseSchema.

What's next for ModSense

  • Use the cached embeddings. Once a sub has thirty plus rules, we want top-k semantic retrieval before the Gemini call so we only pass the most relevant rules into the prompt. The data is already in Redis. The pipeline is built. The query path is the missing piece.
  • Cross-sub ban evasion fingerprinting on the same embedding store. Stylometric similarity between a banned user's recent comments and a new account's first ten comments, surfaced as a mod-only suspicion score, never auto-ban.
  • Submit for Reddit's app review and publish to the App Directory so any subreddit can install ModSense the same way they install Mod Toolbox today.

Built With

Share this project:

Updates