schema: migration v17 — the full import format on import_sources #334

Closed
maximus wants to merge 1 commit from issue-323-migration-v17 into issue-326-corpus-fixtures-csv
Owner

Link 2 of a 10-link linear stack (spec-plan-import-csv-format.md). Based on issue-326-corpus-fixtures-csv (link 1, PR #333) — not on main.

Resolves #323

What this does

import_sources carried only the mechanical CSV settings (delimiter, encoding, date format, column mapping). The two fields that decide how an amount is readamount_mode and sign_convention — existed only on import_config_templates. That asymmetry is the root bug of this chantier: restoring a saved source re-inferred the mode from the mapping and hardcoded signConvention: "negative_expense" (useImportWizard.ts:321-323), so a source configured with positive expenses silently flipped back on its second import.

Migration v17 puts the full format on the source. After it, both tables carry the same eight format fields and the asymmetry is gone structurally. No observable behaviour changes — the backfill reproduces exactly what the code did on the fly.

Migration v17 — strictly additive

v1 to v16 are untouched; the diff is pure insertion (523 lines added, 0 removed), which is what "checksums intact" reduces to here — no checksum harness exists in this crate (per the /review-spec revision).

Column Definition Note
amount_mode TEXT NOT NULL DEFAULT 'single' + CHECK Admits absolute_indicator from the start so the third amount mode ships without another migration, while the DB still refuses a corrupted value today
sign_convention TEXT NOT NULL DEFAULT 'negative_expense' + CHECK
header_signature TEXT Normalized header labels from the last successful import (drift detection)
template_id INTEGER REFERENCES import_config_templates(id) ON DELETE SET NULL Provenance tag only, never re-read as format

The CHECKs follow the revision block, which overrides the original body's "pas de contrainte CHECK". Same pattern as v15 on balance_accounts.kind.

BackfillUPDATE import_sources SET amount_mode = 'debit_credit' WHERE column_mapping LIKE '%debitAmount%', reproducing useImportWizard.ts:321 exactly. LIKE rather than json_extract so the migration depends on no JSON1 extension in the bundled SQLite.

sign_convention is deliberately not backfilled: its DEFAULT restores precisely the value the code hardcoded at :323, the only past convention that can be inferred. Guessing anything else would silently rewrite meaning.

Consolidated mirror

The four columns are mirrored into consolidated_schema.sql, placed before created_at/updated_at with a comment naming the migration — the house convention (cf. balance_accounts v12/v15).

Per the revision block, the rationale is not that new profiles need them there: that script runs after every migration and only uses CREATE TABLE IF NOT EXISTS, so those columns are inert on the production path and a new profile receives them from v17. The mirror exists so the file stays the tested reference definition — the parity test is what gives it teeth.

Tests — 5 new (111 Rust total, was 106)

  • migration_v17_applies_on_a_populated_v16_db — a configured source, a template and an imported_files child row; every pre-existing field survives, new columns land on their defaults, the FK holds.
  • migration_v17_backfill_reproduces_the_wizard_rule — 5 mapping shapes (both columns, debit only, single amount, credit only, minimal).
  • migration_v17_checks_reject_unknown_enum_valuesabsolute_indicator accepted, garbage refused.
  • migration_v17_template_id_is_a_nullable_provenance_tag — editing a template changes no linked source (the spec's acceptance criterion); deleting one nulls the tag and leaves the format intact; a dangling tag is refused.
  • consolidated_schema_matches_v17_chain_on_import_sources_at_parity — the parity test the revision asked for, on the model of consolidated_schema_has_holdings_tables_and_kind_at_parity: 15 columns compared name/type/NOT NULL/DEFAULT, the FK compared with its ON DELETE action, and the CHECKs proven behaviourally on both definitions (pragma does not expose them).

The parity test was mutation-checked, not trusted on a first-try green: drifting the consolidated sign_convention DEFAULT made the column comparison fail with a readable diff, and narrowing the amount_mode CHECK made the behavioural half fail. A guard that cannot fail is not a guard.

Verification

  • cargo check clean, no warnings
  • cargo test 111 passed (106 baseline + 5)
  • npm test 924 passed — unchanged. A schema migration changes no parsing behaviour, so none of link 1's 8 KNOWN DEFECT blocks flipped, as expected
  • npm run build green

Reviewer notes

  • V17_SQL is a hand-kept copy of the inline migration string (the V13/V15/V16 shape), not an include_str! shared constant (the V14 shape, which would have zero drift risk). Chosen because the issue asks for "le pattern V13_SQL a V16_SQL", v15 is the closest analogue and is inline, and V14 uses a file only because it is a whole multi-table schema. The parity test is the drift mitigation.
  • db_pre_v17() applies v1 + v3 + v5 only, not the full v1→v16 chain. Those are exactly the three migrations that shape import_sources and import_config_templates; v2, v4 and v6-v16 never touch them. The claim is not taken on faith — the parity test would fail if any other migration shaped these tables, since consolidated_schema.sql is the full post-v16 truth. Replaying all 16 would mean hand-copying V1_SQL..V9_SQL constants no test needs, multiplying the very drift risk the pattern already carries.
  • Parity compares columns sorted by name, not by physical order: ALTER TABLE can only append, while the consolidated CREATE TABLE places them mid-table per house convention. Order is not part of the contract.
  • No CHANGELOG, no docs/architecture.md, no CLAUDE.md in this PR. This issue changes no observable behaviour, and the spec plan centralizes both in Issue 10 (last link) on purpose, to avoid rebase drift across a 10-link stack (spec-plan line 224). CLAUDE.md still says "16 migrations inline (v1→v16)" and will need the bump there.

Generated autonomously by /autopilot run of 2026-08-13

Link **2 of a 10-link linear stack** (`spec-plan-import-csv-format.md`). Based on `issue-326-corpus-fixtures-csv` (link 1, PR #333) — **not** on `main`. Resolves #323 ## What this does `import_sources` carried only the mechanical CSV settings (delimiter, encoding, date format, column mapping). The two fields that decide how an amount is **read** — `amount_mode` and `sign_convention` — existed only on `import_config_templates`. That asymmetry is the root bug of this chantier: restoring a saved source re-inferred the mode from the mapping and hardcoded `signConvention: "negative_expense"` (`useImportWizard.ts:321-323`), so a source configured with positive expenses silently flipped back on its second import. Migration v17 puts the full format on the source. After it, both tables carry the same eight format fields and the asymmetry is gone structurally. **No observable behaviour changes** — the backfill reproduces exactly what the code did on the fly. ## Migration v17 — strictly additive `v1` to `v16` are untouched; the diff is **pure insertion** (523 lines added, 0 removed), which is what "checksums intact" reduces to here — no checksum harness exists in this crate (per the `/review-spec` revision). | Column | Definition | Note | |---|---|---| | `amount_mode` | `TEXT NOT NULL DEFAULT 'single'` + `CHECK` | Admits `absolute_indicator` **from the start** so the third amount mode ships without another migration, while the DB still refuses a corrupted value today | | `sign_convention` | `TEXT NOT NULL DEFAULT 'negative_expense'` + `CHECK` | | | `header_signature` | `TEXT` | Normalized header labels from the last successful import (drift detection) | | `template_id` | `INTEGER REFERENCES import_config_templates(id) ON DELETE SET NULL` | **Provenance tag only**, never re-read as format | The `CHECK`s follow the revision block, which overrides the original body's "pas de contrainte `CHECK`". Same pattern as v15 on `balance_accounts.kind`. **Backfill** — `UPDATE import_sources SET amount_mode = 'debit_credit' WHERE column_mapping LIKE '%debitAmount%'`, reproducing `useImportWizard.ts:321` exactly. `LIKE` rather than `json_extract` so the migration depends on no JSON1 extension in the bundled SQLite. `sign_convention` is deliberately **not** backfilled: its `DEFAULT` restores precisely the value the code hardcoded at `:323`, the only past convention that can be inferred. Guessing anything else would silently rewrite meaning. ## Consolidated mirror The four columns are mirrored into `consolidated_schema.sql`, placed before `created_at`/`updated_at` with a comment naming the migration — the house convention (cf. `balance_accounts` v12/v15). Per the revision block, the rationale is **not** that new profiles need them there: that script runs *after* every migration and only uses `CREATE TABLE IF NOT EXISTS`, so those columns are **inert on the production path** and a new profile receives them from v17. The mirror exists so the file stays the tested reference definition — the parity test is what gives it teeth. ## Tests — 5 new (111 Rust total, was 106) - `migration_v17_applies_on_a_populated_v16_db` — a configured source, a template and an `imported_files` child row; every pre-existing field survives, new columns land on their defaults, the FK holds. - `migration_v17_backfill_reproduces_the_wizard_rule` — 5 mapping shapes (both columns, debit only, single amount, credit only, minimal). - `migration_v17_checks_reject_unknown_enum_values` — `absolute_indicator` accepted, garbage refused. - `migration_v17_template_id_is_a_nullable_provenance_tag` — editing a template changes no linked source (the spec's acceptance criterion); deleting one nulls the tag and leaves the format intact; a dangling tag is refused. - `consolidated_schema_matches_v17_chain_on_import_sources_at_parity` — the parity test the revision asked for, on the model of `consolidated_schema_has_holdings_tables_and_kind_at_parity`: 15 columns compared name/type/NOT NULL/DEFAULT, the FK compared with its `ON DELETE` action, and the `CHECK`s proven behaviourally on both definitions (pragma does not expose them). **The parity test was mutation-checked**, not trusted on a first-try green: drifting the consolidated `sign_convention` DEFAULT made the column comparison fail with a readable diff, and narrowing the `amount_mode` CHECK made the behavioural half fail. A guard that cannot fail is not a guard. ## Verification - `cargo check` clean, no warnings - `cargo test` **111 passed** (106 baseline + 5) - `npm test` **924 passed** — unchanged. A schema migration changes no parsing behaviour, so none of link 1's 8 `KNOWN DEFECT` blocks flipped, as expected - `npm run build` green ## Reviewer notes - **`V17_SQL` is a hand-kept copy** of the inline migration string (the V13/V15/V16 shape), not an `include_str!` shared constant (the V14 shape, which would have zero drift risk). Chosen because the issue asks for "le pattern `V13_SQL` a `V16_SQL`", v15 is the closest analogue and is inline, and V14 uses a file only because it is a whole multi-table schema. The parity test is the drift mitigation. - **`db_pre_v17()` applies v1 + v3 + v5 only**, not the full v1→v16 chain. Those are exactly the three migrations that shape `import_sources` and `import_config_templates`; v2, v4 and v6-v16 never touch them. The claim is not taken on faith — the parity test would fail if any other migration shaped these tables, since `consolidated_schema.sql` is the full post-v16 truth. Replaying all 16 would mean hand-copying V1_SQL..V9_SQL constants no test needs, multiplying the very drift risk the pattern already carries. - **Parity compares columns sorted by name**, not by physical order: ALTER TABLE can only append, while the consolidated CREATE TABLE places them mid-table per house convention. Order is not part of the contract. - **No CHANGELOG, no `docs/architecture.md`, no `CLAUDE.md`** in this PR. This issue changes no observable behaviour, and the spec plan centralizes both in Issue 10 (last link) on purpose, to avoid rebase drift across a 10-link stack (spec-plan line 224). `CLAUDE.md` still says "16 migrations inline (v1→v16)" and will need the bump there. --- Generated autonomously by /autopilot run of 2026-08-13
maximus added 1 commit 2026-08-13 16:57:11 +00:00
schema: add migration v17 carrying the full import format on import_sources
All checks were successful
PR Check — Rust / rust (pull_request) Successful in 9m27s
bd1085c148
`import_sources` carried only the mechanical CSV settings. The two fields that
decide how an amount is READ -- `amount_mode` and `sign_convention` -- lived
only on `import_config_templates`. That asymmetry is the root bug of this
chantier: restoring a saved source re-inferred the mode from the mapping and
hardcoded `signConvention: "negative_expense"` (useImportWizard.ts:321-323), so
a source configured with positive expenses silently flipped back on its second
import. After v17 both tables carry the same eight format fields.

v17 is strictly additive -- v1 to v16 are untouched, and the diff is pure
insertion. The four columns are defaulted or nullable so the ALTERs are safe on
a populated database:

- amount_mode / sign_convention carry a CHECK, same pattern as v15 on
  balance_accounts.kind. amount_mode admits 'absolute_indicator' from the start
  so the third amount mode ships without another migration, while the database
  still refuses a corrupted value today.
- header_signature stores the normalized header labels seen at the last
  successful import, for drift detection.
- template_id is a provenance tag only, never re-read as format: the eight
  source columns are authoritative, so editing a template changes no linked
  source. ON DELETE SET NULL keeps the source and its format when a template
  goes.

The backfill reproduces exactly the rule the wizard applied on the fly, so no
source changes behaviour on migration. It tests `column_mapping LIKE
'%debitAmount%'` rather than json_extract, so the migration depends on no JSON1
extension in the bundled SQLite. sign_convention is deliberately not
backfilled: its DEFAULT restores precisely the value the code hardcoded, the
only past convention that can be inferred.

The four columns are mirrored into consolidated_schema.sql. They are inert
there on the production path -- that script runs after every migration and only
uses CREATE TABLE IF NOT EXISTS, so new profiles receive them from v17 -- but
it stays the tested reference definition, and a parity test now compares it
against the v1->v17 chain column by column, DEFAULT by DEFAULT, CHECK by CHECK
and FK by FK. Both halves of that test were mutation-checked to confirm they
fail on drift.

5 new tests (111 Rust total): v17 on a populated v16 database with a child row,
the backfill against 5 mapping shapes, the CHECKs, the provenance-tag
semantics, and the consolidated parity.

Resolves #323
maximus added the
autopilot:pending-human
label 2026-08-13 16:57:17 +00:00
Author
Owner

/pr-review — APPROVE

Migration additive propre, dont chaque affirmation du corps a ete verifiee plutot que crue. La v17 s'applique reellement sur une base v1+v3+v5 avec PRAGMA foreign_keys = ON (la condition runtime de sqlx), la parite consolidated/chaine tient a 15 colonnes et une FK identiques, et le garde de parite est genuinement sensible a la mutation — je l'ai re-teste independamment.

Verifications faites (pas de blocage)

Diff pur ajout — confirme. git diff --numstat sur la base reelle (issue-326-corpus-fixtures-csv) donne 16/0 + 507/0 = 523 insertions, 0 suppression. Aucune chaine SQL v1→v16 n'apparait au diff. J'ai aussi verifie independamment par grep de toutes les migrations de lib.rs que seules v1, v3 et v5 faconnent import_sources / import_config_templates (la ligne v6 qui les mentionne cree imported_files_new, elle n'altere pas import_sources) — la premisse de db_pre_v17() tient.

SQL rejoue hors du harnais. J'ai reconstruit la chaine (schema v1 + v3 + v5) et applique V17_SQL tel quel, foreign_keys actives :

  • les 4 ALTER passent — ADD COLUMN ... NOT NULL DEFAULT ... CHECK a deja le precedent v15 en production (balance_accounts.kind), et ADD COLUMN ... REFERENCES ... ON DELETE SET NULL satisfait la contrainte SQLite (« defaut NULL obligatoire quand les FK sont actives ») puisque la colonne n'a pas de DEFAULT ;
  • backfill : mapping avec debitAmountdebit_credit, sans → single, sign_convention = negative_expense partout ;
  • les CHECK mordent : absolute_indicator accepte, valeur inconnue refusee ;
  • ON DELETE SET NULL se declenche bien (tag a NULL, sign_convention intact) et une etiquette pendante est refusee.

Parite reproduite independamment : 15 colonnes identiques (nom/type/NOT NULL/DEFAULT), FK identique (import_config_templates, template_id, id, SET NULL).

Le garde de parite a ete re-mute de mon cote, pas seulement pris au mot :

  • deriver le DEFAULT de sign_convention dans le consolide → la comparaison de colonnes echoue ;
  • retirer absolute_indicator du CHECK consolide → invisible au pragma, mais rattrape par la moitie comportementale.

C'est exactement la separation que le corps annonce, et elle est reelle. Un garde qui ne peut pas echouer n'est pas un garde — celui-ci echoue.

La regle du backfill correspond au runtime. useImportWizard.ts:321 = mapping.debitAmount !== undefined ? "debit_credit" : "single", :323 = signConvention: "negative_expense" en dur. Ce bloc est la seule voie qui relit une source stockee, donc le DEFAULT restaure bien la valeur que le code appliquait : le point 4 des contraintes tient au challenge.

Le rationale du miroir consolide est le bon, pas l'inverse. Le commentaire dit « runs AFTER every migration / only uses CREATE TABLE IF NOT EXISTS / inert on the production path » — conforme a profile_commands.rs:116-123 (« tauri-plugin-sql applies every declared migration on Database.load; the consolidated script then... »).

Surface d'effet de bord fermee : l'export/import SREF ne fait pas de round-trip sur import_sources (dataExportService.ts:265-318 purge puis insere une source de suivi synthetique), donc les 4 colonnes n'ouvrent aucune perte de donnees a la restauration.

Reste : zero secret, zero injection (chaine de migration statique ; les format! des tests interpolent des litteraux de test), zero #[ignore]/TODO/dead code, commentaires en anglais.

Suggestions non bloquantes

  1. LIKE '%debitAmount%' est non ancre et insensible a la casse (LIKE SQLite, ASCII, par defaut). Verifie : une cle ecrite DEBITAMOUNT part en debit_credit alors que la regle JS repondrait single. Aucune source reelle ne peut declencher ca — les cles sont ecrites par le code et les valeurs de ColumnMapping sont des entiers (src/shared/types/index.ts:205-211) — donc ce n'est pas un blocage. Mais une migration est a coup unique et irreparable a posteriori : LIKE '%"debitAmount":%' fermerait la porte pour 8 caracteres.
  2. lib.rs:3488 (et les 5 cas de migration_v17_backfill_reproduces_the_wizard_rule) : les fixtures utilisent des valeurs chaines ({"date":"Date","debitAmount":"Débit"}) alors que ColumnMapping porte des index numeriques. La forme testee n'existe pas en production. Le risque n'est pas le test d'aujourd'hui mais celui de demain : quelqu'un qui durcirait le LIKE en se fiant a ces fixtures ecrirait un motif qui ne matche plus les vraies donnees. Des index entiers coutent la meme chose.
  3. db_pre_v17() saute la v6, donc imported_files est dans sa forme v1 (UNIQUE(source_id, file_hash), ON DELETE CASCADE) et non v16. Sans consequence — la v17 n'y touche pas — mais le docstring « this IS their v16 shape » n'est exact que des deux tables que la v17 faconne ; une demi-phrase leve l'ambiguite.
  4. Libelles de fixtures en francais ("Deux colonnes", "Débit seul", "Modèle A") dans un module de test integralement anglais, et interpoles dans les messages d'assertion → sortie d'echec bilingue. Cosmetique.
  5. Checklist docs du maillon 10 : le corps ne cite que CLAUDE.md:122. Il faut aussi docs/architecture.md:144-161, qui porte un tableau par migration s'arretant a v16 et qui reclamera sa ligne v17. Au passage, CLAUDE.md:73 dit encore « 7 migrations inline » (perime bien avant cette PR) — a balayer en meme temps.

Review adversariale — verdict base sur rejeu du SQL et mutation independante du garde de parite, pas sur le corps de la PR.

## `/pr-review` — APPROVE Migration additive propre, dont chaque affirmation du corps a ete verifiee plutot que crue. La v17 s'applique reellement sur une base v1+v3+v5 avec `PRAGMA foreign_keys = ON` (la condition runtime de sqlx), la parite consolidated/chaine tient a 15 colonnes et une FK identiques, et le garde de parite est genuinement sensible a la mutation — je l'ai re-teste independamment. ### Verifications faites (pas de blocage) **Diff pur ajout — confirme.** `git diff --numstat` sur la base reelle (`issue-326-corpus-fixtures-csv`) donne `16/0` + `507/0` = **523 insertions, 0 suppression**. Aucune chaine SQL v1→v16 n'apparait au diff. J'ai aussi verifie independamment par grep de toutes les migrations de `lib.rs` que seules **v1, v3 et v5** faconnent `import_sources` / `import_config_templates` (la ligne v6 qui les mentionne cree `imported_files_new`, elle n'altere pas `import_sources`) — la premisse de `db_pre_v17()` tient. **SQL rejoue hors du harnais.** J'ai reconstruit la chaine (schema v1 + v3 + v5) et applique `V17_SQL` tel quel, `foreign_keys` actives : - les 4 `ALTER` passent — `ADD COLUMN ... NOT NULL DEFAULT ... CHECK` a deja le precedent v15 en production (`balance_accounts.kind`), et `ADD COLUMN ... REFERENCES ... ON DELETE SET NULL` satisfait la contrainte SQLite (« defaut NULL obligatoire quand les FK sont actives ») puisque la colonne n'a pas de `DEFAULT` ; - backfill : mapping avec `debitAmount` → `debit_credit`, sans → `single`, `sign_convention` = `negative_expense` partout ; - les `CHECK` mordent : `absolute_indicator` accepte, valeur inconnue refusee ; - `ON DELETE SET NULL` se declenche bien (tag a NULL, `sign_convention` intact) et une etiquette pendante est refusee. **Parite reproduite independamment** : 15 colonnes identiques (nom/type/NOT NULL/DEFAULT), FK identique `(import_config_templates, template_id, id, SET NULL)`. **Le garde de parite a ete re-mute de mon cote**, pas seulement pris au mot : - deriver le `DEFAULT` de `sign_convention` dans le consolide → la comparaison de colonnes echoue ; - retirer `absolute_indicator` du `CHECK` consolide → **invisible** au pragma, mais rattrape par la moitie comportementale. C'est exactement la separation que le corps annonce, et elle est reelle. Un garde qui ne peut pas echouer n'est pas un garde — celui-ci echoue. **La regle du backfill correspond au runtime.** `useImportWizard.ts:321` = `mapping.debitAmount !== undefined ? "debit_credit" : "single"`, `:323` = `signConvention: "negative_expense"` en dur. Ce bloc est la **seule** voie qui relit une source stockee, donc le `DEFAULT` restaure bien la valeur que le code appliquait : le point 4 des contraintes tient au challenge. **Le rationale du miroir consolide est le bon, pas l'inverse.** Le commentaire dit « runs AFTER every migration / only uses CREATE TABLE IF NOT EXISTS / inert on the production path » — conforme a `profile_commands.rs:116-123` (« tauri-plugin-sql applies every declared migration on `Database.load`; the consolidated script then... »). **Surface d'effet de bord fermee** : l'export/import SREF ne fait **pas** de round-trip sur `import_sources` (`dataExportService.ts:265-318` purge puis insere une source de suivi synthetique), donc les 4 colonnes n'ouvrent aucune perte de donnees a la restauration. Reste : zero secret, zero injection (chaine de migration statique ; les `format!` des tests interpolent des litteraux de test), zero `#[ignore]`/`TODO`/dead code, commentaires en anglais. ### Suggestions non bloquantes 1. **`LIKE '%debitAmount%'` est non ancre et insensible a la casse** (LIKE SQLite, ASCII, par defaut). Verifie : une cle ecrite `DEBITAMOUNT` part en `debit_credit` alors que la regle JS repondrait `single`. Aucune source reelle ne peut declencher ca — les cles sont ecrites par le code et les valeurs de `ColumnMapping` sont des **entiers** (`src/shared/types/index.ts:205-211`) — donc ce n'est pas un blocage. Mais une migration est a coup unique et irreparable a posteriori : `LIKE '%"debitAmount":%'` fermerait la porte pour 8 caracteres. 2. **`lib.rs:3488` (et les 5 cas de `migration_v17_backfill_reproduces_the_wizard_rule`)** : les fixtures utilisent des valeurs **chaines** (`{"date":"Date","debitAmount":"Débit"}`) alors que `ColumnMapping` porte des index numeriques. La forme testee n'existe pas en production. Le risque n'est pas le test d'aujourd'hui mais celui de demain : quelqu'un qui durcirait le `LIKE` en se fiant a ces fixtures ecrirait un motif qui ne matche plus les vraies donnees. Des index entiers coutent la meme chose. 3. **`db_pre_v17()` saute la v6**, donc `imported_files` est dans sa forme v1 (`UNIQUE(source_id, file_hash)`, `ON DELETE CASCADE`) et non v16. Sans consequence — la v17 n'y touche pas — mais le docstring « this IS their v16 shape » n'est exact que des deux tables que la v17 faconne ; une demi-phrase leve l'ambiguite. 4. **Libelles de fixtures en francais** (`"Deux colonnes"`, `"Débit seul"`, `"Modèle A"`) dans un module de test integralement anglais, et interpoles dans les messages d'assertion → sortie d'echec bilingue. Cosmetique. 5. **Checklist docs du maillon 10** : le corps ne cite que `CLAUDE.md:122`. Il faut aussi `docs/architecture.md:144-161`, qui porte un **tableau par migration** s'arretant a v16 et qui reclamera sa ligne v17. Au passage, `CLAUDE.md:73` dit encore « 7 migrations inline » (perime bien avant cette PR) — a balayer en meme temps. --- *Review adversariale — verdict base sur rejeu du SQL et mutation independante du garde de parite, pas sur le corps de la PR.*
Author
Owner

Mergée dans main en fast-forward avec le reste de la pile (tip 37b832e).

Forgejo ne détecte pas un merge local comme merged — la PR est donc fermée à la main, et l'issue liée s'est fermée automatiquement via son Resolves #N.

Tip cumulé validé avant push : 1181 vitest, 111 tests Rust, build tsc + vite. La CI ne tourne pas sur push main, cette validation locale était donc le seul filet.

Mergée dans `main` en fast-forward avec le reste de la pile (tip `37b832e`). Forgejo ne détecte pas un merge local comme *merged* — la PR est donc fermée à la main, et l'issue liée s'est fermée automatiquement via son `Resolves #N`. Tip cumulé validé avant push : **1181 vitest**, **111 tests Rust**, build tsc + vite. La CI ne tourne pas sur push `main`, cette validation locale était donc le seul filet.
maximus closed this pull request 2026-08-14 16:13:57 +00:00
All checks were successful
PR Check — Rust / rust (pull_request) Successful in 9m27s

Pull request closed

Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: maximus/Simpl-Resultat#334
No description provided.