fix(report): unrated content carries no trend, demoted gets no model narrative (ADR-0038) - #59
Merged
Merged
Conversation
…narrative (ADR-0038)
Two severity-derived signals still leaked onto the moderation view after ADR-0032:
1. The standard /report path (management view + offline snapshot) ran a full LLM
synthesis over demoted (rasismi/asiaton) groups too. The synthesis digest feeds
the model each item's severity, so it could editorialize a rating back into the
moderation card ("'<slur>' (korkea)") — the thing ADR-0032 removed from badges.
2. The trend/direction was computed and shown for demoted themes on desk,
management, and snapshot. "paheneva" (worsening) is severity-derived, so a
moderation card could show a severity judgment on hostile content.
Build a demoted category as a deterministic count-only moderation theme
(BuildModerationTheme) in both the standard and live-summary paths: neutral
"stable" direction with an EMPTY label, a deterministic count narrative
(ReportText.ModerationNarrative), and no model call — so the model never authors
prose over hostile content (and the LLM budget is saved). The views (desk,
index.html, SnapshotHtml) suppress the trend for unrated themes, symmetric with
how they already suppress severity/sentiment.
Unrated now means no severity, no sentiment, no trend, no model narrative — the
full "the category is the signal" contract.
Adds a regression test: a demoted group makes no LLM call, is Unrated with a
"stable"/empty direction, and its narrative carries no severity words.
…ct, tidy moderation UI From the two-PR review: - Reword ADR-0038 so it does not assert the whole-window Yhteenveto is already fixed on this branch: scope the "no model over demoted" claim to the per-category moderation themes, and reference the companion rated-only Overall change (ADR-0033 §3) for the Yhteenveto — accurate whether or not the companion PR is merged yet. - Relocate this PR's ReportServiceTests [Fact] to a disjoint anchor (after the Alerts test) so it no longer collides with the sibling PR's test at the same insertion point — the two now merge without a hand-resolved conflict. - index.html: also drop the "automaattinen kooste" tag on unrated moderation cards, so a moderation card is count-only and consistent with the desk/snapshot. - feedctl: print the count only (no "stable" trend token) for unrated themes, matching the management views.
MikkoNumminen
added a commit
that referenced
this pull request
Jul 16, 2026
* chore: ignore local poro-findings.pdf export; record TODO #9 (deploy) done The canonical Poro findings are docs/poro-findings.md; the root PDF is a personal export, so gitignore it rather than track a binary. Also commits the already-recorded TODO item #9 (rebuild+restart to pick up the #58/#59 demoted-content fixes, verified live 2026-07-15). * docs: mark TODO #8 (SQLite security watch) done — resolved by #55 Bundled SQLite is now 3.50.4 (>= 3.50.2), clearing GHSA-2m69-gcr7-jv3q.
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.
Follow-up to #58. Closes the two residual severity-leak surfaces that audit surfaced there (Notes A and B). Independent of #58 (disjoint code regions) — can merge in either order.
Problem
After ADR-0032 made demoted categories (
rasismi/asiaton) unrated, two severity-derived signals still reached the moderation view:/reportpath (management view/+ the offline snapshot) ran a full LLM synthesis over demoted groups. The synthesis digest feeds the model each item's severity, so it could editorialize a rating back into the moderation card —'<slur>' (korkea)— the very thing ADR-0032 removed from the badges. (The desk/live-summary path was already deterministic there.)kasvava/paheneva/…) was computed and shown for demoted themes on desk, management, and snapshot.paheneva(worsening) is severity-derived, so a moderation card could show a severity judgment on hostile content.Fix
ReportService.BuildModerationTheme) in both the standard and live-summary paths: neutralstabledirection with an empty label, a deterministic count narrative (ReportText.ModerationNarrative), and no model call — the model never authors prose over hostile content, and the LLM budget is saved.Unrated now means no severity, no sentiment, no trend, no model narrative — the full "the category is the signal" contract.
Tests
StandardMode_DemotedCategory_IsCountOnlyModerationTheme_NoModelNarrative_NoTrend: a demoted group makes no LLM call (exactly one call, for the rated group), isUnratedwithstable/empty direction, and its narrative carries no severity words.AI-first docs
Yes — new ADR-0038 (unrated = no trend + no model narrative for demoted), indexed in
docs/decisions/README.md.