feat(balance): schema & migrations v14/v15 + types (#210) #219
No reviewers
Labels
No labels
autopilot:pending-human
source:analyste
source:defenseur
source:human
source:medic
status:approved
status:blocked
status:in-progress
status:needs-clarification
status:needs-fix
status:ready
status:review
status:triage
type:bug
type:feature
type:infra
type:refactor
type:schema
type:security
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: maximus/Simpl-Resultat#219
Loading…
Reference in a new issue
No description provided.
Delete branch "issue-210-schema-migrations"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Resolves #210
Fondations de schema pour la phase « detail par titre » (Etape 2), purement additives. v1-v13 jamais editees, checksums intacts. Version max courante = 13 → ajout de v14 + v15.
Migrations
balance_holdings_schema.sql, expose viaBALANCE_HOLDINGS_SCHEMA, applique tel quel par la migration inline) :balance_securities— catalogue d instruments ;symbolnormalise (upper/trim) +COLLATE NOCASE UNIQUE(anti-doublons de casse, caveat SEC/ARCH) ;asset_typeCHECK (stock|crypto) ;currencyDEFAULT CAD ; timestamps.balance_snapshot_holdings— detail par titre d une ligne de snapshot ; FKsnapshot_line_id→balance_snapshot_lines(CASCADE),security_id→balance_securities(RESTRICT) ;valuedenormalise ;book_cost/price_source/price_fetched_atnullable ;UNIQUE(snapshot_line_id, security_id)+ 2 index.balance_accounts.kind(simple|detailed, NOT NULL DEFAULT simple, CHECK) +detailed_since DATE; backfillkind=detailedpour les comptes sous categoriepriced. Deux ADD COLUMN distincts (contrainte SQLite), idempotent.Parite & types
consolidated_schema.sql(nouveaux profils) a parite : 2 tables + 2 index + colonneskind/detailed_since+ backfill v15 (no-op pour les 4 starters simples, reproduit pour un futur starter price).BalanceAccountKind,BalanceSecurity,BalanceSnapshotHolding(+ variante jointeWithSecurity) ;BalanceAccountgagnekind+detailed_since?;BalanceAccountWithCategoryexposekindETcategory_kind.Decisions
balance_schema.sql(reference v9) non modifie : c est le SQL checksum-locked de v9 ; le toucher casserait le checksum. La nouvelle reference du v14 estbalance_holdings_schema.sql(le v9 reste fige a cote de sa migration inline). MEDIUM.IF NOT EXISTS/ADD COLUMNadditif pour l idempotence (convention v9..v13).Quality gate
cargo check: PASScargo test: PASS — 89 tests (+9 nouveaux v14/v15 + parite consolidee ; le test chaine v9→v13 reste vert)npm run build(tsc + vite) : PASSnpm test(vitest) : PASS — 552 testsGenerated autonomously by /autopilot run of 2026-06-06
Foundations for 'détail par titre' (Étape 2), purely additive — v1-v13 are untouched and their checksums stay intact. v14 (balance_holdings_schema.sql, applied verbatim via BALANCE_HOLDINGS_SCHEMA): - balance_securities: instrument catalogue, symbol normalized + COLLATE NOCASE UNIQUE (no case-dupes), asset_type CHECK ('stock','crypto'), currency CAD. - balance_snapshot_holdings: per-security breakdown of a snapshot line, FK to balance_snapshot_lines (CASCADE) + balance_securities (RESTRICT), value denormalized, UNIQUE(snapshot_line_id, security_id) + 2 indexes. v15 (inline): balance_accounts gains kind ('simple'|'detailed', NOT NULL DEFAULT 'simple', CHECK) + detailed_since DATE; backfills kind='detailed' on accounts under a priced category. Two single-column ADDs (SQLite), idempotent. consolidated_schema.sql brought to parity: 2 tables + 2 indexes + kind/ detailed_since columns + the v15 backfill (no-op for the 4 simple starters, reproduced for future priced starters). TS types: BalanceAccountKind, BalanceSecurity, BalanceSnapshotHolding (+ WithSecurity join variant); BalanceAccount gains kind + detailed_since; BalanceAccountWithCategory exposes both kind and category_kind. Tests: +9 (v14/v15 via V14_SQL/V15_SQL consts + db_through_v13 helper, plus a consolidated parity test). cargo test 89 passed, npm build + 552 vitest green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>Adversarial review — PR #219 (issue #210) — Bilan Étape 2, schema & migrations v14/v15 + types
Verdict : APPROVE (1 suggestion importante à traiter avant les PRs consommatrices, 2 mineures)
Fondations propres, purement additives, et exceptionnellement bien testées. La sécurité des migrations est vérifiée à la source.
Vérifié ✓
Migration safety (v1-v13 intactes)
@@ -203,6 +203,53,@@ -1793,6 +1840,@@ -1899,5 +2310). Les entrées v1→v13 ne sont pas touchées ; v14/v15 sont appended.balance_schema.sql(= SQL checksum-locké de v9) non modifié. Aucun risque de casser le SHA-384 sur les profils existants. 5 fichiers seulement.Idempotence
CREATE TABLE IF NOT EXISTS×2 +CREATE INDEX IF NOT EXISTS×2. OK.ADD COLUMNdistincts (contrainte SQLite mono-colonne), backfillUPDATE … WHERE … IN (SELECT … kind='priced')naturellement idempotent.Schema correctness
symbol TEXT NOT NULL COLLATE NOCASE UNIQUE✓ ;asset_type … CHECK (… IN ('stock','crypto'))✓ ;kind … CHECK (… IN ('simple','detailed'))✓.snapshot_line_id … ON DELETE CASCADE✓ ;security_id … ON DELETE RESTRICT✓.UNIQUE(snapshot_line_id, security_id)✓. 2 index présents ✓.balance_snapshot_lines(id)(pas snapshot+account) — justifié parUNIQUE(snapshot_id, account_id)de la table lignes. Bon choix.snapshot_lines(238) etsecurities(275) avantholdings(285). OK.Parité consolidated_schema (drift-proof)
CREATE TABLE(inlinebalance_holdings_schema.sqlvs consolidated) : byte-identiques. Les 2 index et le backfill v15 sont reproduits. Le backfill consolidated cible le mêmeWHERE kind='priced'que l'inline. Pas de divergence silencieuse.Types
BalanceAccountKind,BalanceSecurity,BalanceSnapshotHolding(+WithSecurity) ✓.BalanceAccountWithCategory extends BalanceAccount→ héritekind/detailed_since, etcategory_kind(ligne 682) coexiste aveckind. Conforme.Tests (vrais, pas juste compile)
PRAGMA foreign_keys=ONconfirmé dansfresh_db/consolidated_db), UNIQUE, backfill priced→detailed vs cash→simple, default 'simple', parité consolidated.V14_SQL = BALANCE_HOLDINGS_SCHEMA(même const) = drift-proof par construction.🟡 Suggestion importante (non bloquante au scope « fondations », mais à corriger avant tout consommateur — #211/#214/#216)
balance.service.ts:BalanceAccount.kinddéclaré requis mais jamais SELECT.listBalanceAccounts(l.306-310) etgetBalanceAccount(l.324) sélectionnent des colonnes explicites sansa.kindnia.detailed_since.db.select<BalanceAccount[]>(…)/<BalanceAccountWithCategory[]>(…)sont des assertions de type → tsc ne bronche pas (CI verte attendue), mais à l'exécutionaccount.kind === undefinedalors que le type garantit unBalanceAccountKindnon-nullable.if (account.kind === 'detailed')obtiendra silencieusementfalse. Lekindrequis (vs?) rend le piège plus tranchant.a.kind, a.detailed_sinceaux deux SELECTs (les colonnes existent après v15). À faire ici idéalement, ou au plus tard en ouverture de #211 — à ne pas oublier.🟢 Mineur
BalanceSnapshotHolding.price_sourcetypéstring | nullavec doc'manual' | 'maximus-api' | NULL— un union littéral serait plus strict (cohérent avec le patternBalanceAccountKind). Cosmétique.Excellent travail de fondation. Les commentaires inline (caveats SEC/ARCH, rationale denormalisation/FK) sont de qualité.