feat(comment): AI spam checking + real nested replies - #201
Merged
Conversation
Two additions to the comment module. SPAM — Tiger_Comment_Spam is the registry COMMENTS.md §6 designed, with the in-platform agent as its first checker. An Akismet-style module registers into the same seam. - Offered in the module's admin ONLY when an agent is actually connected, and the settings service REFUSES to store an enabled flag without one: a stored `1` with no agent looks like a working filter and silently isn't. With no agent it's a no-op plus one Tiger_Log line and the comment passes through unchecked, exactly as asked. - Gated on Tiger_Agent::isConnected(), not isAvailable() — the latter also asks whether the CURRENT USER may chat, which is meaningless for a background check running for an anonymous commenter. - A verdict may only TIGHTEN. `spam` bins the comment; ham/unknown/timeout/no-agent/broken-checker all leave the install's moderation posture alone. Nothing a checker says can publish something that wasn't going to be published — which is what makes it safe to hand attacker-controlled text to a language model. - Prompt injection is the live risk, so the body is delimited and framed as DATA, only the two literal answers are accepted, and anything else is `unknown` (fail-open). The worst an injection wins is the treatment the comment would have had with no checker at all. - The poster is never told they were classified — a binned comment gets the same "awaiting moderation" reply a held one does, or a spammer just iterates until it passes. - One-shot provider complete(), not Tiger_Agent_Loop: a classification needs no tools, no ReAct steps, no transcript, and must not be able to DO anything. NESTING — parent_id was accepted by the API but unreachable: there was no reply affordance and the thread rendered flat. Now the default depth is 3 (tiger.comment.threading, per-subject override), the list payload publishes the limit so the Reply button disappears AT the limit rather than failing on submit, and the client orders the flat list into a tree (appending orphans rather than dropping somebody's words when a parent is removed mid-thread). Indent still caps at 3 so a deep thread doesn't walk off a phone. 25 integration + 11 unit tests for these. One harness note: several deliberately exercise fail-soft branches that LOG why they degraded, which trips beStrictAboutOutputDuringTests — they set the null log writer so a covered branch doesn't have to choose between being tested and being quiet. Full suite: OK (2183 tests, 21627 assertions).
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.
AI spam checking
Tiger_Comment_Spamis the registry COMMENTS.md §6 designed, with the in-platform agent as its first checker. An Akismet-style module registers into the same seam.1with no agent looks like a working filter and silently isn't.Tiger_Logline, comment passes through unchecked.isConnected(), notisAvailable()— the latter also asks whether the current user may chat, meaningless for a background check running for an anonymous commenter.spambins it; ham / unknown / timeout / no-agent / broken-checker all leave the moderation posture alone. Nothing a checker says can publish something that wasn't going to be published — that's what makes it safe to hand attacker-controlled text to a model.unknown(fail-open). Worst case an injection wins the treatment the comment would have had with no checker at all.complete(), not the agent Loop — a classification needs no tools, no ReAct steps, no transcript, and must not be able to do anything.Cost/latency is real and acknowledged in the doc: an LLM round-trip per comment with a body, on the org's BYO key. Opt-in, skipped for star-only ratings, fails open on timeout.
Nested replies
parent_idwas accepted by the API but unreachable — no reply affordance, and the thread rendered flat. Now:tiger.comment.threading, per-subject override).Tests
36 new. One harness note worth flagging: several deliberately exercise fail-soft branches that log why they degraded, which trips
beStrictAboutOutputDuringTestsand reports a passing test as risky. They set the null log writer, so a covered branch doesn't have to choose between being tested and being quiet.Full suite: OK (2183 tests, 21627 assertions).