diff --git a/v1-plan.md b/v1-plan.md index 0e236a7..877b800 100644 --- a/v1-plan.md +++ b/v1-plan.md @@ -188,8 +188,8 @@ Les 8 écarts a→h du §1quinquies. Ne démarre qu'après les lots B et C1 (**L | Écart | Quoi | Attention | |---|---|---| -| c | `Instance` : 4 champs migrés sur ~35 | **Le plus dangereux : aucune erreur levée.** `PublicApiKey` null = les apps visiteur ne s'authentifient plus ; `WebSlug` null = visitapp-web injoignable ; `SubscriptionPlanId` null = quotas à 0, donc `AiTokensPerMonth = 0`, donc **rien ne s'indexe** et l'IA tourne sans compteur. Réutiliser `ApplyPlanQuotas` | -| a | `BuildSection` ne couvre que 11 types : `SectionEvent` et `SectionParcours` tombent dans le `default` | Section non migrée, silencieusement | +| c | `Instance` : 4 champs migrés sur ~35 — **requalifié le 2026-08-11 : ce n'est pas un oubli de mapping.** `OldInstance` et l'export réel ne contiennent que `_id`, `Name`, `DateCreation`, `PinCode`. Les 31 autres colonnes **n'existent pas dans la source** : elles sont nées avec Postgres v3. Il n'y a donc rien à mapper, il faut **générer, dériver ou décider**. `PublicApiKey` → générer · `WebSlug` → dériver du `Name` (`SlugHelper` existe) · `IsMobile`/`IsTablet`/`IsWeb` → dériver des `ApplicationInstance` réellement présentes · `SubscriptionPlanId` → décidé, voir ci-dessous | **Le plus dangereux : aucune erreur levée.** `PublicApiKey` null = les apps visiteur ne s'authentifient plus ; `WebSlug` null = visitapp-web injoignable ; `SubscriptionPlanId` null = quotas à 0, donc `AiTokensPerMonth = 0`, donc **rien ne s'indexe** et l'IA tourne sans compteur. Réutiliser `ApplyPlanQuotas` | +| ~~a~~ | ⛔ **Fausse alerte — vérifiée dans l'export le 2026-08-11.** `BuildSection` couvre **exactement les types que Mongo contient**. L'enum va de `Map`=0 à `Weather`=10, puis `Event`=11 et `Parcours`=12 ; or `TabletDb.Sections.json` (327 sections) ne contient **que les valeurs 0 à 10**. `SectionEvent` et `SectionParcours` **n'existaient pas dans Mongo** — ce sont des types nés avec Postgres v3. Le `default` est un filet, pas un trou | Le kanban parlait de « perte de données » et du « scénario carnaval » : ce scénario est du **contenu à créer** dans Postgres, pas du contenu à migrer. Vérifié aussi : le renommage `SectionPuzzle` → `SectionGame` est **déjà traité** — la branche `Game` parse `OldPuzzleDTO` et pose `GameType = GameTypes.Puzzle`, et la valeur 8 n'a pas bougé | | b | Collections filles jamais remplies (`QuizQuestions = new()`, `EventAgendas = new()`, aucun `GuidedPath`/`GuidedStep`) | Un quiz arrive sans ses questions | | d | `Role = UserRole.ContentEditor` en dur | Plus aucun administrateur après la bascule | | e | Colonnes `Resource` non renseignées, `SizeBytes` déduit d'un `HEAD` avalé | **L5** — appeler le calculateur C1 | @@ -240,6 +240,41 @@ Les CGU §8 ont été réécrites le 2026-08-11 (`cgu-myinfomate.md`) et le text | I4 | `dotnet ef database update` sur la base vide, puis vérifier `postgis` **et** `vector` présents | Une base en retard d'une migration fait planter l'API dès le login — c'est arrivé en local le 09/08 | | I5 | **Plan de retour arrière écrit**, avant le jour J | « Un rollback qu'on improvise à 23 h n'est pas un rollback » | | I6 | `pg_dump` avant, puis **rejouer instance par instance** (`instanceId=…`), en commençant par la plus petite | | + +**Plans — décisions du 2026-08-11, à ne pas re-trancher.** + +**1. Les lignes en base sont alignées sur la grille de tarifs** (migration `AlignSubscriptionPlansWithPricing`, appliquée). La base semait `Starter` / `Standard` / `Premium` / `Essentiel` : noms **et quotas** désalignés. Corrigé : + +| Id | Nom | Stockage | Jetons IA | Stats | Rétention | Avancées | +|---|---|---|---|---|---|---| +| `plan-essentiel` | Essentiel | 1 GB | **0** (non inclus) | ✓ | 30 j | ✗ | +| `plan-pro` | Pro | 15 GB | **0** (non inclus) | ✓ | 30 j | ✗ | +| `plan-premium` | Premium | 50 GB | 20 M (~2 000 req) | ✓ | 395 j | ✓ | +| `plan-enterprise` | Enterprise | 0 = illimité | `long.MaxValue` | ✓ | 395 j | ✓ | + +`plan-starter` **supprimé** (aucun équivalent commercial, et piège à 0 jeton). `plan-standard` → `plan-pro`, avec repointage SQL des `Instances` **avant** la suppression : EF avait scaffoldé les `DeleteData` en tête, ce qui aurait buté sur la clé étrangère ou laissé des instances sans plan, donc à quotas nuls. + +⚠️ **Sémantique du `0`, asymétrique et volontaire** : pour le stockage `0` = illimité, pour les jetons `0` = pas d'IA. D'où la sentinelle `long.MaxValue` sur Enterprise — un `0` l'aurait privé d'assistant. Ne pas « harmoniser » sans repasser dans `CheckQuota`, `AiController` et `SectionIndexingInterceptor`. + +⚠️ **Le plan ne porte que 5 champs.** « App native », « offline + beacons », « push », « traduction automatique » sont dans la grille commerciale mais **pas dans la table** : ils se règlent par instance. La base ne les applique pas, l'affectation le fait. + +**2. Add-on IA sur Essentiel.** L'IA n'est pas incluse dans Essentiel ni Pro, mais se vend en add-on activé à la main après paiement. **Aucun code à écrire** : `ApplyPlanQuotas` ne recopie les quotas du plan que si le plan change, donc surcharger `Instance.AiTokensPerMonth` survit — le commentaire de la méthode le prévoit (« un client peut recevoir un geste commercial sans changer de plan »). + +> ⚠️ **Piège à connaître** : le rattrapage d'indexation (`BackfillInstanceAsync`) est déclenché par `UpdateInstance`, en C#. **Activer l'add-on directement en SQL ne le déclenche pas** — le client paierait un guide qui ne connaît rien de son contenu déjà saisi. Après l'UPDATE, cliquer le bouton de relance SuperAdmin de l'écran Guide IA. + +**3. Affectation des 4 clients existants** — pour la bascule, aucun client Essentiel : + +| Instance Mongo | Plan | Conséquence | +|---|---|---| +| MyInfoMate instance | `plan-premium` | IA active | +| VisitNamur instance | `plan-premium` | IA active | +| MDLF instance | `plan-pro` | ⚠️ **pas d'IA** | +| Fort Saint-Héribert instance | `plan-pro` | ⚠️ **pas d'IA** | + +> ⚠️ **À arbitrer avant le lot H** : Pro n'inclut pas l'IA, donc rien ne s'indexera pour MDLF et le Fort et leur guide ne répondra jamais. Sur 4 instances, la chaîne RAG ne serait éprouvée que sur 2 — or le lien **L14** demande justement de tester le post-filtrage HNSW **à deux instances au moins**. Deux sorties : les passer en Premium le temps des tests, ou leur poser un quota IA à la main (l'add-on ci-dessus), en pensant au rattrapage d'indexation. + +**4. Écart restant, hors bascule** : la FAQ de `myinfomate-landing` (`src/data/segments.ts`) appelle encore le plan à 179€ « **Bundle** » — **41 occurrences en 4 langues** — alors que les cartes de tarifs disent « Premium ». Un prospect qui lit la grille puis la FAQ voit deux noms pour la même offre. Chantier de texte commercial, sans effet sur la migration. + | I7 | Vérifications post-bascule : login manager-app, une app visiteur **avec sa clé API**, un parcours, une carte, un PDF, un quiz avec ses questions | Les écarts b et c ne se voient qu'ici | | I8 | Bascule DNS / API, Mongo gardé en lecture seule quelques jours, `pg_dump` après | |