refactor(gateway): remove user reputation feature entirely

Drop trust-score/infraction system: delete userReputationStore, remove call sites in fallback/batch processors, drop formatReputationAttrs, drop user_reputations table (migration 0016), delete trust-model test, update docs.
This commit is contained in:
asepharyana
2026-08-18 18:27:15 +07:00
parent 9b3134d767
commit 2a8f6d9062
13 changed files with 19 additions and 643 deletions
@@ -1,12 +1,11 @@
// ═══════════════════════════════════════════════════════════════════════════
// Context enrichment builders — rich <user_reputation> attrs, <user_history>,
// <user_profiles> as_of, bot/edited detection (pure, no DB)
// Context enrichment builders — <user_history>, <user_profiles> as_of,
// bot/edited detection (pure, no DB)
// ═══════════════════════════════════════════════════════════════════════════
import { describe, expect, it } from "vitest";
import {
buildUserHistoryXml,
buildUserProfilesBlock,
formatReputationAttrs,
resolveIsBot,
resolveIsEdited,
} from "../src/modules/ai-moderation/moderationBuilders.js";
@@ -42,72 +41,6 @@ function msg(overrides: Partial<MessageRecord> = {}): MessageRecord {
const DAY_MS = 24 * 60 * 60 * 1000;
describe("formatReputationAttrs — rich reputation signal", () => {
it("emits trust, infraction count and clean streak", () => {
const attrs = formatReputationAttrs({
trust_score: 62,
total_infractions: 3,
clean_message_streak: 45,
last_infraction_at: null,
});
expect(attrs).toContain('trust_score="62"');
expect(attrs).toContain('total_infractions="3"');
expect(attrs).toContain('clean_streak="45"');
});
it("derives last_offense_days_ago and marks repeat offenders (7-day window)", () => {
const attrs = formatReputationAttrs(
{
trust_score: 50,
total_infractions: 2,
clean_message_streak: 0,
last_infraction_at: NOW - 2 * DAY_MS,
},
NOW,
);
expect(attrs).toContain('last_offense_days_ago="2"');
expect(attrs).toContain('repeat_offender="true"');
});
it("does NOT mark repeat offender when the last offense is older than 7 days", () => {
const attrs = formatReputationAttrs(
{
trust_score: 50,
total_infractions: 2,
clean_message_streak: 10,
last_infraction_at: NOW - 30 * DAY_MS,
},
NOW,
);
expect(attrs).toContain('last_offense_days_ago="30"');
expect(attrs).not.toContain("repeat_offender");
});
it("omits offense-derived attrs when the user has no recorded infraction date", () => {
const attrs = formatReputationAttrs({
trust_score: 85,
total_infractions: 0,
clean_message_streak: 120,
last_infraction_at: null,
});
expect(attrs).not.toContain("last_offense_days_ago");
expect(attrs).not.toContain("repeat_offender");
});
it("clamps a future/skewed timestamp to days_ago=0", () => {
const attrs = formatReputationAttrs(
{
trust_score: 50,
total_infractions: 1,
clean_message_streak: 0,
last_infraction_at: NOW + 5 * DAY_MS,
},
NOW,
);
expect(attrs).toContain('last_offense_days_ago="0"');
});
});
describe("buildUserHistoryXml — last flagged messages for repeat offenders", () => {
it("returns empty when there is no real history", () => {
expect(buildUserHistoryXml([])).toBe("");
@@ -1,93 +0,0 @@
// ═══════════════════════════════════════════════════════════════════════════
// Trust model v2 — pure math tests (no DB required)
// ═══════════════════════════════════════════════════════════════════════════
import { describe, expect, it } from "vitest";
import {
computeCleanTrustGain,
computeInfractionPenalty,
INFRACTION_FLOORS,
INFRACTION_PENALTIES,
TRUST_DEFAULTS,
} from "../src/modules/ai-moderation/userReputationStore.js";
describe("computeCleanTrustGain — trust CAN rise", () => {
it("grants +1 every CLEAN_MESSAGES_PER_POINT clean messages", () => {
const before = computeCleanTrustGain(14);
expect(before.newStreak).toBe(15);
expect(before.trustGain).toBe(1);
const after = computeCleanTrustGain(15);
expect(after.newStreak).toBe(16);
expect(after.trustGain).toBe(0);
});
it("keeps compounding past the threshold (no wasted progress)", () => {
expect(computeCleanTrustGain(29).trustGain).toBe(1);
expect(computeCleanTrustGain(44).trustGain).toBe(1);
// 45 clean messages from a fresh start → 3 points of recovery
let gain = 0;
let streak = 0;
for (let i = 0; i < 45; i++) {
const r = computeCleanTrustGain(streak);
streak = r.newStreak;
gain += r.trustGain;
}
expect(gain).toBe(3);
});
});
describe("computeInfractionPenalty — fair and escalating", () => {
const NOW = Date.now();
it("applies base penalty for a repeat offender outside the window", () => {
const r = computeInfractionPenalty({
totalInfractions: 3,
lastInfractionAt: NOW - TRUST_DEFAULTS.REPEAT_OFFENSE_WINDOW_MS - 1000,
severity: "medium",
now: NOW,
});
expect(r.penalty).toBe(INFRACTION_PENALTIES.medium); // 6
expect(r.appliedRules.firstOffense).toBe(false);
expect(r.appliedRules.repeatEscalation).toBe(false);
});
it("halves the penalty for a first offense (leniency)", () => {
const r = computeInfractionPenalty({
totalInfractions: 0,
lastInfractionAt: null,
severity: "high",
now: NOW,
});
expect(r.penalty).toBe(Math.ceil(INFRACTION_PENALTIES.high / 2)); // 6
expect(r.appliedRules.firstOffense).toBe(true);
});
it("escalates ×1.5 for a repeat offense within 7 days", () => {
const r = computeInfractionPenalty({
totalInfractions: 2,
lastInfractionAt: NOW - 60 * 60 * 1000, // 1h ago
severity: "medium",
now: NOW,
});
expect(r.penalty).toBe(Math.ceil(INFRACTION_PENALTIES.medium * 1.5)); // 9
expect(r.appliedRules.repeatEscalation).toBe(true);
});
it("critical first offense still hurts but is halved", () => {
const r = computeInfractionPenalty({
totalInfractions: 0,
lastInfractionAt: null,
severity: "critical",
now: NOW,
});
expect(r.penalty).toBe(Math.ceil(INFRACTION_PENALTIES.critical / 2)); // 13
});
it("severity floors prevent minor offenses from zeroing a user", () => {
expect(INFRACTION_FLOORS.low).toBeGreaterThan(0);
expect(INFRACTION_FLOORS.medium).toBeGreaterThan(0);
// high/critical can still reach zero — severe behavior has consequences
expect(INFRACTION_FLOORS.high).toBe(0);
expect(INFRACTION_FLOORS.critical).toBe(0);
});
});