# Plan `0.3.16` -> `0.3.18` — résilience RAW, variantes, conflits et récupération ## 1. Statut du document Ce document ferme le gate d'architecture et de sizing de `0.3.16-pre.001`. Base autorisée : ```text v0.3.15 commit 7328be6997399c513306f2f4c8cdf00bd8b22367 ``` `pre.001` reste volontairement documentaire. Il ne crée aucune migration finale, aucune table SQL, aucun contrat Rust définitif et aucune UI de conflit. Les décisions ci-dessous fixent les invariants à respecter par les tranches d'implémentation suivantes. Le fix documentaire `0.3.16-pre.001-fix.001` conserve toutes ces décisions techniques mais corrige le sizing : le programme est réparti sur trois releases stables, `0.3.16`, `0.3.17` et `0.3.18`, afin qu'aucune version ne nécessite plus d'une session de développement normale. Ce fix ne modifie pas `workspace.package.version` et ne change aucun contrat Rust ou SQL. ## 2. Contraintes normatives confirmées Les règles KSP restent cumulatives. `0.3.16` ne crée aucune exception implicite. Contraintes bloquantes pour cette release : - Rust 2024 ; `unsafe`, `unwrap`, `expect` et `panic` restent interdits conformément aux règles KSP ; - tous les éléments partagés `pub`/`pub(crate)` restent réexportés par le `lib.rs` de la crate et consommés via `crate::Item` ; - `ksp-store-lib` reste l'unique façade Store des Worker, Job et Desk ; - `ksp-store-postgres-lib` reste le propriétaire du schéma, du SQL, des transactions et des verrous PostgreSQL ; - `ksp-config-lib` reste le propriétaire de la configuration projet et de sa validation ; - le Worker live et le Job Backfill restent indépendants ; - aucune application Desk ne reçoit de SQL, de backend PostgreSQL direct ni de secret d'infrastructure ; - aucune nouvelle dépendance protocolaire Solana directe n'est introduite pour contourner les couches existantes ; - les migrations V000/V001/V002 restent byte-identiques et leurs checksums ne sont jamais réécrits ; - toute évolution physique se fait par une migration additive nouvelle ; - aucune queue non bornée n'est introduite ; - aucune provenance provider n'est une autorité canonique ; - aucune divergence RAW conservable ne doit être convertie par défaut en panne terminale d'acquisition ; - une validation n'est déclarée PASS que si elle a réellement été exécutée. ## 3. État stable `0.3.15` audité ### 3.1 Fermeture de la release précédente Le delta stable `0.3.15-rel.001` documente un gate opérateur `pre.018` propre, puis les couloirs de fermeture `pre.016` technique/live, `pre.017` documentaire et `pre.018` publication. La fermeture stable a notamment validé : - les cinq familles de routes live ; - plusieurs Worker indépendants partageant le même Store RAW ; - Yellowstone + HTTP Block Polling en parallèle environ dix-neuf minutes ; - absence de `grpc_backpressure_overflow` pendant ce live de référence ; - Stop final `Stopped/Healthy` pour les deux routes ; - acceptation du seul cas de convergence asymétrique prouvé en `0.3.15` : canonique complet + entrant dont `logMessages` est strictement tronqué et compatible. Le sens canonique tronqué -> entrant complet est explicitement réservé à `0.3.16`. ### 3.2 Store API et outcomes actuels Le Store RAW `0.3.15` expose un modèle centré sur une identité logique `(network, signature)` et un canonique unique par signature dans le backend PostgreSQL d'une base déjà liée à un réseau. Les outcomes d'écriture actuels distinguent notamment : ```text Inserted AlreadyPresent Rehydrated SkippedPurged ``` Les observations distinguent notamment : ```text Inserted AlreadyPresent NotRecorded ``` Le contrat ne possède pas encore d'outcome durable représentant : ```text variant inserted compatible less complete canonical promoted conflict quarantined conflict resolved ``` Ces notions doivent donc être ajoutées explicitement au contrat backend-neutral plutôt que détournées vers des erreurs. ### 3.3 Persistance PostgreSQL actuelle Le chemin principal `0.3.15` est transactionnel : ```text BEGIN tentative d'insertion du canonique si collision d'identité : verrouillage de la ligne existante comparaison avec le canonique courant insertion/idempotence de l'observation COMMIT ``` Le verrou de collision repose sur la ligne canonique et permet de sérialiser deux écritures concurrentes de la même signature. Le cas étroit `ActiveIncomingTruncatedLogs` permet déjà de traiter l'entrant tronqué comme observation sans remplacer le canonique complet. Toute divergence non reconnue reste un `content_conflict`. La rétention actuelle porte sur le canonique et distingue : ```text Full Archived Purged ForceRehydrate ``` Les migrations enregistrées sont V000, V001 et V002. Leur registre, leurs ressources et leurs checksums sont stables et intouchables. ### 3.4 Chemin Worker actuel lors d'un conflit Le Worker `0.3.15` possède une convergence run-local avant et autour de la persistance. Une divergence peut être rejetée avant que le Store ne dispose d'un mécanisme durable permettant de conserver la variante. Si le Store remonte encore un `content_conflict`, le Worker le transforme en faute de persistance. Une tâche de persistance qui termine en faute provoque ensuite l'arrêt de la source concernée et un état terminal de route. Cette chaîne est précisément ce que `0.3.16` doit modifier : ```text content divergence conservable -> Store arbitre sous verrou -> variante durable -> conflit durable si nécessaire -> outcome non terminal -> Worker continue ``` Le cache de convergence run-local ne doit plus pouvoir prendre une décision plus forte que le Store durable. Il pourra rester un accélérateur uniquement s'il ne supprime jamais l'arbitrage durable nécessaire. ### 3.5 Erreurs Store temporaires et terminales `0.3.15` ne possède pas encore de politique Worker dédiée de retry Store avec classification backend-neutral complète. Un défaut PostgreSQL remonté comme faute de persistance tend donc à devenir terminal pour la route, même lorsqu'il correspond à une indisponibilité temporaire potentiellement récupérable. Cette responsabilité doit être séparée du `content_conflict` et de la reconnexion Transport. ### 3.6 Retry et reconnexion Transport actuels L'audit confirme des mécanismes déjà présents : - HTTP possède son propre retry de requête borné dans le Transport ; - WebSocket possède `max_retries`, un délai initial et un délai maximum avec backoff exponentiel ; - Yellowstone possède également une boucle de reconnexion/reprise bornée et un backoff ; - Config sait déjà projeter des réglages Transport typés vers ces composants. `0.3.16` ne doit donc pas créer un second moteur concurrent. Le travail attendu est une extension/cohérence des contrats déjà propriétaires de la reconnexion réseau. ### 3.7 Store Desk actuelle `ksp-app-store-desk` reste une application d'inspection via `ksp-store-lib`, sans SQL direct ni dépendance PostgreSQL directe. L'inspection actuelle sait présenter les surfaces RAW existantes mais ne possède pas encore de DTO/query/action pour : ```text variants open conflicts resolution history canonical transitions manual resolution synthetic merge lineage ``` Ces contrats doivent apparaître d'abord dans Store API/lib, puis seulement dans le bridge Tauri et le frontend. ## 4. Réaudit externe du 19 septembre 2026 Sources réauditées : - Solana `getTransaction` ; - Solana `getBlock` ; - Solana RPC JSON Structures ; - `TransactionStatusMeta` / `UiTransactionStatusMeta` actuels ; - collecteur de logs SVM/Agave actuel ; - documentation PostgreSQL actuelle pour `INSERT ... ON CONFLICT`, `SELECT ... FOR UPDATE` et isolation transactionnelle. ### 4.1 `logMessages` : seule relation de complétude automatiquement admise Le collecteur SVM actuel conserve une limite de 10 000 octets. Lorsqu'une nouvelle ligne ferait atteindre ou dépasser la limite, il ajoute une seule ligne exacte : ```text Log truncated ``` puis n'ajoute plus les lignes suivantes. La relation KSP sûre est donc limitée au cas suivant : ```text A = [l0, l1, ..., ln, "Log truncated"] B = [l0, l1, ..., ln, ln+1, ...] ``` avec égalité exacte de toutes les autres composantes canoniques pertinentes. Alors : ```text A < B ``` au sens de la complétude des logs seulement. Cette preuve permet les deux sens : - canonique complet + entrant tronqué -> `CompatibleLessComplete`, sans promotion ; - canonique tronqué + entrant complet -> `CompatibleMoreComplete`, avec promotion atomique. Ne sont pas des preuves de troncature : - une liste simplement plus courte sans marqueur ; - une divergence avant le marqueur ; - plusieurs listes différentes sans relation de préfixe stricte ; - un provider connu pour tronquer ; - la seule longueur en octets ou en nombre de lignes. ### 4.2 `innerInstructions` La réponse RPC officielle distingue selon la forme de réponse des tableaux, `null`, ou une omission dans certains modes. Aucune relation générale `null < [] < valeur` n'est prouvée. Politique `0.3.16` : ```text valeurs identiques -> Exact sur ce champ valeurs différentes -> Conflict/Incomparable absence/null/[] -> aucune dominance automatique ``` ### 4.3 `loadedAddresses` Les structures RPC officielles indiquent que `loadedAddresses` est présent comme objet dans les réponses JSON/full, mais volontairement omis avec certains encodages/modes comme `jsonParsed` ou `accounts`. L'omission peut donc être structurelle et dépendre de la sérialisation, pas de la qualité intrinsèque d'une observation. Politique : aucune promotion automatique fondée sur présence/absence de ce champ. ### 4.4 `returnData`, `computeUnitsConsumed` et `costUnits` Ces champs sont optionnels dans les structures UI et peuvent être omis selon le mode de réponse. La documentation RPC montre également des cas où `returnData` apparaît à `null` dans des exemples alors que son type documentaire principal est optionnel. Politique : ```text même valeur présente -> Exact sur le champ valeurs présentes différentes -> Conflict absent/null/présent -> Incomparable tant qu'un contrat plus précis n'est pas prouvé ``` ### 4.5 Token balances `preTokenBalances` et `postTokenBalances` sont optionnels dans la représentation de statut et la documentation RPC distingue tableaux et `null`. Une liste vide est une valeur métier possible et ne doit jamais être traitée comme une troncature. Politique : aucune dominance automatique hors égalité exacte. ### 4.6 Rewards `rewards` peut être tableau, `null` ou omis selon le mode et `showRewards`/`rewards` demandé. KSP utilise déjà certaines routes avec rewards désactivées. Politique : l'absence de rewards n'est pas une preuve d'une variante moins complète dans le comparateur générique. Toute règle future devra intégrer explicitement le contrat d'acquisition qui a produit la représentation. ### 4.7 `maxSupportedTransactionVersion` Ce paramètre contrôle quelles versions de transaction le client déclare pouvoir traiter. Il ne constitue pas un score de qualité entre deux payloads déjà acquis. Une différence d'acquisition causée par une limite de version doit être diagnostiquée au niveau Transport/acquisition, pas transformée en règle de promotion canonique. ### 4.8 Conclusion champ par champ Pour `0.3.16`, la seule dominance automatique de contenu autorisée au départ est : ```text relation de troncature logMessages strictement prouvée ``` Tous les autres champs restent fail-closed : égalité exacte ou `Conflict/Incomparable`. Cette politique pourra être étendue dans une future tranche uniquement après preuve normative et canari KSP dédié. ## 5. Modèles physiques étudiés ### 5.1 Modèle A — modifier fortement la ligne canonique V001 Principe : transformer `ksp_raw_transactions` en identité portant directement un pointeur de variante, ajouter les variantes puis déplacer progressivement le payload hors de la ligne historique. Avantages : - modèle relationnel final compact ; - un seul point d'identité logique. Inconvénients : - forte modification du schéma stable V001 ; - risque élevé sur les lecteurs/rétention/tests existants ; - migration volumique plus intrusive ; - rollback de release plus difficile ; - davantage de surfaces changées dans une seule étape. Décision : non retenu pour `0.3.16`. ### 5.2 Modèle B — ledger de variantes + sélecteur sidecar + projection V001 Principe : ```text ksp_raw_transactions = projection canonique compatible V001 variant ledger V003 = toutes les représentations conservées à partir de 0.3.16 canonical selector V003 = variant_id actuellement sélectionné conflict/resolution journal V003 = historique immuable des décisions ``` Une promotion effectue dans une même transaction : ```text lock identité V001 verify expected canonical/revision persist incoming variant if new update canonical selector update V001 canonical projection append canonical transition / resolution event attach observation to exact variant commit ``` Avantages : - V000/V001/V002 inchangées ; - anciens lecteurs restent compatibles ; - rollback conceptuel vers une ancienne variante sans Internet ; - migration additive et progressive ; - verrou existant de l'identité réutilisable ; - séparation nette identité / variante / historique. Inconvénients : - duplication contrôlée entre la projection V001 et la variante canonique ; - invariants de cohérence à tester entre projection et sélecteur ; - rétention plus complexe ; - nouvelles écritures doivent maintenir plusieurs structures atomiquement. Décision : modèle retenu. ### 5.3 Modèle C — variante adressée uniquement par `content_hash` Principe : utiliser le hash comme clé physique principale et considérer deux hashes identiques comme même variante. Avantages : - déduplication simple ; - index de recherche efficace. Inconvénients : - confond hash cryptographique et preuve d'égalité ; - rend la sémantique de collision implicite ; - complique le cas tombstone/payload absent ; - impose une hypothèse plus forte que nécessaire au contrat de domaine. Décision : rejeté. ## 6. Identité d'une variante La variante utilise un identifiant surrogate stable : ```text variant_id ``` `content_hash` reste stocké et indexable, mais sert à : - accélérer la recherche de candidats égaux ; - vérifier l'intégrité ; - fournir un diagnostic redacted ; - aider les tombstones de rétention. Il ne suffit jamais seul à conclure `Exact` lorsque les deux payloads sont disponibles. Pour une identité verrouillée : ```text same hash -> candidate equality -> exact compare of canonical representation -> Exact only after exact comparison ``` Aucune contrainte d'unicité ne doit transformer automatiquement `(signature, content_hash)` en preuve métier. La sérialisation par verrou de l'identité permet de prévenir les doubles insertions concurrentes après comparaison exacte. ## 7. Structures V003 conceptuelles retenues Les noms SQL exacts seront figés dans la tranche de migration, mais les rôles sont désormais décidés. ### 7.1 Variant ledger Chaque variante conserve au minimum : ```text variant_id transaction identity/signature origin kind = native | synthetic canonical raw format id/version slot block_time content_hash payload ou état de rétention created_at ``` Une variante native représente exactement un payload effectivement reçu. Une variante synthétique doit être explicitement marquée et posséder une lignée de parents. ### 7.2 Canonical selector Une structure sidecar contient au minimum : ```text transaction identity canonical_variant_id canonical_revision updated_at ``` `canonical_revision` sert de garde optimiste aux actions Desk afin d'éviter une résolution sur une vue périmée. ### 7.3 Variant observations À partir de V003, chaque observation nouvelle est durablement liée à la variante réellement reçue. L'écriture reste atomique avec la variante et l'outcome de persistance. Point historique important : les observations créées avant `0.3.16` ne contiennent pas toujours le payload reçu. Le cas `logMessages` tronqué accepté en `0.3.15` conserve l'observation mais pas une variante complète de l'entrant. Il est donc interdit de prétendre reconstruire après coup l'association exacte de toutes les observations historiques. Le bootstrap V003 doit les exposer comme observations héritées dont la variante exacte est `unknown/legacy`, ou comme association explicitement qualifiée et non comme preuve native. ### 7.4 Conflict case Une identité peut posséder un dossier de conflit durable unique et réouvrable. Le dossier porte : ```text conflict identity status = Open | Resolved revision current canonical variant latest classification/reason created_at updated_at ``` Les variantes participantes sont conservées séparément. ### 7.5 Journal de transitions et résolutions Le journal est append-only. Types conceptuels : ```text AutoPromoteMoreComplete ManualPromoteVariant KeepCurrentAndResolve RestorePreviousCanonical ReopenConflict CreateSyntheticMerge ``` Chaque événement garde : ```text action id conflict id optionnel from variant id optionnel to variant id expected revision origin = system | operator result created_at ``` Aucun payload arbitraire ni secret ne doit être requis dans les logs sûrs. ### 7.6 Parents de variante synthétique Une fusion assistée crée une nouvelle variante : ```text origin = synthetic parents = [variant A, variant B, ...] ``` Elle ne réécrit jamais A ou B et ne se présente jamais comme une observation provider native. ## 8. Migration strategy ### 8.1 Invariant principal V000/V001/V002 restent byte-identiques. V003 ajoute des ressources nouvelles et, si nécessaire, des index/contraintes nouvelles via une migration indépendante enregistrée avec son propre checksum. ### 8.2 Bootstrap des identités existantes La migration ne doit pas inventer des variantes historiques perdues. Pour chaque identité déjà présente, le système peut créer une variante bootstrap représentant l'état canonique encore réellement disponible : - `Full` : payload canonique actuel copié comme variante bootstrap ; - `Archived` : payload exact récupéré depuis l'archive locale existante et porté comme variante archivée ; - `Purged` : variante tombstone seulement, sans prétendre disposer encore des bytes perdus. Les observations historiques restent qualifiées `legacy` lorsqu'aucune preuve ne permet de les rattacher à la variante exacte reçue. ### 8.3 Migration volumique La création de schéma et le bootstrap de données doivent être séparables si le volume réel rend un backfill monolithique trop coûteux. La stratégie préférée est : 1. créer V003 de façon additive ; 2. permettre un bootstrap paresseux d'une identité sous son verrou lorsque sa projection V003 manque ; 3. fournir un backfill borné/idempotent de préparation si nécessaire pour réduire le coût au premier accès ; 4. ne jamais bloquer la migration de schéma sur une reconstruction externe. Cette stratégie sera testée sur une copie PostgreSQL réaliste avant gate final. ## 9. Atomicité et concurrence ### 9.1 Même identité, deux entrants concurrents L'identité V001 reste le mutex transactionnel naturel. Séquence cible : ```text BEGIN SELECT canonical identity FOR UPDATE ensure V003 bootstrap reload canonical selected variant compare incoming against locked canonical persist or reuse exact incoming variant attach incoming observation apply promotion/conflict transition if required update compatibility projection if canonical changes append event COMMIT ``` Deux transactions ne peuvent donc pas promouvoir simultanément à partir du même `canonical_revision` sans que la seconde réévalue l'état après attente/verrou. ### 9.2 Action Desk concurrente avec ingestion Toute action manuelle contient : ```text expected canonical_variant_id expected conflict revision ``` Si l'état a changé, l'action échoue comme stale action et l'UI recharge. Elle ne force jamais silencieusement un ancien choix. ### 9.3 Hash collision Le hash réduit l'espace de recherche mais l'égalité exacte des représentations disponibles tranche l'idempotence. Si deux payloads différents ont le même hash, ils restent deux variantes différentes et le diagnostic doit le signaler comme anomalie de hash, pas les fusionner. ## 10. Relation de qualité Le comparateur interne utilise cinq relations : ```text Exact CompatibleLessComplete CompatibleMoreComplete Conflict Incomparable ``` `Incomparable` est nécessaire pour représenter une relation partielle avant projection vers le comportement durable. Dans `0.3.16`, un `Incomparable` est conservé comme conflit durable fail-closed. ### 10.1 Exact Tous les éléments du contrat canonique sont égaux. Conséquence : observation supplémentaire/idempotente sur la même variante. ### 10.2 CompatibleLessComplete Seul un manque explicitement prouvé existe et le canonique domine l'entrant. En première version, seul le cas de troncature `logMessages` prouvée est admis. Conséquence : conserver le canonique ; conserver la variante reçue si elle n'existe pas ; rattacher son observation ; ne pas ouvrir de conflit nécessitant action humaine. ### 10.3 CompatibleMoreComplete L'entrant domine le canonique selon la même preuve stricte. Conséquence : persister l'entrant puis promotion atomique ; conserver l'ancien canonique ; ajouter un événement automatique réversible. ### 10.4 Conflict Une contradiction prouvée existe. Conséquence : conserver les deux variantes, conserver le canonique courant, ouvrir/compléter le conflict case, Worker non terminal. ### 10.5 Incomparable Aucune contradiction simple n'est nécessairement démontrée, mais aucune dominance sûre n'est prouvée. Conséquence `0.3.16` : même traitement durable prudent qu'un conflit, avec reason code distinct. ## 11. Ownership du comparateur La décision de convergence doit être backend-neutral et réutilisable sous verrou par le backend. Le contrat de relation appartient à la surface Store RAW commune ; il ne doit pas être une heuristique privée du Worker. Décision d'ownership : - `ksp-store-api` porte les types de relation, reason codes, données backend-neutral et le comparateur pur de convergence RAW ; - le comparateur appartient au contrat de domaine Store RAW parce qu'il doit être identique pour tous les backends et directement appelable par `ksp-store-postgres-lib` sous verrou sans dépendance inverse vers `ksp-store-lib` ; - `ksp-store-postgres-lib` applique cette relation dans la transaction durable et reste propriétaire de la sérialisation PostgreSQL, des verrous et des mutations atomiques ; - `ksp-store-lib` expose uniquement la façade commune et les opérations d'inspection/résolution ; il ne devient pas une dépendance du backend ; - `ksp-raw-transaction-lib` reste l'autorité sur la canonicalisation RAW v1 et fournit les golden/canaris utiles, sans devenir propriétaire de la politique Store de convergence ; - le Worker peut utiliser la relation à titre de métrique/optimisation, mais le Store verrouillé reste l'autorité de la décision durable. La tranche API devra encore vérifier le graphe Cargo avant de fixer le fichier/module Rust exact, mais la frontière de crate est fermée : le comparateur backend-neutral appartient à `ksp-store-api`. Aucune nouvelle dépendance circulaire n'est admise. ## 12. Rétention, archive et rehydration ### 12.1 Règle générale Une promotion réversible exige de conserver les bytes nécessaires au retour arrière. Une variante : - actuellement canonique ; - participante à un conflit ouvert ; - ancien canonique référencé par un événement de restauration ; - parent requis d'une variante synthétique ; doit pouvoir être archivée mais ne doit pas être purgée de manière irréversible tant que le contrat de restauration dépend d'elle. ### 12.2 Full -> Archived Autorisé si les bytes exacts sont conservés dans la surface d'archive V003 et que la restauration locale est possible. ### 12.3 Purged `Purged` reste possible pour des variantes non épinglées par les invariants ci-dessus, selon politique de rétention explicite. Un ancien canonique nécessaire à `RestorePreviousCanonical` est épinglé contre la purge. ### 12.4 ForceRehydrate `ForceRehydrate` ne réécrit jamais arbitrairement une variante existante : - si les bytes exacts sont encore disponibles, égalité exacte normale ; - si seul un tombstone legacy subsiste, le hash peut vérifier une attente d'intégrité mais n'est pas présenté comme preuve d'égalité historique ; - un payload réellement différent devient une nouvelle variante ; - une représentation plus complète peut ensuite être promue selon le comparateur. ## 13. Machine d'état du conflict case États : ```text Open Resolved ``` Transitions : ```text new Conflict/Incomparable -> Open Open + KeepCurrent -> Resolved Open + PromoteVariant -> Resolved Open + SyntheticMerge -> Resolved Resolved + new unresolved variant -> Open Resolved + explicit Reopen -> Open Resolved + RestorePreviousCanonical -> Resolved with new journal event ``` Le statut est volontairement petit. Le détail de la décision vit dans le journal append-only et non dans une multiplication d'états terminaux. ## 14. Outcomes Store cibles Les noms Rust exacts seront figés dans `pre.002`, mais les catégories suivantes sont requises : ```text InsertedCanonical ObservedExact ObservedCompatibleLessComplete PromotedCompatibleMoreComplete QuarantinedConflict Rehydrated SkippedPurged ``` `QuarantinedConflict` n'est pas une erreur de transport ni une erreur terminale Store. C'est un succès de durabilité avec réconciliation canonique encore ouverte. ## 15. Store retry — ownership et sémantique ### 15.1 Classification `ksp-store-api` expose une classification backend-neutral : ```text Transient Terminal ``` `ksp-store-postgres-lib` mappe les erreurs PostgreSQL réelles vers cette classification. Exemples transients à couvrir par tests : - connexion interrompue ; - timeout/pool temporairement indisponible ; - serialization failure lorsque le niveau choisi peut la produire ; - deadlock détecté et transaction entièrement rejouable. Exemples terminaux : - checksum/migration/schema incompatible ; - contrat modèle invalide ; - payload invalide ; - violation d'invariant non récupérable ; - politique de retry épuisée. Un conflit de contenu conservé durablement n'est plus dans cette classification d'erreur. ### 15.2 Orchestration Le Worker possède l'orchestration de retry de persistance, car il possède : - l'admission ; - la concurrence de persistance ; - le lifecycle ; - le backpressure ; - le health ; - le Stop/drain. Le backend Store reste responsable de l'atomicité d'une tentative et de la classification de sa faute ; il ne masque pas une boucle de retry indéfinie au Worker. ### 15.3 Paramètres de politique Le type Worker doit accepter : ```text initial_delay max_delay backoff_multiplier max_attempts = bounded | explicitly unbounded jitter bounded reset after successful durable persistence ``` Les valeurs numériques par défaut seront calibrées par tests ciblés avant la tranche Worker retry ; leur ownership et leur sémantique sont cependant fermés ici. ### 15.4 Backpressure Pendant une indisponibilité Store : ```text pending durable write retained retry according to policy persistence concurrency remains bounded admission queue remains bounded pressure propagates upstream when capacity fills no success counter before durable commit no silent drop ``` Si `max_attempts` est épuisé, la route peut devenir terminale. Si un mode explicitement illimité est configuré, la route reste `Blocked`/non terminale jusqu'à recovery ou Stop, tout en conservant des files bornées. ## 16. Transport reconnect — ownership et extension La reconnexion réseau reste propriété de `ksp-onchain-transport-lib`. Le moteur existant WebSocket/Yellowstone doit être étendu plutôt que dupliqué avec une politique typée comprenant : ```text initial_delay max_delay backoff_multiplier max_attempts reset_after_stable_duration bounded_jitter ``` Les defaults de compatibilité doivent conserver le comportement `0.3.15` lorsqu'un nouveau réglage n'est pas fourni. HTTP conserve sa sémantique de retry de requête ; il ne doit pas être forcé dans une abstraction de « reconnect » si cette abstraction dégrade son contrat. `ksp-config-lib` reste le propriétaire de la représentation de configuration et projette les valeurs validées vers Transport. Une reconnexion réussie ne prouve jamais que la coverage est continue. Gaps/replay/repair restent des mécanismes séparés. ## 17. Health et métriques source-neutral Les compteurs cibles sont source-neutral et redacted : ```text raw_variant_created_count raw_exact_observation_count raw_less_complete_observation_count raw_canonical_promotion_count raw_unresolved_conflict_count raw_auto_resolved_count store_retry_attempt_count store_retry_exhausted_count store_blocked_duration_ms ``` Les noms pourront être harmonisés avec les conventions exactes du Worker API, mais aucune dimension provider ne doit décider de la santé canonique. Le lifecycle peut rester `Running` pendant : ```text unresolved conflict -> health Degraded transient Store retry -> health Degraded Store pressure saturating bounded capacity -> health Blocked ``` La route devient terminale seulement lorsqu'une erreur structurelle l'exige ou lorsque la politique de récupération est épuisée. Deux notions de complétude doivent rester distinctes : ```text acquisition_complete canonical_complete ``` ## 18. Store Desk — scope retenu ### 18.1 Lecture Ajouter via `ksp-store-lib` seulement : ```text RAW Conflicts RAW Conflict History / Resolution History variant detail canonical transition history observation/provenance per variant ``` Les listes doivent être paginées/bornées ; DataTables `serverSide` est privilégié lorsque le volume le justifie. ### 18.2 Actions Actions structurées : ```text Promote variant Keep current canonical + Resolve Restore previous canonical Reopen conflict Assisted merge, seulement si les règles prouvées l'autorisent ``` Chaque action envoie des identifiants typés et la revision attendue ; pas d'éditeur JSON libre comme mécanisme principal. ### 18.3 Architecture Desk Conserver : ```text lib.rs = exports only tauri.rs = bridge IPC modules Rust spécialisés Vite + TypeScript tracing frontend des actions sans valeurs sensibles ksp-store-lib seule façade Store cargo tauri dev/build pour les gates Desk ``` ## 19. Risques identifiés ### 19.1 Divergence projection V001 / sélecteur V003 Mitigation : mise à jour atomique sous le même verrou, tests de canari et vérification d'invariant lors des lectures sensibles. ### 19.2 Double variante concurrente Mitigation : verrou identité, recherche hash comme préfiltre puis comparaison exacte avant insertion. ### 19.3 Stale action Store Desk Mitigation : `canonical_revision` + `conflict revision` attendues. ### 19.4 Rétention détruisant le rollback Mitigation : pin explicite des variantes nécessaires à l'historique/restauration ; archive locale admise, purge interdite tant que l'invariant l'exige. ### 19.5 Migration volumique Mitigation : V003 additive, bootstrap idempotent/paresseux et éventuel backfill borné plutôt qu'une reconstruction externe ou une migration monolithique obligatoire. ### 19.6 Retry Store créant une file infinie Mitigation : réutiliser les capacités bornées existantes, backpressure upstream et aucun buffer latéral non borné. ### 19.7 Reconnect masquant des gaps Mitigation : reconnect et coverage restent deux compteurs/contrats séparés. ### 19.8 Faux ordre de qualité hors logs Mitigation : `Incomparable` fail-closed pour tous les champs sans preuve normative. ### 19.9 Historique legacy incomplet Mitigation : ne jamais prétendre reconstruire une variante pré-V003 non stockée ; qualifier explicitement l'observation comme legacy/unknown. ## 20. Découpage recalibré du programme `0.3.16` -> `0.3.18` Le découpage initial de `pre.001` jusqu'à `pre.022` est abandonné. Même si chaque tranche restait petite isolément, plus de vingt prereleases auxquelles s'ajouteraient les `fix.*` ne constituent pas une cible réaliste pour la règle KSP « une version = au maximum une session ». Le programme conserve les mêmes invariants techniques, mais il est réparti en trois vertical slices stables. La cible est de rester autour de sept à huit prereleases par version, avec une marge réelle pour les fixes. Une release ne doit pas être artificiellement maintenue ouverte pour absorber le scope de la suivante. ### 20.1 `0.3.16` — fondations RAW multi-variantes et ingestion non terminale Objectif de fermeture : disposer d'une persistance durable des variantes et conflits, d'une promotion automatique strictement prouvée, et permettre au Worker live de continuer lorsqu'une divergence conservable est enregistrée. #### `0.3.16-pre.001` Gate d'audit, architecture, sizing, plan et validation. Cette tranche est déjà appliquée. #### `0.3.16-pre.002` Contrats Store API backend-neutral : variant identity, relation de qualité, outcomes de persistance, reason codes et contrats minimaux nécessaires au conflit durable. #### `0.3.16-pre.003` Migration V003 : registre, resources et schéma multi-variantes/selector/conflict minimal ; canaris garantissant l'immuabilité byte/checksum de V000/V001/V002. #### `0.3.16-pre.004` Backend PostgreSQL : bootstrap V003, variant ledger, rattachement exact des nouvelles observations à leur variante, idempotence et concurrence d'insertion. #### `0.3.16-pre.005` Comparateur partagé : `Exact`, `CompatibleLessComplete`, `CompatibleMoreComplete`, `Conflict`, `Incomparable` ; canaris `logMessages` bidirectionnels et politique fail-closed sur les autres champs. #### `0.3.16-pre.006` Sélecteur canonique et promotion atomique : revision, projection V001 cohérente, conservation de l'ancien canonique et journal minimal des transitions. #### `0.3.16-pre.007` Conflit durable minimal + intégration Worker : variante conservée, case ouverte, outcome non terminal, suppression/restriction de l'arbitrage run-local plus fort que le Store et health `Degraded` lorsque requis. #### `0.3.16-pre.008` Hardening ciblé de la vertical slice : concurrence, rollback transactionnel, régression Job Backfill/Store, gates workspace, validation documentaire et préparation de `0.3.16-rel.001`. #### `0.3.16-rel.001` Publication stable mécanique après gates propres. Aucun Store Desk ni retry Store complet n'est requis pour fermer cette release. ### 20.2 `0.3.17` — résilience opérationnelle et cycle de vie des variantes Objectif de fermeture : rendre l'acquisition résiliente aux indisponibilités Store/Transport et fournir les contrats backend-neutral d'inspection/résolution nécessaires aux opérateurs et à la future UI. #### `0.3.17-pre.001` Contrats Store API d'inspection/résolution, revisions attendues et classification backend-neutral des erreurs Store `Transient/Terminal`. #### `0.3.17-pre.002` Cycle de vie complet des conflict cases : participants, résolution, reopen, historique append-only et races ingestion/résolution. #### `0.3.17-pre.003` Rétention des variantes : archive, pins, purge guards, rollback local et `ForceRehydrate` exact/différent. #### `0.3.17-pre.004` `ksp-store-lib` : inspection, historique et actions backend-neutral ; maintien explicite de la compatibilité du Job Backfill existant. #### `0.3.17-pre.005` Worker : Store retry/backpressure/`Blocked`, exhaustion explicite, cancellation et drain pendant backoff. #### `0.3.17-pre.006` Transport/Config : reconnexion WebSocket/Yellowstone configurable en étendant les mécanismes propriétaires existants, sans confondre reconnexion et coverage et sans réimplémenter le retry HTTP. #### `0.3.17-pre.007` Hardening cross-layer et preuves live ciblées : conflits concurrents, outage/recovery Store, retry, reconnect et invariants de queue bornée. #### `0.3.17-pre.008` Gate technique final, réconciliation documentaire et préparation de `0.3.17-rel.001` ainsi que du prompt `0.3.18`. #### `0.3.17-rel.001` Publication stable mécanique après gates propres. ### 20.3 `0.3.18` — inspection et résolution opérateur Store Desk Objectif de fermeture : exposer les variantes/conflits/résolutions déjà stabilisés par Store API/lib dans une UI Desk sûre, paginée, traçable et sans dépendance backend directe. #### `0.3.18-pre.001` Store Desk backend/Tauri : queries typées, pagination server-side et actions via `ksp-store-lib` uniquement. #### `0.3.18-pre.002` Frontend Vite/TypeScript : listes de conflits, historique, détail de variantes et transitions canoniques. #### `0.3.18-pre.003` Actions opérateur `promote/keep/restore/reopen` avec revision attendue, stale-action explicite et rafraîchissement cohérent. #### `0.3.18-pre.004` Fusion assistée/synthétique strictement bornée uniquement si les règles de fusion sont réellement prouvées ; sinon fermeture documentaire explicite du non-support, sans heuristique libre. #### `0.3.18-pre.005` Hardening UI/backend : redaction, pagination bornée, concurrence, cancellation, dependency boundaries et régressions Store Desk. #### `0.3.18-pre.006` Preuves end-to-end et gate technique/live final : PostgreSQL/Store/Worker/Desk, Tauri build/smokes pertinents et graphes Cargo lorsque requis par les règles. #### `0.3.18-pre.007` Réconciliation documentaire, `CHANGELOG.md`, `ROADMAP.md`, prompt de reprise suivant et préparation de `0.3.18-rel.001`. #### `0.3.18-rel.001` Publication stable mécanique après gates propres. ### 20.4 Règles de sizing pour les trois releases Le nombre exact de prereleases reste souple, mais les règles suivantes sont désormais normatives pour ce programme : - une version ne doit pas dépasser une session normale ; - une tranche peut être subdivisée si sa validation devient trop lourde ; - un `fix.*` corrige la tranche concernée sans obliger à compresser la suite ; - un scope non indispensable à la vertical slice de la release est reporté à la release suivante ; - aucune fusion artificielle de migration, concurrence, Worker, Transport et UI n'est autorisée pour « économiser » un numéro de prerelease ; - la fermeture stable de chaque release doit laisser le workspace dans un état cohérent et exploitable indépendamment du développement de la suivante. ## 21. Hors périmètre confirmé et déplacement des scopes réservés Le programme `0.3.16` -> `0.3.18` n'implémente pas : - multi-route/multi-stratégie du Job Backfill, désormais reporté après `0.3.18` et ciblé par défaut sur `0.3.19` ; - adaptation Backfill Desk correspondante, ciblée par défaut sur `0.3.20` ; - RAW -> STRUCTURAL ; - persistence STRUCTURAL ; - DECODED/DOMAIN ; - majority voting provider ; - provider priority ; - fusion JSON libre ; - reconstruction Internet comme rollback normal ; - second pipeline d'acquisition ; - dépendance Worker <-> Job Backfill. Les anciennes réservations `0.3.17 = Backfill multi-route` et `0.3.18 = Backfill Desk` sont donc explicitement annulées par `0.3.16-pre.001-fix.001`. Ce déplacement évite deux scopes concurrents sur les mêmes numéros de version. Le Job Backfill actuel doit seulement rester compatible avec les contrats Store communs pendant `0.3.16` -> `0.3.18`. Les numéros `0.3.19`/`0.3.20` restent des cibles de planning et pourront être réévalués par le gate d'ouverture correspondant sans réouvrir le programme RAW résilience déjà fermé. ## 22. Questions non bloquantes reportées aux tranches d'implémentation Les décisions suivantes ne changent pas l'architecture du gate et peuvent être finalisées au moment où des mesures/tests réels existent : - noms SQL définitifs des ressources V003 ; - valeurs numériques par défaut de Store retry ; - valeur par défaut du jitter et de `reset_after_stable_duration` Transport ; - colonnes exactes des DataTables Store Desk ; - stratégie de backfill eager ou lazy optimale après mesure du volume PostgreSQL réel. Toute réponse à ces questions doit respecter les invariants fixés ci-dessus ; elle ne peut pas réouvrir un provider ranking ou une perte silencieuse. ## 23. Sources externes réauditées - - - - - - - - - - ## 24. Gate `pre.001` Le gate est architecturalement fermé lorsque : - le modèle sidecar/variant ledger est accepté comme base de développement ; - aucune migration V000/V001/V002 n'est modifiée ; - le surrogate `variant_id` est l'identité physique et le hash n'est pas une preuve d'égalité ; - les observations futures sont rattachées à leur variante réelle ; - l'historique legacy non reconstructible est explicitement qualifié ; - `logMessages` est la seule dominance automatique admise ; - tous les autres champs restent fail-closed ; - Store retry appartient à l'orchestration Worker avec classification backend-neutral fournie par Store ; - Transport reconnect reste propriété Transport ; - Store Desk passe exclusivement par Store-lib ; - les risques de concurrence, rétention, stale action et migration volumique ont un mécanisme de contrôle explicite ; - le découpage multi-version `0.3.16` -> `0.3.18` remplace la prévision initiale `0.3.16-pre.001` -> `pre.022` trop longue pour une seule session.