Transaction categorization on Silvia Analyst
Status: phase 1 (shadow) in this PR, off by default. Plaid’s category mapping stays the fallback. User rules and hand edits always win. Silvia Analyst fills in what those miss, and every user fix becomes a training label.Non-invasive by design
- Two new tables,
txcat_predictionsandtxcat_labels. Nothing existing gets a new column, trigger or foreign key. - Ids that point at existing tables (
transaction_id, category uuids) are soft references with no FK, so existing tables never depend on txcat. Dropping the two tables removes the feature. - Phase 1 only reads existing tables. Users see nothing new.
- The tables live in
publicwith atxcat_prefix rather than their own schema, because a new schema has to be exposed in the Supabase API settings before supabase-js can reach it.
Pipeline
Tables
How it maps onto existing tables
The model sees numbered categories and numbered transactions and answers with
numbers. It never sees or writes a uuid, and a number outside the list is
dropped.
Turning it on
- Apply
supabase/migrations/20260925_02_txcat_shadow.sql. - Set
TXCAT_SHADOW_ENABLED=true(and optionallyTXCAT_SHADOW_MAX_ROWS, default 500, at most 1000) in the environment. - Set
TXCAT_MODEL_VERSIONto the model behind the endpoint (defaultsilvia-analyst-1.0). The served name (quant-analyst) is the same across versions, so this is the only thing that tells them apart. Each version predicts once per transaction, so a new version re-predicts recent rows and two versions can be compared on the same transactions. - The cron runs daily at 07:45 UTC. It needs
QUANT_ANALYST_MODAL_URLandQUANT_ANALYST_MODAL_KEY; without them it returnsskipped.
Reading the results
After a week, labels start landing. The first question is accuracy on user decisions, model vs the Plaid mapping:Phase 2 (not in this PR)
Only if the model beats the Plaid mapping on user decisions: writetransactions.category_uuid for rows with no rule_id, auto_categorized
not false, and confidence above a cutoff picked from the phase 1 data, and
record which rows were written so the change can be rolled back.