The thread behind a report, and the two statuses that need one

FeatureMediaService
Shipped
September 6, 2026 at 7:18 PM UTC
Author
Kamo
Commit
a4508e8

`SystemBugThreadService` is the whole feature: a report's MediaSession, the status events written into it with the reason their author gave, the comments anybody who can see the report may leave, replies, reactions and per-member mute. Every entry point is SystemBugController, never MediaController. That is not tidiness — the generic media endpoints authorize by session membership, and a bug thread is readable by everybody the REPORT is visible to, which is a far wider set than the people who have posted on it. Membership here means "has taken part" and exists to decide who is written to. Routing the thread through the bug's own visibility rule is what keeps that rule in the one place that owns it. Threads are created lazily and seeded, not backfilled. Reports existed before threads did, so `media_session_guid` is nullable and `ensureThread` fills it on first touch — writing a CREATED event stamped with the row's own creation time, and moving any `denial_reason` the row still carries into the transcript before clearing the column. Without that seeding, replacing the column with a thread would have silently thrown away the one sentence per report the old design did manage to keep. `thread()` is the writable read that does it; `detail()` stays read-only because it is the hot path. `reportIssuePersists` is the widest action on a report and the only capability that does not narrow by viewer: anybody who can see it may say a fix failed, because the person who walks into a broken fix is very often not the person who filed the report. It narrows by STATUS instead — a report that never claimed to be fixed has nothing to contradict. The status email now plans one letter per participant. `plans()` takes the audience as a parameter so the rule stays testable without a Spring context, and free-standing comments ride ChatEmailNoticeService's existing ledger under their own template key — one letter per recipient until read, digested, mute respected.

All changes

Like what you see shipping?

Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.

Start Free ForeverView Pricing