Files
GMW/.hermes/plans/2026-08-18_gmw-public-features-7-15.md
T
2026-08-18 20:45:12 +07:00

7.9 KiB
Raw Blame History

GMW — Fitur Publik Lanjutan #7#15 + Bug Fix Reputation Removal

For Hermes: Implement task-by-task. Build + lint + typecheck each service after its changes. Deploy via push to main (CI handles Nix build + systemd). Apply any new drizzle migration MANUALLY (systemd does NOT run migrations). Hard constraint (user): public read-only web, fully automatic, rules in code, NO admin endpoints, NO shadow mode, NO per-user reputation aggregation.

Bug fix discovered during planning (MUST do first)

services/backend/src/modules/dashboard/dashboard.repository.ts still references pgUserReputationsTable (import line 8; JOINs at lines 173 + 457) — that table was DROPPED in migration 0016. dashboard.listUsers / dashboard.userDetail will crash at runtime (undefined table). Remove the import + the r.* join columns (trust_score, clean_message_streak, total_infractions) from both queries. This is a regression introduced by the reputation removal commit.

Features to implement (#7#15)

All reuse existing infra: moderation_actions, messages, channel_cultures, term_glossary_cache, ai_analysis_runs, message_edits, gateway cron (for #15), WS (proven Live Feed pattern), oRPC over WS (proven), pure-SVG charts (no libs).

# Feature Data source Surface
7 Flagged Link / Scam Domain Reporter regex URL from moderation_actions.content/evidence /moderation
8 Top Flagged Channels join moderation_actions.message_idmessages.channel_id /moderation
9 Moderation Heatmap by Hour moderation_actions.created_at hour-of-day /moderation
10 Flag Category Drill-down moderation_actions.categories (reuse Trends) /moderation FE-only
11 Channel Culture Glossary channel_cultures (exists) new /channels panel
12 Term Knowledge Base term_glossary_cache (exists) new /glossary panel
13 Edit/Evasion Tracker message_edits (exists) /messages
14 Auto-mod Coverage Stats ai_analysis_runs (exists) /moderation metric tiles
15 Weekly Digest (auto, cron) aggregate #7/#8/#9 → Discord via gateway cron gateway cron + /moderation

Architecture per layer

Backend (oRPC, services/backend/src)

  • New repository methods (add to existing repos, follow getTrends SQL style):
    • moderation.repository.ts:
      • getTopFlaggedDomains(days)regexp_matches(content,'https?://([^/\s]+)') on moderation_actions WHERE created_at>=since, group by host, COUNT, order DESC LIMIT 20.
      • getTopFlaggedChannels(days) — join moderation_actions a LEFT JOIN messages m ON m.id=a.message_id, group by m.channel_id, COUNT, order DESC LIMIT 15. Channel name via m.metadata::jsonb->'channel'->>'channelName'.
      • getHourlyModeration(days)EXTRACT(HOUR FROM to_timestamp(created_at/1000)) group by hour, COUNT, severity breakdown. (24 rows)
      • getFlaggedByCategory(days, category) — list actions where categories contains category (reuse listActions filter or new query), for drill-down #10.
      • getCoverage(days) — from ai_analysis_runs: total runs, status breakdown (clean/flagged/warn/error/pending), coverage % = (analyzed)/(captured in window).
    • dashboard.repository.ts (or new knowledge.repository.ts):
      • listChannelCultures(limit, search?)channel_cultures rows (channel_id, guild_id, channel_name from messages metadata, culture_summary, last_analyzed_at).
      • listGlossary(limit, search?)term_glossary_cache (term, definition, source_url, resolved_at, hit_count) order by hit_count DESC.
    • messages.repository.ts:
      • getEditHistory(limit, channelId?)message_edits join messages for old_content + channel + username + edited_at, order DESC LIMIT.
  • moderation.service.ts / dashboard.service.ts / messages.service.ts: thin wrappers.
  • orpc/router.ts: add procedures (follow trends shape):
    • moderation.topDomains, moderation.topChannels, moderation.byHour, moderation.byCategory (input {days,category}), moderation.coverage.
    • dashboard.channelCultures, dashboard.glossary.
    • messages.editHistory.

Frontend (services/frontend/src)

  • lib/types/moderation.ts: add FlaggedDomain, FlaggedChannel, HourlyModeration, ModerationCoverage interfaces.
  • lib/types/index.ts (+ message.ts): add ChannelCultureRow, GlossaryRow, EditHistoryRow.
  • lib/api/moderation.ts: add topDomains, topChannels, byHour, byCategory, coverage.
  • lib/api/dashboard.ts (or messages.ts): add channelCultures, glossary, editHistory.
  • lib/api/server.ts: add SSR seed fetchers (follow getModerationStats).
  • hooks/use-moderation.ts: add useTopDomains, useTopChannels, useHourlyModeration, useByCategory, useCoverage. hooks/use-dashboard.ts/use-messages.ts: add culture/glossary/edit hooks. hooks/index.ts: export all.
  • New components (pure SVG/CSS, reuse GlassPanel/SectionHeader/Badge/Donut):
    • components/ScamDomains.tsx, components/TopChannels.tsx, components/ModerationHeatmap.tsx, components/CoverageTiles.tsx, components/ChannelCultureGlossary.tsx, components/TermGlossary.tsx, components/EditHistory.tsx.
  • Wire into app/(dashboard)/moderation/view.tsx (grid col-span-2/3/5 as space allows) and app/(dashboard)/messages/view.tsx (EditHistory panel) and new route pages app/(dashboard)/channels/page.tsx + app/(dashboard)/glossary/page.tsx with matching view.tsx (follow existing page→view SSR pattern; check app/(dashboard)/dashboard/page.tsx).
  • Export CSV buttons reuse lib/csv.ts downloadCsv (client-side) for domains/channels/edits.

Gateway (#15 Weekly Digest)

  • Add a cron/interval in services/discord-gateway (check existing scheduler pattern — search setInterval/cron in src). On a 7-day cadence, query backend oRPC (dashboard.activity, moderation.trends, moderation.topChannels) — OR compute directly via a shared repository — and post a formatted summary to the monitor guild channel (via existing discordClient.channels.send helper). Fully automatic, no UI.

Files touched (summary)

  • backend: modules/moderation/{repository,service}.ts, modules/dashboard/{repository,service}.ts, modules/messages/{repository,service}.ts, orpc/router.ts, shared/index.ts (if new tables), lib/types/* (FE)
  • frontend: lib/api/*, lib/types/*, hooks/*, components/*, app/(dashboard)/*
  • gateway: new digest scheduler + (none if reuse backend) maybe shared/redis-channels.ts

Constraints / pitfalls (from gmw-ops skill)

  • created_at is bigint epoch-MS — compare with </>, do NOT divide by 1000 in SQL.
  • Pure SVG only — frontend has ZERO chart libs.
  • Badge Tone = signal|amber|vermilion|neutral (no "rose").
  • Frontend WS import is @/lib/ws/context; method on not subscribe.
  • Commit author asepharyana, no Co-Authored-By.
  • Rebuild dist/ after gateway changes; apply drizzle migrations manually.

Verification

  • Per service: pnpm typecheck && pnpm lint && pnpm build green.
  • Gateway: pnpm test (117+ pass).
  • Live: moderation/stats WS returns data (proves adapter); new procedures registered (typecheck = proof). systemctl show new ActiveEnterTimestamp after deploy.
  • DB: confirm channel_cultures/term_glossary_cache/message_edits/ai_analysis_runs have rows before relying on them (some may be empty → components handle empty state).

Execution order

  1. Bug fix dashboard.repository (reputation JOIN) — deploy-safe.
  2. Backend repositories + service + router (#7,#8,#9,#14 dashboard; #11,#12; #13).
  3. FE types + api + hooks + components + wire (#7,#8,#9,#10,#11,#12,#13,#14).
  4. Gateway #15 digest (if scheduler exists) — verify via log, not UI.
  5. Build/lint all 3 services; commit; push; monitor CI; apply migrations; verify live.