fix(moderation): log the moderation the audit trail claimed it already had - #122
Merged
Conversation
…y had /admin/log says it holds "every administrative and moderation action" and the ModCP offers itself as the record of a forum. Both were short. The moderation tools do write to admin_log — in the transaction that makes the change, which is stronger than the control panel's helper — but several consequential paths wrote nothing at all, and one that did write was missing from the ModCP's allow-list and so was never shown. Closing a report wrote only a report_event, so report.resolve and report.reject were labels for rows nothing produced; they are now written beside the event, in the same transaction, carrying the report's forum as forumIds/forumId. Deleting, restoring or editing a single post from the postbit wrote nothing; it now writes post.delete, post.restore or post.edit, scoped to the post's forum, when the actor is not the post's author — a member acting on their own post is not moderation and stays out. Locking a signature or an avatar now records signature.lock/unlock and avatar.lock/unlock through recordAdminAction, so the address is recorded too. Each further batch of a mass mail records user.mass_mail_continued, so a campaign is no longer one row and then silence. thread.copy joins the allow-list and gains the forum keys #113 established, so a copy reaches the moderators of both the forum it came from and the one it landed in rather than nobody. Report assignment stays unlogged: it moves nothing and the report's own timeline already shows who holds it. Both copies stand as written, because the record now matches them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014kP43A5shJWmAKqkBovhaF
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
MEI-52, finding D.
/admin/logsays it holds "every administrative and moderation action" and the ModCP offers itself as the record of a forum. Neither was true.What was already true, before changing anything
The finding said no moderation action writes an
admin_logrow viarecordAdminAction. That is literally true and misleading: the moderation writers do not use that helper because they write the row in the transaction that makes the change —logModeratorActioninpackages/db/src/thread-counters.ts, called from the queue, the thread tools, thread surgery, inline moderation and the warning repository. That is stronger thanrecordAdminAction, which is a separate write that cannot roll back with the change it describes. SomoderateQueueAction,inlineModerateAction,splitThreadAction/splitSelectedAction/mergeThreadAction,threadToolActionandissueWarningAction/revokeWarningActionwere already logged and already visible on both screens. The copy was not wrong about them.It was wrong about everything below.
Gap by gap, and the decision
report.resolve/report.rejectwere labels nothing ever wrotereport_eventsrow, invisible to anyone not opening that one report. Written inPostgresReportRepository.close, in the same transaction as the status change and the event, carrying the report's forum asforumId/forumIds. A report about a member or a PM has no forum and so carries no forum key — under #113's rule that makes it its actor's own business, which is right: nobody moderates "no forum".post.delete/post.restore, written inapplyVisibilitybeside the counter move, scoped to the post's forum.post.edit, written inapplyEdit. The row carries the post, thread and forum — never the body or the edit reason.signature.lock/signature.unlockandavatar.lock/avatar.unlockviarecordAdminAction, which also records the address prefix. Four action names rather than one with a flag, following #113's lock/unlock split: "Locked a signature" has to be true on its own. The reason shown to the member stays out of the log.continueMassMailActionwas silent while its sibling loggeduser.mass_mail_continued, withmassMailId, this batch'ssentand the runningqueued. A 3,000-recipient campaign was one row and then silence for five further batches. Not in the moderator vocabulary — it is administration.thread.copywrote a row thatMOD_LOG_ACTIONSfiltered outtoForumIdbut noforumIdsand no source forum, so a copy would have reached only the destination's moderators. It now carriesfromForumId/toForumId/forumIds, so both ends see it, and one forum once when a thread is copied within its own forum./admin/loglede and the overview cardTwo things are deliberately still unlogged, and both are documented rather than left to be rediscovered:
Everything reuses the existing shapes:
recordAdminActionfor the app-level actions,logModeratorActionfor the transactional ones, and #113'sforumIds/forumId/fromForumId/toForumIdkeys for anything that belongs in the moderator log. No new detail keys — every key the new rows carry already had a label inDETAIL_LABELS.Validation
pnpm verify— exit 0 (310 test files, 5,745 tests).DATA_SOURCE=fixture pnpm build— exit 0. Run becauseapps/community/src/server/moderation-actions.tsgained an import of./admin, which isserver-only.mainin two batches turned 22 of the new assertions red — the report rows, the copy's forum keys, the post rows, the two vocabulary tests, the two lock actions, the mass-mail batch row, and the three ModCP scoping tests — then all restored to green. The negative tests (a member's own delete and own edit logging nothing) stay green in both directions by construction, which is the point of them.pnpm test:e2ewas not run.mainat 41ea6bd after verify; the affected suites andeslintwere re-run on the rebased tree.Docs updated in the same commit: a new parity entry under The moderator log stating what is logged, where the row is written and where the authorship boundary is; and a "What reaches the admin log" section in
docs/operating.mdnaming the two exclusions, so an operator who cannot find a row knows in ten seconds whether it should exist.🤖 Generated with Claude Code
Generated by Claude Code