Ctrl K
ComunidadGuías…

scripts/recategorize-plan.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Recategorize plan

Goal: every transaction carries the category of what was bought, every rule is one
the data supports, and new rows after each sync land the same way.

## Signals, strongest first (per non-manual row)

1. **Hold list** - rows the owner left for review (Prosper, RR Bowker): untouched.
2. **User rules** (`rules.source != 'llm'`) - the owner's intent. Applied as written.
3. **Order receipts** (`lib/orders.ts`) - Amazon order/shipment emails (Gmail via
   `gogx`, read-only) or an Amazon "Your Orders" CSV in `data/`. A card charge
   matches a receipt by exact total and a date window. The receipt's Amazon
   category label ("Electronics", "Snack Foods") or item names decide the category.
4. **Descriptor rules** (`DESCRIPTOR_RULES` in `lib/recategorize.ts`) - code rules
   on the raw bank descriptor for multi-category merchants and known facts:
   Amazon Tips / Grocery Subscription -> Groceries, Kindle -> Subscriptions,
   Amazon Media EU / Amazon.com Services payments (credits) -> Income (book
   royalties), Apple.com/bill -> Subscriptions, Uber trips vs Uber One, Walmart+
   vs Walmart, Wawa fuel vs store, Costco gas, student/auto loans -> Loans,
   JPMorgan Chase Ext Trnsfr -> Loans (owner: a real payment out), cleaner
   payments -> Home.
5. **Single-category LLM rules** that survive the audit.
6. **LLM** (fast lane, batches of 20, sequential) with name, merchant, Plaid
   detailed, amount, account type. Writes category, a clean display merchant
   when Plaid gave none, and confidence. Below 0.6 -> "Other" (To-Review).
7. Fallback: `mapPfc(primary, detailed)`.

Never written: `category_source='manual'` rows. Instead `flagManualPicks()` lists
manual picks whose category disagrees with signals 3-4 or with a strong Plaid
detailed category.

## Taxonomy additions (counts measured on the 2026-10-18 copy)

Electronics, Household, Clothing, Pets, Personal Care, Loans. Each is justified
by the dry-run counts in the report; prompts and `mapPfc` read `CATEGORIES`.

## Rules audit (`scripts/audit-rules.mts`)

- Delete LLM rules on multi-category merchants (Amazon, Apple, Walmart, Costco,
  Uber, Target, Google, Wawa) - per-row signals replace them.
- Delete LLM rules whose substring is too broad (matches rows of 2+ categories
  in the data, or a pattern under 4 chars).
- Delete LLM rules duplicated or contradicted by a user rule.
- Add LLM rules only for merchants whose rows (after the pass) are 100% one
  category with 3+ rows.
- Manual rules are never deleted; suspicious ones are printed.

## Moving forward

`classifyNewTransactions({ llm })` after each sync: refresh receipts (best
effort), then run signals 3-7 on rows still `category_source='plaid'`, capped.
Never throws, never touches manual rows.

## Evidence

`scripts/recategorize-all.mts` (dry-run default, `--apply`): before/after per
category, 40 sample changes, flagged manual picks. `scripts/verify-recategorize.mts`
asserts key cases on a throwaway db.