--- name: investigate-forum description: Triage the NewsBlur support forum (forum.newsblur.com). Use when the user runs /investigate-forum, pastes a forum.newsblur.com topic URL, or says "check the forum", "triage the forum", "what's waiting on the forum", "look at this forum post". Finds topics whose last post is not from Sam, investigates each one newest first, and either opens a fix PR in a worktree with a before/after and a drafted reply (tier 1), drafts a reply only, or interviews Sam with AskUserQuestion when a decision or a production change is needed (tier 2). Read-only against production. Never posts to the forum and never merges. --- # Investigate Forum Sam runs this about once a day and comes back every few hours to approve things. The job: for each forum topic still waiting on Sam, do the investigation he would do, get a fix as far as a reviewed PR when it is safe to, and leave a reply he can paste. ## Ground rules These hold for every topic, every run. Do not reinterpret them mid-run. 1. **At most three topics per run, one at a time, newest activity first.** The queue is cut at three (`--limit`, default 3); the rest wait for the next run so open PRs get merged before more pile up. Finish a topic (state recorded) before opening the next. No parallel subagents across topics. A subagent may help inside one topic (for example, a second opinion on a fix) but the loop stays sequential. 2. **Production is read-only.** Pre-authorized: Django ORM reads, mongoengine reads, redis read commands (`GET`, `HGETALL`, `ZRANGE`, `SMEMBERS`, `KEYS` on a narrow pattern, `INFO`), log greps, `sentry-cli issues list`, Play Console crash reads. Never `.save()`, `.update()`, `.delete()`, `.create()`, bulk writes, redis writes, container restarts, deploys, or `make deploy`/`make celery`. Anything that changes production state is tier 2 and needs an explicit yes through AskUserQuestion, then run exactly the approved command and nothing more. 3. **Never post to the forum.** Replies are drafted for Sam to paste. Never call any Discourse write endpoint. 4. **Never merge or push to main.** PRs are opened ready for review with the `forum` label. Sam merges, or says "merge" in the session, in which case the merge procedure in the `commit-pr` skill applies (order, bringing branches up to date, the test-file conflict pattern). 5. **Tier 1 means no input needed.** The bug is reproducible, the fix is local to the code, no product decision is involved, no user account is touched. Everything else is tier 2. 6. **Reproduce before fixing.** Per CLAUDE.md: write the failing test first, then fix, then show it passing. If it cannot be reproduced, say so in the PR and the reply rather than guessing. 7. **Reply-only topics are tier 1.** A how-to question, a known limitation, a duplicate, a "works as designed": draft the reply, record it, move on. No interview needed. 8. **Ask with AskUserQuestion, never plain text.** Tier 2 decisions, and anything mid-fix that could go two materially different ways. 9. **When the auto-mode classifier blocks an approved prod write, hand it to Sam.** Write the exact script or SQL to a file at the repo root, print the one-line command that runs it, record `tier2-pending` with that command in the note, and move on. Never look for another route to the same write. 10. **Before any prod `merge_feeds`, check for branches.** `Feed.objects.filter(branch_from_feed=)` must be empty or re-parented first; until PR #2133 is deployed, a merge that deletes a feed with branches deletes the branches and their subscriptions too (CBC incident, 2026-09-14). Prefer `force=False` so the heavier feed survives. Once #2133 is deployed, every merge logs `MERGE_FEEDS_INVENTORY` JSON lines and a bad one is reversed with `manage.py restore_merged_feed --log FILE --feed-id ID --dry-run` first, then without `--dry-run`; grep the lines from `docker logs task-celery` on the worker that ran the merge. 11. **A PR is done only when `/commit-pr` says so.** Green CI on the current head, zero unresolved Claude or Codex review threads, marked ready for review. The push-to-clean loop lives in the `commit-pr` skill; this skill never re-implements it. That loop is capped at three review rounds; a fix that keeps growing under review (locks, leases, queues, a new store) has become a subsystem and goes back to Sam through AskUserQuestion before another round, with the PR's size next to its first commit's. ## Arguments `/investigate-forum [flags] [forum URL ...]` | Flag | Meaning | |---|---| | (none) | Topics with activity in the last 7 days, newest first, skipping ones already in `state.json`, capped at 3 topics per run | | `--days N` | Widen or narrow the activity window | | `--since YYYY-MM-DD` | Window start date, overrides `--days` | | `--topic ID` or a forum URL | Only that topic (repeatable). Ignores window and state. A pasted forum URL anywhere in the prompt counts as this. | | `--all` | Include topics already recorded in state.json | | `--limit N` | Stop after N topics (default 3; `--limit 0` removes the cap). Three is the cap because each topic can fan out into a worktree, a PR, and review rounds, and Sam merges between runs. | | `--dry-run` | Investigate and classify only. No worktrees, no PRs, no state writes. Print what would happen. | ## Files in this skill | File | Purpose | |---|---| | `fetch_topics.py` | Pulls the queue from the public Discourse JSON API. No key needed. | | `screenshot.py` | Headless before/after PNGs of a worktree stack via the Playwright docker image | | `state.json` | Committed record of what was done per topic (see Step 7) | Run both scripts from the repo root with `python3 .agents/skills/investigate-forum/