Skip to content

docs(adr-022): taxonomie des catégories + catégorisation assistée 100 % locale#269

Merged
thierryvm merged 2 commits into
mainfrom
docs/adr-022-categories
Jul 26, 2026
Merged

docs(adr-022): taxonomie des catégories + catégorisation assistée 100 % locale#269
thierryvm merged 2 commits into
mainfrom
docs/adr-022-categories

Conversation

@thierryvm

@thierryvm thierryvm commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Tranche les deux arbitrages que tu m'as délégués. Décision uniquement, zéro code.

Je ne fais pas exactement ce que tu as demandé — voici pourquoi

Tu voulais des sous-catégories pour générer des graphiques. L'objectif est juste, le moyen coûterait cher : un choix à deux niveaux double le coût de chaque saisie.

L'étude de marché est sans appel sur ce qui tue les apps à saisie manuelle. Goodbudget, le comparable le plus direct d'Ankora : « si vous passez trois jours sans ouvrir l'app, vous avez un arriéré de transactions à retrouver et à saisir ». La friction de saisie tue le produit avant que le graphique n'ait servi.

Ma décision : liste plate à la saisie, colonne group sur la catégorie pour l'agrégation. Un seul geste pour toi, deux niveaux dans les graphiques — « Vie courante 640 € » se déplie en Courses, Restaurant, Carburant.

Tu obtiens le résultat visé sans en payer le prix.

La taxonomie — 18 catégories, 5 groupes

Groupe Catégories
Vie courante Courses · Restaurant & café · Carburant · Shopping & vêtements · Loisirs & sorties
Logement Logement ᴱ · Énergie · Internet & télécom
Obligations Assurances ᴱ · Taxes ᴱ · Santé ᴱ · Crédits
Famille Famille ᴱ · Animaux
Divers Abonnements ᴱ · Cadeaux · Autres ᴱ

ᴱ = existante, conservée telle quelle (des lignes en prod la référencent, la renommer casserait l'historique).

18 et pas 60 : Monarch en livre ~60, mais sa propre recommandation de praticien est 3-6 groupes × 3-12 catégories. Au-delà, on ne choisit plus — on prend la première qui ressemble, ou « Autres ».

Oui, l'automatisation est faisable — et c'est un avantage

Les banques belges catégorisent parce qu'elles voient tes transactions. KBC range d'ailleurs la fonction sous « Privacy », présentée comme un confort que tu accordes.

Ankora lit le libellé que tu tapes. Rien ne quitte le serveur.

  • Règles apprises : tu catégorises « Intermarché » en Courses → mémorisé. C'est le mécanisme d'Actual Budget.
  • Dictionnaire de départ : Delhaize/Colruyt/Aldi → Courses, Q8/Total/Shell → Carburant, Proximus/Telenet → Télécom. Évite l'écran vide du premier mois.
  • À la correction : « cette fois seulement » ou « toujours pour ce libellé », proposé dans le flux (pattern Revolut), pas dans un écran de règles séparé.

Désactivable, avec la liste des règles apprises supprimables une par une.

NORTH_STAR

« Aucune question importante sans réponse » remplace « 8 sections obligatoires ». Une liste de sections contraint le contenu là où ton problème est la hiérarchie.

L'invariant à tester, pas à supposer

La catégorie n'entre dans aucun calcul. Changer la catégorie d'une dépense ne doit déplacer aucun montant agrégé. C'est ce qui rend une taxonomie ratée bon marché à corriger.

Fichier : docs/adr/ADR-022-taxonomie-categories-et-categorisation-assistee.md

Fourteen agents read the actual code and studied the market, a synthesis agent
turned it into a roadmap, and three adversarial critics were told to destroy
that roadmap rather than approve it. This is the revision that survived them.

Three premises of the original request turned out to be false, and each changes
what needs building.

Editing an expense already works. ExpenseEditDrawer.tsx is 217 lines, mounted at
ExpensesClient.tsx:337, calls updateExpenseAction and has four test blocks. Its
trigger is a muted pencil sitting next to a red bin that takes the eye. An
affordance defect, not missing code — repaired in step 1 for almost nothing.

Real categories already exist too. The table has shipped since the initial
schema, eight are seeded at every signup with colour tokens, expenses.category_id
exists, the Zod schema carries it and createExpenseAction writes it. Two lines of
UI unplug the whole thing: categoryId: null hardcoded, and an RSC mapping that
drops the field before it reaches the component. Not a model to redesign, a wire
to reconnect.

And the menus are not the problem. The navigation exposes six table names as
seven equal-rank destinations, three of which describe the same mental act —
money going out. No visual redesign fixes that, which is why polish is
deliberately last rather than first.

The market study reframes the product itself: Belgian banks already ship free
automatic categorisation, so manual entry loses that fight outright. What none of
them knows is what is already committed — and that figure cannot be derived from
a transaction history, it has to be declared. It is the one thing PSD2 will never
supply, which turns the missing aggregation from a handicap into the position.

Seventeen steps, one branch each, ordered so no step depends on a later one.
Step 1 is deliberately small and independently shippable, per the rule that
nothing starts until it is finished.

The critics earned their place. They killed an ON DELETE SET NULL that would have
made deleting a category impossible on three tables at once, forced a rollback
gate on every migration (one Supabase project, Free tier, no PITR, a real user),
and pushed the only remaining migration off the critical path entirely.
… at entry

@Thierry asked for sub-categories so charts could break spending down. The goal
is right; the mechanism would have cost more than it returns, so this ADR takes
a different route to the same result and says why.

A two-level pick doubles the cost of every expense recorded. The market study is
unambiguous about what kills manually-entered budget apps — Goodbudget, the
closest comparable to Ankora: "if you go three days without opening the app, you
have a backlog of transactions to remember and enter". Entry friction ends the
product before any chart gets used.

So: a flat list at entry, a group column on the category for aggregation. One
gesture for the user, two levels in the charts — "Vie courante 640 €" unfolds
into Courses, Restaurant, Carburant.

Eighteen categories across five groups. The eight already seeded are kept
verbatim, because production rows reference them and renaming would break the
history. Ten are added, all for day-to-day spending, which is what the original
eight were missing — they are a fixed-charges taxonomy, and without the addition
most manual entries would land in "Autres".

Assisted categorisation turns out to be an advantage rather than catching up.
Belgian banks categorise because they see your transactions; KBC files the
feature under Privacy, framed as a convenience you grant. Ankora reads the label
you type and nothing leaves the server. Rules are learned from your own
corrections — Actual's mechanism — over a seed dictionary of Belgian merchants
so the first month is not an empty screen. The correction offers "this once" or
"always for this label" inside the flow, per Revolut, rather than in a separate
rules screen.

The free colour picker stays banned: an arbitrary hex breaks WCAG AA contrast and
the visual contract. That is a design decision, not a limitation.

NORTH_STAR's "8 mandatory dashboard sections" from April collide with July's "the
users are lost". A list of mandatory sections constrains content where the
problem is hierarchy, so "no important question left unanswered" replaces it.

One invariant to test rather than assume: the category enters no calculation.
Changing an expense's category must move no aggregate. That is what keeps a
wrong taxonomy cheap to correct.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @thierryvm, your pull request is larger than the review limit of 150000 diff characters

@vercel

vercel Bot commented Jul 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ankora Ready Ready Preview, Comment Jul 26, 2026 12:28pm

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@github-actions github-actions Bot added status:review-needed Ready for review type:docs Documentation only labels Jul 26, 2026
@thierryvm
thierryvm merged commit 33cecf9 into main Jul 26, 2026
10 checks passed
@thierryvm
thierryvm deleted the docs/adr-022-categories branch July 26, 2026 12:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status:review-needed Ready for review type:docs Documentation only

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant