Repository navigation
Deprecate dead pre-event-sourcing tables (rename first, drop later) + delete dead code referencing them #330
Description
Context
The VahterBanBot database migrated from per-feature tables to a single append-only
event-sourced event table (Flyway V23__events.sql, cut over 2026-04-02). That
migration left a cluster of pre-event-sourcing tables behind: nothing writes to them
any more, and application code no longer reads from them either — except for one dead
method, GetVahterStats (src/VahterBanBot/DB.fs:936), which still queries two of
them and has zero callers itself.
This issue started as "delete GetVahterStats" and is being broadened to cover the
whole orphaned-table cluster it's part of, plus the coupon bot's DB (which has its own,
smaller, event-sourcing migration — coupon_event).
Evidence
Methodology: for every table, (a) grepped src/VahterBanBot / src/CouponHubBot and
scripts/queries/ for the table name, distinguishing "referenced only by dead code /
migrations / one-off ops scripts" from "referenced by a live code path", and (b)
checked pg_stat_user_tables write counters (never reset on either DB — pg_stat_database.stats_reset
is NULL) plus max() of each table's own timestamp column. A table is only called
DEAD below when code-side and data-side evidence agree.
VahterBanBot DB — DEAD (candidates for this issue)
| Table | Last write (max timestamp) | Live code references | Verdict |
|---|---|---|---|
banned |
2026-04-02 20:18 UTC | None outside GetVahterStats (DB.fs:947) |
DEAD |
banned_by_bot |
2026-04-02 22:40 UTC | None outside GetVahterStats (DB.fs:955) |
DEAD |
vahter_actions |
2026-04-02 19:19 UTC | None in app code; superseded by event (VahterActed) |
DEAD |
message |
2026-04-02 22:42 UTC | None in app code; superseded by event (MessageReceived) |
DEAD |
"user" |
2026-04-02 22:36 UTC | None outside GetVahterStats (DB.fs:948, JOIN "user") |
DEAD |
callback |
2026-04-02 22:40 UTC | None; all callback flows (RecordCallback/ResolveCallback/ExpireOrphanedCallbacks etc. in DB.fs) now read/write event (CallbackCreated/CallbackMessagePosted/CallbackResolved/CallbackExpired) |
DEAD |
llm_triage |
2026-04-02 19:14 UTC | None; LlmTriage.fs writes LlmClassified events to event, not this table |
DEAD |
false_positive_messages |
no timestamp column; 0 pg_stat writes ever; static row count (2381) |
None in app code; only read by the one-time V23__events.sql backfill and replicated in tests/VahterBanBot.Tests/test_seed.sql (seed helper, not app code) |
DEAD |
false_negative_messages |
no timestamp column; 0 pg_stat writes ever; static row count (25) |
Same as above | DEAD |
false_positive_users |
no timestamp column; 0 pg_stat writes ever; static row count (14) |
Same as above | DEAD |
Cross-checked FK dependents of the tables above via information_schema — every FK
pointing at message/"user"/banned originates from another table in this same DEAD
set (false_negative_messages, banned, banned_by_bot, false_positive_users,
vahter_actions); no live table has a foreign key into any of these 10 tables, so
renaming them is safe.
Note: tests/VahterBanBot.Tests/Program.fs:12 has a stale doc comment claiming
DB.getUserStatsByLastNMessages "joins against" false_positive_messages /
false_negative_messages — the current implementation of that method (DB.fs:453-511)
reads only from event. Not fixing that comment here (out of scope for this issue),
flagging so it doesn't cause confusion later.
scripts/queries/vahter/01-daily-messages.sql, 02-daily-bans-actions.sql and
03-llm-ml-verdicts.sql already carry comments documenting the 2026-04-02 freeze for
message/user, banned/banned_by_bot/vahter_actions, and llm_triage
respectively — this issue is the first place tying that into an actual cleanup plan.
VahterBanBot DB — LIVE (not in scope)
bot_setting, event, llm_verdict_cache, ocr_cache, ml_trained_model,
scheduled_job, snapshot_message, snapshot_user, user_msg_text_index,
user_profile_cache, flyway_schema_history — all have recent pg_stat_user_tables
write activity and/or live code references outside GetVahterStats.
CouponHubBot DB — reviewed, no dead tables found
CouponHubBot has its own, separate event-sourcing table (coupon_event). Every other
table in that database (bot_setting, chat_message, coupon, pending_add,
pending_add_batch, pending_add_batch_item, pending_feedback, user,
user_feedback, flyway_schema_history) is referenced live in
src/CouponHubBot/Services/DbService.fs. The pending_*/pending_feedback tables
show n_live_tup = 0 in pg_stat_user_tables but that's expected — they're
transient in-flight queue tables (rows inserted then deleted once a pending
add/batch/feedback resolves), not orphans. No action proposed for the coupon DB in
this issue.
Staged plan (binding, per owner decision)
Data-changing steps below are not applied by an agent — DB access here is
read-only, and per repo convention (schema/data changes aren't done via Flyway seeds
by agents) the owner runs these by hand.
- Rename now — the 10 DEAD VahterBanBot tables get a
deprecated_prefix and
stay in place (data untouched, nothing drops). Idempotent SQL below. - Delete the dead code that references them —
GetVahterStats
(src/VahterBanBot/DB.fs:936-963) and its return typeVahterStats
(src/VahterBanBot/Types.fs:469), which have no other callers/uses. This is
ordinary code deletion, tracked as a follow-up PR against this issue. - Observe — leave the renamed tables in place for a while (no fixed deadline set
here) in case something unexpected still depends on the old names. - Drop later — only after step 3 confirms nothing broke, the owner drops the
deprecated_*tables by hand.
Idempotent rename SQL (VahterBanBot DB, step 1 — for the owner to run by hand)
ALTER TABLE ... RENAME TO deprecated_... (10 tables)
-- Idempotent: ALTER TABLE IF EXISTS is a no-op if already renamed / already absent.
ALTER TABLE IF EXISTS banned RENAME TO deprecated_banned;
ALTER TABLE IF EXISTS banned_by_bot RENAME TO deprecated_banned_by_bot;
ALTER TABLE IF EXISTS vahter_actions RENAME TO deprecated_vahter_actions;
ALTER TABLE IF EXISTS message RENAME TO deprecated_message;
ALTER TABLE IF EXISTS "user" RENAME TO deprecated_user;
ALTER TABLE IF EXISTS callback RENAME TO deprecated_callback;
ALTER TABLE IF EXISTS llm_triage RENAME TO deprecated_llm_triage;
ALTER TABLE IF EXISTS false_positive_messages RENAME TO deprecated_false_positive_messages;
ALTER TABLE IF EXISTS false_negative_messages RENAME TO deprecated_false_negative_messages;
ALTER TABLE IF EXISTS false_positive_users RENAME TO deprecated_false_positive_users;Suggested approach for step 2 (code deletion)
- Remove
GetVahterStats(src/VahterBanBot/DB.fs:936-963) and theVahterStats/
VahterStattypes it uses (src/VahterBanBot/Types.fs:469and neighbors) if they
have no other use. - Grep for
GetVahterStats(/VahterStatsacross the solution first to confirm
zero remaining callers (matches the original triage for this issue). - No other application code references the 10 DEAD tables, so step 2 is scoped to
this one method + type; it does not itself require any table rename to land first,
but should ship as its own PR separate from the rename (rename is a DB-owner action,
code deletion is a normal PR).
Activity
github-actions commentedon Aug 1, 2026
Project assessment bump (2026-08-01) Still relevant.
Evidence
src/VahterBanBot/DB.fs:936definesmember _.GetVahterStats(banInterval: TimeSpan option) : Task<VahterStats> =but a repository-wide search shows no callers toGetVahterStats(; only its definition is present. See file snippet at that location.src/VahterBanBot/Cleanup.fsusesdb.GetVahterActionStats(...)(the active stats path), confirming the live code usesGetVahterActionStatsinstead ofGetVahterStats.Suggested Approach
GetVahterStatsmember fromsrc/VahterBanBot/DB.fs(or mark it obsolete with a comment and add a unit test if it must be preserved for historic reasons).dotnet test -c Releaseto confirm no caller remains and CI stays green.(Reported by automated project-agent scan.)
github-actions commentedon Aug 2, 2026
Project assessment bump (2026-08-02)
Still relevant.
Evidence:
GetVahterStatsis defined atsrc/VahterBanBot/DB.fs:936but has no callers (code search returned no references).GetVahterActionStats(seesrc/VahterBanBot/DB.fs:966andsrc/VahterBanBot/Cleanup.fs:77), which suggestsGetVahterStatsis dead/unused.Suggested approach:
GetVahterStatsor mark it[<Obsolete>]with an explanation and a TODO linking to this issue, and preferGetVahterActionStatsin callers.This is low-risk, repository-local cleanup;
priority-lowis appropriate.github-actions commentedon Aug 3, 2026
Project assessment bump (2026-08-03) Still relevant. Evidence: references in source (see src/VahterBanBot/DB.fs members on lines around 936/966) and open project issue list.
github-actions commentedon Aug 4, 2026
Project assessment bump (2026-08-04) Still relevant.
Evidence:
src/VahterBanBot/DB.fs:936definesmember _.GetVahterStats(...).src/VahterBanBot/DB.fs:966definesmember _.GetVahterActionStats(...).src/VahterBanBot/Cleanup.fs:77callsdb.GetVahterActionStats(...), indicatingGetVahterActionStatsis used whileGetVahterStatsappears unused.Context:
GetVahterStatsin the codebase.Suggested approach:
GetVahterStats, or add a comment linking it to the newerGetVahterActionStatsand mark it as intentionally retained.github-actions commentedon Aug 5, 2026
Project assessment bump (2026-08-05)
Still relevant.
Quick checks performed:
src/VahterBanBot/DB.fs:936(member _.GetVahterStats(banInterval: TimeSpan option) : Task<VahterStats> =).GetVahterStats(; the live cleanup flow callsGetVahterActionStats(src/VahterBanBot/Cleanup.fs:77).Suggested next step: either remove the unused member or mark it explicitly (comment/
[<Obsolete>]) with rationale. See this assessment for reproduction evidence.github-actions commentedon Aug 7, 2026
Project assessment bump (2026-08-07) Still relevant.
Updated context
src/VahterBanBot/DB.fs:936definesGetVahterStats. I searched the repository and found no callers ofGetVahterStats(no references). The live path isGetVahterActionStatswhich is used (e.g.src/VahterBanBot/Cleanup.fs:77).Suggested approach
GetVahterStatsand any unused DTOs it returns, orGetVahterActionStatsif the API must remain available for external callers. Prefer removing dead code if there is no external dependency.Evidence:
src/VahterBanBot/DB.fs:936(definition)src/VahterBanBot/Cleanup.fs:77(usesGetVahterActionStats)github-actions commentedon Aug 8, 2026
Project assessment bump (2026-08-08)
Still relevant.
Evidence
src/VahterBanBot/DB.fs:936definesGetVahterStatswhich appears unused (dead method). (scanned in today's repo-wide TODO/search)github-actions commentedon Aug 9, 2026
Project assessment bump (2026-08-09) Still relevant.
Context
src/VahterBanBot/DB.fsand confirmed theGetVahterStatsimplementation remains present aroundsrc/VahterBanBot/DB.fs:920-1010.Evidence
src/VahterBanBot/DB.fscontainsmember _.GetVahterStats(banInterval: TimeSpan option) : Task<VahterStats>and the SQL used to compute per-vahter kill counts.Suggested next step
github-actions commentedon Aug 10, 2026
Project assessment bump (2026-08-10)
Still relevant.
Evidence
src/VahterBanBot/DB.fs:936—member _.GetVahterStats(...)is present.src/VahterBanBot/DB.fs:966—member _.GetVahterActionStats(...)is present and used by cleanup flows, whileGetVahterStatsappears unused.Suggested action
Scanned repo for other project-relevant TODO/FIXME and doc/config drift; no new duplicates found.
github-actions commentedon Aug 11, 2026
Project assessment bump (2026-08-11) Still relevant.
Context
src/VahterBanBot/DB.fs:936definesGetVahterStatswhich appears to be a dead/unused helper; the live path for the stats isGetVahterActionStats(see same file).Why still relevant
[-]Dead method GetVahterStats in VahterBanBot/DB.fs[/-][+]Deprecate dead pre-event-sourcing tables (rename first, drop later) + delete dead code referencing them[/+]Szer commentedon Aug 12, 2026
Opened #362 implementing stages 1–2 of this issue's staged plan as a single PR:
V41__deprecate_dead_pre_event_sourcing_tables.sqlrenames all 10 dead tables todeprecated_*(idempotent, no drop). Per the owner decision above, this ships as a normal schema migration rather than hand-run SQL, so the testcontainers suite runs it as part of the full migration chain before it ever touches prod — the hand-run "Idempotent rename SQL" section in this issue's body is superseded by that migration.GetVahterStats(DB.fs) and its only-consumer typeVahterStats/VahterStat(Types.fs) — verified zero remaining callers.tests/VahterBanBot.Tests/test_seed.sql(which seeded the pre-rename table names directly) and a stale doc comment inProgram.fsto keep the suite green against the renamed schema.Stages 3 (observe) and 4 (drop, owner-run) are unchanged and still open — not closing this issue.
2 remaining items
github-actions commentedon Sep 2, 2026
Project assessment bump (2026-09-02)
Still relevant. Automated scans of the tree found remaining references to the pre-event-sourcing read tables and the migration that renames them to
deprecated_*.Evidence
src/VahterBanBot/DB.fs:476(SQL referencingbanned_by_bot).src/VahterBanBot/DB.fs:499(SQL logic usingbanned_by_bot/is_spam).src/VahterBanBot/DB.fs:502(SQL logic usingbanned_by_bot/banned_by_vahter).src/VahterBanBot/Cleanup.fs:83(callsdb.GetVahterActionStats, which queries the legacy read model).src/vahter-bot/migrations/V41__deprecate_dead_pre_event_sourcing_tables.sql(renamesbanned_by_bot,vahter_actions,llm_triage->deprecated_*).Suggested approach
V41has been applied in production, drop thedeprecated_*tables and remove or refactor the legacy SQL insrc/VahterBanBot/DB.fsto use the snapshot/read-model tables (snapshot_message/snapshot_user) or event-derived queries.src/VahterBanBot/Cleanup.fscallers/tests accordingly.Context
No change to priority/labels requested here — bumping for visibility.
github-actions commentedon Sep 4, 2026
Project assessment bump (2026-09-04) Still relevant. Updated context below.
src/vahter-bot/migrations/V41__deprecate_dead_pre_event_sourcing_tables.sqlexists and renames the 10 pre-event-sourcing tables todeprecated_*(rename-only, idempotent).GetVahterStats/VahterStatssymbols.banned,message,vahter_actions,callback,llm_triage) and found matches only inside migration files (e.g.V23__events.sql,V27__backfill_spam_events.sql) and the V41 rename migration — no application code references remain outside migrations.Suggested next steps (owner action):
GetVahterStats/VahterStatsleft to delete.Evidence:
src/vahter-bot/migrations/V41__deprecate_dead_pre_event_sourcing_tables.sql(rename migration present).GetVahterStats/VahterStatsinsrc/(grep run).src/vahter-bot/migrations/V23__events.sql,src/vahter-bot/migrations/V27__backfill_spam_events.sql).-- Project agent automated scan
github-actions commentedon Sep 5, 2026
Project assessment bump (2026-09-05) Still relevant.
I re-ran the quick repo checks for today's assessment and reconfirm the dead pre-event-sourcing table cluster described in #330 remains present in the repo and in migrations. Evidence below points to the same legacy artifacts and migrations listed in the issue.
Evidence (repo matches found during scan):
src/vahter-bot/migrations/V21__llm_triage.sqlreferencesllm_triageand grants it. (migration exists).src/vahter-bot/migrations/V8__ml-stuff.sqlcreatesbanned_by_bot(legacy table). (migration exists).src/vahter-bot/migrations/V27__backfill_spam_events.sqlreferencesbanned_by_botandbannedin backfill steps.src/vahter-bot/migrations/V16__topics_to_channels.sqltouchescallback(legacy table) and its indexes.tests/SerializationCompat.Tests/CallbackCompatTests.fs,tests/VahterBanBot.Unit.Tests/UpdateIdentityTagsTests.fs), but live code paths useevent/snapshot_*now.What I ran:
rg "TODO|FIXME|HACK|XXX" src scripts -g '*.fs' -g '*.sh'— one hit (comment insrc/VahterBanBot/Types.fs) but this is the same explanatory comment already discussed in the closed issue(s) around addingRequireQualifiedAccess(see Add RequireQualifiedAccess to discriminated unions in VahterBanBot Types #283 / Leftover TODO comment: blanket-applying RequireQualifiedAccess in VahterBanBot.Types #332).rg "GetVahterStats|GetVahterActionStats"— foundGetVahterActionStatsinsrc/VahterBanBot/DB.fs(live) and no remaining callers of the olderGetVahterStatsbeyond the context described in Deprecate dead pre-event-sourcing tables (rename first, drop later) + delete dead code referencing them #330.rg "banned_by_bot|vahter_actions|llm_triage|callback|banned"— matches in the migrations listed above and in a small set of tests/compat fixtures; matches are the same legacy artifacts called out in the issue.Conclusion / request to owner:
GetVahterStats+VahterStatstypes) is still scoped and safe to land.— Project agent
github-actions commentedon Sep 7, 2026
Project assessment bump (2026-09-07)
Still relevant. I scanned the repo (
src/,scripts/) forTODO|FIXME|HACK|XXXand inspected references to the pre-event-sourcing tables; the dead-table cluster and the lone dead accessor method described in this issue remain present per a quick grep and file inspection. No new actionable items were found that would change the scope of this issue.Scans run:
rg "TODO|FIXME|HACK|XXX" src/ scripts/rg "GetVahterStats|deprecated_banned|deprecated_message|deprecated_vahter_actions" -nIf you want, I can open a small PR removing the now-unreferenced
GetVahterStats/VahterStatscode once the DB-owner has performed the safe rename step described in the issue.github-actions commentedon Sep 8, 2026
Project assessment bump (2026-09-08)
Still relevant.
Actions I ran:
TODO|FIXME|HACK|XXXinsrc/andscripts/and found one residual TODO comment insrc/VahterBanBot/Types.fs:28.GetVahterStats(none found)..github/bots.yml(file exists) and scannedbot_settingreferences insrc/(AlitaBot documents many bot_setting keys).Evidence:
src/VahterBanBot/Types.fs:28contains a TODO-style comment aboutRequireQualifiedAccess.Suggested next step:
github-actions commentedon Sep 10, 2026
Project assessment bump (2026-09-10) Still relevant.
Quick scan performed across
src/andscripts/forTODO|FIXME|HACK|XXXand for known dead-code signatures.Findings:
.github/AGENT-FLOWS-REDESIGN.mddocuments the event-sourcing cutover and calls outDB.fs:866 GetVahterStatsas dead (the file still references the historic location).GetVahterActionStatsatsrc/VahterBanBot/DB.fs:1016.Action: leaving this issue open for the deprecation / rename / drop workflow. Scans performed:
rg 'TODO|FIXME|HACK|XXX' src scriptsandrg 'GetVahterStats|GetVahterActionStats' src.Evidence:
.github/AGENT-FLOWS-REDESIGN.md: relevant section documenting dead tables / dead code.src/VahterBanBot/DB.fs:1016(live path:GetVahterActionStats).If you want, I can attempt to locate all SQL migrations that still populate the legacy tables so the deprecation PR can include a rename/drop plan — say the word and I'll gather them.
github-actions commentedon Sep 11, 2026
Project assessment bump (2026-09-11)
Still relevant. Quick context from today's scan:
TODO|FIXME|HACK|XXXinsrc/andscripts/and found one comment insrc/VahterBanBot/Types.fs(already surfaced and closed by Leftover TODO comment: blanket-applying RequireQualifiedAccess in VahterBanBot.Types #332).GetVahterStatsreference and found the same root-cause documented in.github/AGENT-FLOWS-REDESIGN.mdand in this issue.src/VahterBanBot/DB.fscontains theGetVahterStatscode paths referenced by the redesign doc.This issue remains the canonical place to track the rename/delete plan for the 10 dead Vahter tables and the associated code deletion (
GetVahterStats/VahterStats).Scan commands run (sanity):
grep -rn "TODO\\|FIXME\\|HACK\\|XXX" src/ scripts/ --include='*.fs' --include='*.sh'rg "GetVahterStats"gh issue list --state all --label project --limit 100No new issue created — bumping this existing issue as it already captures the full remediation plan.
github-actions commentedon Sep 12, 2026
Project assessment bump (2026-09-12)
Still relevant. Quick findings to add:
No additional evidence found that would change the recommended next steps in #330 (rename first / observe / drop later).
github-actions commentedon Sep 15, 2026
Project assessment bump (2026-09-15)
Still relevant.
Evidence
src/VahterBanBot/Types.fs:28—TODOfound during a repo scan forTODO|FIXME|HACK|XXXinsrc/andscripts/..github/AGENT-FLOWS-REDESIGN.md:125and.github/prompts/project.md:112mentionDB.fs:866 GetVahterStatsas dead code.Context / Suggested Approach
This is the same root cause as this issue (deprecate pre-event-sourcing tables, rename then drop, delete dead-code references). Continue with the planned deprecation: add migrations to rename/drop legacy tables, remove dead code usages (e.g.
GetVahterStats), and update tests and docs accordingly.(Automated project assessment scan performed: grep for
TODO|FIXME|HACK|XXXinsrc/&scripts/, inspected.github/bots.yml, and verified doc paths.)Szer commentedon Sep 15, 2026
Step 3 (observe) — verdict 2026-09-15: quiet. Step 4 (drop) prepared as #494.
V41 (#362) was applied in prod on 2026-08-12 09:53 UTC; eight migrations (V42–V49) have deployed green on top since. Observation-window evidence, 2026-08-12 → 2026-09-15:
pg_stat_user_tablesfor all 10deprecated_*tables: 0 inserts/updates/deletes since the rename; lastseq_scan/idx_scanon any of them is 2026-08-13 (rename day +1, exploratory queries) — nothing has touched them for 32 days.namespace="vahter-bot", full window (~1.2M lines scanned): zero lines containingdeprecated_, zero42P01/undefined_table/relation ... does not existerrors.scripts/queries/**, test seed or Grafana dashboard references the old or thedeprecated_names (thebanned_by_bothits inDB.fsare column aliases inside anevent-derived query).Drop-safety facts (from
pg_constraint/information_schemain prod):deprecated_user); no live table depends on any of them.banned_id_seq,banned_by_bot_id_seq,callback_internal_id_seq,llm_triage_id_seq,message_id_seq,vahter_actions_id_seq) are owned by these tables and drop with them.deprecated_message677 MB,deprecated_banned_by_bot116 MB).PRs (both green, both unmerged, owner decision):
V50__drop_deprecated_pre_event_sourcing_tables.sql: a singleDROP TABLE IF EXISTSover the 10 tables, deliberately withoutCASCADEso any unexpected external dependent fails the migration loudly. LocalVahterBanBot.Tests(full V1→V50 chain on Testcontainers) 302/302. Merging is irreversible — the deploy runs it against prod; the PR body carries apg_dumprecipe to take first..github/prompts/project.mdno longer cites the deletedDB.fs:866 GetVahterStatsas its dead-code example (that stale example is what made the project agent bump this issue daily);AGENT-FLOWS-REDESIGN.mdandbots.ymlnow describe the V41 rename / V50 drop instead of "frozen legacy tables".Once #494 is merged and
flyway_schema_historyshows version 50 succeeded, this issue can be closed.VahterBanBot: drop the 10 deprecated pre-event-sourcing tables (V50) (#…
docs: stop citing deleted GetVahterStats; note V41 rename / V50 drop …
CI: project agent — silence-by-default bumps, gh shim enforcement, no…
Szer commentedon Sep 15, 2026
Done. V50 (
DROP TABLE IF EXISTS deprecated_*×10, PR #494) merged 2026-09-15 20:52 UTC, vahter deploy run 35022290552 green, flyway_schema_history v50 success at 20:58 UTC, information_schema shows zero deprecated_* tables. Docs updated in #495.