Escalate repeat sends across groups and delete earlier copies (#124) #43

Merged
backmeupplz merged 4 commits from fix/124-repeat-send-escalation into main 2026-10-14 21:14:36 +00:00
backmeupplz commented 2026-10-14 14:48:57 +00:00 (Migrated from github.com)

Ticket: https://agentboard.icefish-betta.ts.net/#124. This implements Nikita's decision (Telegram 23476): repeat-send escalation in the bot, with no retrain and no keyword rule.

What changes

  • src/repeat-sends.ts adds normalization: NFKD, lowercase, dropping invisible characters and marks, folding lookalike letters, and stripping punctuation and emoji. Copies are keyed by actor+content (sha256) and scored with 1 − (1 − p) × 0.5^n, where n counts the same account's other copies within 24 h in any group. The floor is p ≥ 0.5 and the minimum is 30 normalized chars. It ships a MemoryRepeatStore (default and tests), a bounded fail-open PostgresRepeatStore (migration 007_repeat_sends.sql, content-free rows, 24 h×2 prune, 100k cap, 250 ms deadline), and in-memory audit snapshots for earlier copies.
  • src/bot.ts: after classification, eligible copies are recorded. Once the effective score crosses the unchanged gate (or the model deletes), the current copy and the account's unhandled earlier copies are deleted, in this group and every other group. Each goes through the normal audit-report and stats path (rule: "repeat_send"). Handled copies are never retried. Edits keep their copy. A single send behaves exactly as before.
  • src/deletion.ts now awaits its callbacks so the handled state is persisted before the next copy.
  • src/index.ts wires the Postgres store and closes it on shutdown. spam-cache.ts adds the new file to the policy digest.
  • Docs: docs/repeat-sends.md (rule, constants and the log data behind them) and a README paragraph.

Data (content-free logs, 2026-10-13 15:19 to 2026-10-14 14:39 UTC)

  • The #124 ask appears as two 66-char copies in one chat, 3 h 23 min apart, both scoring 0.6407. No same-shaped copy shows up in other chats. Sender identity isn't logged, so "same account" is likely but unproven.
  • 328 kept 52-char messages scored ≥0.5, and 251 of them were immediately followed by a model-deleted spam message (spammer preambles).
  • A replay upper bound (clusters by length/links/score) gives 138 clusters and 707 messages that would escalate, against 4,607 model deletions in the window.

Tests

  • bun run check with the CI-style Postgres passes: 543 pass, 253 skip, 0 fail.
  • New tests:
    • the incident across 3 groups escalates and deletes earlier copies, with audit reports and stats;
    • the incident twice in one group deletes both;
    • a model delete takes the earlier copies;
    • the #124 controls and crypto-trade-courier controls sent once are untouched;
    • short repeats are untouched;
    • repeats scored under 0.5 are untouched;
    • other accounts are untouched;
    • failed classification and edits;
    • restart with synthesized reports;
    • Postgres persistence, window, prune, cap and outage.

Not proven live: the real-account Telegram deletion and the actual sender of msgs 34977 and 34979.

Ticket: https://agentboard.icefish-betta.ts.net/#124. This implements Nikita's decision (Telegram 23476): repeat-send escalation in the bot, with no retrain and no keyword rule. ## What changes - **`src/repeat-sends.ts`** adds normalization: NFKD, lowercase, dropping invisible characters and marks, folding lookalike letters, and stripping punctuation and emoji. Copies are keyed by actor+content (sha256) and scored with `1 − (1 − p) × 0.5^n`, where `n` counts the same account's other copies within 24 h in any group. The floor is p ≥ 0.5 and the minimum is 30 normalized chars. It ships a `MemoryRepeatStore` (default and tests), a bounded fail-open `PostgresRepeatStore` (migration `007_repeat_sends.sql`, content-free rows, 24 h×2 prune, 100k cap, 250 ms deadline), and in-memory audit snapshots for earlier copies. - **`src/bot.ts`**: after classification, eligible copies are recorded. Once the effective score crosses the unchanged gate (or the model deletes), the current copy and the account's unhandled earlier copies are deleted, in this group and every other group. Each goes through the normal audit-report and stats path (`rule: "repeat_send"`). Handled copies are never retried. Edits keep their copy. A single send behaves exactly as before. - **`src/deletion.ts`** now awaits its callbacks so the handled state is persisted before the next copy. - **`src/index.ts`** wires the Postgres store and closes it on shutdown. `spam-cache.ts` adds the new file to the policy digest. - **Docs:** `docs/repeat-sends.md` (rule, constants and the log data behind them) and a README paragraph. ## Data (content-free logs, 2026-10-13 15:19 to 2026-10-14 14:39 UTC) - The #124 ask appears as two 66-char copies in one chat, 3 h 23 min apart, both scoring 0.6407. No same-shaped copy shows up in other chats. Sender identity isn't logged, so "same account" is likely but unproven. - 328 kept 52-char messages scored ≥0.5, and 251 of them were immediately followed by a model-deleted spam message (spammer preambles). - A replay upper bound (clusters by length/links/score) gives 138 clusters and 707 messages that would escalate, against 4,607 model deletions in the window. ## Tests - `bun run check` with the CI-style Postgres passes: 543 pass, 253 skip, 0 fail. - New tests: - the incident across 3 groups escalates and deletes earlier copies, with audit reports and stats; - the incident twice in one group deletes both; - a model delete takes the earlier copies; - the #124 controls and `crypto-trade-courier` controls sent once are untouched; - short repeats are untouched; - repeats scored under 0.5 are untouched; - other accounts are untouched; - failed classification and edits; - restart with synthesized reports; - Postgres persistence, window, prune, cap and outage. Not proven live: the real-account Telegram deletion and the actual sender of msgs 34977 and 34979.
Sign in to join this conversation.
No description provided.