feat(admin): forward in-game moderation to the website (bidirectional audit)

Phase C / §5.5: so the site's moderation log is complete regardless of origin,
in-game uses of the write-plane verbs are forwarded as admin.audit
(origin:"in-game").

- patches/commandlogging-event.patch: adds CommandLogging.OnWrite, raised in
  WriteLine before the m_Enabled guard so it fires even when file logging is
  off. Scripts-layer file -> dynamic build, no core rebuild.
- patches/BridgeModerationAudit.cs: subscriber. Taps OnWrite for resolved
  ban/kick (parsing the target from the log line) and EventSink.Command for
  [bcast. Lives in patches/ (not overlay/) because it references OnWrite,
  which only exists post-patch — same rule as BridgeVendorSale.cs.
- tools/scaffolding/BridgeAuditProbe.cs: gated headless verification.

Verified live: a genuine [bcast plus simulated ban/kick log lines produced
admin.audit frames with origin=in-game, actor, and the target parsed
(seed_010); a non-moderation line was correctly ignored.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0114TpmrNW4wNXsHq5CR72jQ
This commit is contained in:
2026-07-13 02:12:30 -05:00
parent a0dbb80e1e
commit 11169c52a6
5 changed files with 257 additions and 1 deletions

View File

@@ -32,6 +32,24 @@ Both patches are `git`-format and verified with `git apply --check` against stoc
Not applicable to a non-git shard? `git apply` works in a plain directory too. If `patch` is used instead, note the core files are CRLF; use `patch --binary`.
## In-game moderation audit (admin controls §5.5)
So the website's moderation log stays complete, in-game uses of the write-plane verbs are forwarded to it as `admin.audit` (`origin:"in-game"`). Broadcasts already surface through `EventSink.Command`, but resolved bans/kicks only carry their target inside the command's own `CommandLogging.WriteLine` call — which has no event to subscribe to. One small change fixes that:
| Item | Target | What |
|------|--------|------|
| `commandlogging-event.patch` | `Scripts/Commands/Logging.cs` | Adds a `public static event Action<Mobile,string> OnWrite`, raised in `WriteLine` **before** the `m_Enabled` guard so it fires even when file logging is off. |
| `BridgeModerationAudit.cs` | copy to `Scripts/Custom/Bridge/` | The subscriber: taps `OnWrite` for ban/kick (parsing the target out of the log line) and `EventSink.Command` for `[bcast`, emitting `admin.audit`. **Not** in `overlay/` because it references `CommandLogging.OnWrite`, which does not exist until the patch is applied. |
```bash
cd <servuo root>
git apply --check patches/commandlogging-event.patch # dry run
git apply patches/commandlogging-event.patch
cp patches/BridgeModerationAudit.cs Scripts/Custom/Bridge/BridgeModerationAudit.cs
```
`Logging.cs` is a **Scripts** file, so this is picked up by the dynamic script build — no core/solution rebuild needed (unlike the Phase 7 `EventSink.cs` patch). Verified end-to-end with `tools/scaffolding/BridgeAuditProbe.cs` (gated by `Bridge.AuditProbeOnStart`): a genuine `[bcast` plus simulated ban/kick log lines produced the expected `admin.audit` frames, target parsed, with non-moderation lines ignored.
## Note on `Scripts.csproj`
Phase 0 modifies an existing file but ships as a whole-file overlay (`overlay/Scripts/Scripts.csproj`) because the file is small, we own it operationally, and a copy is less fragile than a diff against a project file. Revisit if it starts drifting from upstream.