diff --git a/Cargo.toml b/Cargo.toml index fb1127a..107e6d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ # file: Cargo.toml -# version: 651 +# version: 652 [workspace] resolver = "3" members = ["crates/ksp-app-backfill-desk", "crates/ksp-app-config-desk", "crates/ksp-app-raw-transaction-ingest-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-store-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-interface-lib", "crates/ksp-job-api", "crates/ksp-job-backfill-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-program-api", "crates/ksp-raw-transaction-lib", "crates/ksp-store-api", "crates/ksp-store-lib", "crates/ksp-store-postgres-lib", "crates/ksp-wallet-lib", "crates/ksp-worker-api", "crates/ksp-worker-raw-transaction-ingest-lib"] [workspace.package] -version = "0.3.16" +version = "0.3.17-pre.1" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" diff --git a/deltas/0.3.17/pre.001.md b/deltas/0.3.17/pre.001.md new file mode 100644 index 0000000..d30c4e1 --- /dev/null +++ b/deltas/0.3.17/pre.001.md @@ -0,0 +1,287 @@ + + + +# Delta `0.3.17-pre.001` — gate d'audit, architecture et sizing + +## Base requise + +```text +v0.3.16 +workspace.package.version = 0.3.16 +``` + +Ne pas appliquer sur une `0.3.16-pre.*` ou sur un état intermédiaire. + +## Objet + +Exécuter le gate obligatoire de lecture, audit, brainstorming et sizing défini par `prompts/036-V0_3_17_START_PROMPT.md` avant toute implémentation du cycle de vie complet des conflict cases, du retry Store ou du hardening reconnect. + +Cette tranche : + +- vérifie l'archive stable reçue et son handoff `0.3.16` ; +- réaudite intégralement les règles KSP demandées ; +- inventorie les contrats Store API/Store/PostgreSQL/Worker/Transport/Config/Backfill ; +- réaudite les sources upstream nécessaires ; +- fixe le modèle conflict case/participants/history/resolution/reopen ; +- fixe la double revision conflict/selector et l'ordre de locks ; +- fixe les semantics de rétention/restore/purge guards ; +- fixe la classification Store `Transient/Terminal` ; +- fixe le retry Worker, la backpressure et `WorkerActivity::Blocked` ; +- confirme que les reconnects existants doivent être durcis et non réimplémentés ; +- recalibre les prereleases `0.3.17` ; +- ne modifie aucun Rust, SQL, migration ou UI. + +## Version workspace + +`Cargo.toml` : + +```text +header version : 651 -> 652 +workspace : 0.3.16 -> 0.3.17-pre.1 +``` + +Aucune autre modification sémantique du `Cargo.toml` racine n'est prévue. + +## Fichiers ajoutés + +```text +docs/plans/039-V0_3_17_RAW_OPERATIONAL_RESILIENCE_PLAN.md +docs/validation/034-V0_3_17_RAW_OPERATIONAL_RESILIENCE.md +deltas/0.3.17/pre.001.md +``` + +## Fichiers modifiés + +```text +Cargo.toml +``` + +## Fichiers supprimés + +```text +aucun +``` + +## Audit baseline réellement exécuté + +```text +General Rust rule audit: clean +Rust export completeness audit: 0 candidate(s) +KSP workspace Rust rule audit: clean +Markdown table audit: clean (352 table(s), 962 file(s)) +``` + +Contrôle complémentaire : + +```text +workspace version stable 0.3.16 +workspace members 22 +deltas 0.3.16 présents 21 +handoff prompt/plan/validation complet +lockfiles/target/node_modules absents +manifest inheritance cohérente +root lint contract cohérent +``` + +Le toolchain Cargo n'est pas installé dans l'environnement d'assemblage ; aucun PASS Cargo post-bump n'est inventé. + +## Décisions principales + +### Conflict case + +Identité backend-neutral : `RawTransactionReference` encapsulée par une référence de conflict case. + +Etats : + +```text +Open +Resolved +``` + +Aucun état supplémentaire n'est justifié. + +Chaque mutation réelle incrémente une revision `u64` monotone ; une réobservation identique est idempotente. + +### Participants et historique + +V004 doit ajouter un participant ledger monotone et un journal append-only. Le bootstrap depuis V003 ne reconstruit jamais un historique non prouvé. + +Le journal n'enregistre que références, enums/codes KSP et timestamps bornés ; aucun payload RAW, texte DB ou acteur libre. + +### Actions + +Les contrats futurs couvrent : + +```text +keep current canonical +promote variant +restore variant +resolve +reopen +``` + +Toute action mutable compare à la fois : + +```text +expected_conflict_revision +expected_canonical_revision +``` + +Une action déjà exactement appliquée est idempotente `AlreadyAtTarget`; une action concurrente incompatible est `StaleRevision`. + +### Rétention + +Les rows de variantes référencées restent durables comme identités. + +Purge payload interdite pour : + +```text +canonical selector courant +participants d'un conflict case Open +mutation en cours +``` + +L'historique conserve l'identité mais ne pin pas éternellement les bytes. Un restore utilise uniquement des bytes locaux Full/Archived ; après purge explicite, il échoue clairement sans fetch réseau. + +### Store error class + +Type public backend-neutral retenu : + +```text +Transient +Terminal +``` + +La classification est un contrat Store, pas une liste PostgreSQL codée dans le Worker. Un code inconnu est `Terminal`. + +SQLSTATE retryables initiaux : + +```text +40001 serialization_failure +40P01 deadlock_detected +57P01 admin_shutdown +57P02 crash_shutdown +57P03 cannot_connect_now +``` + +Pool timeout/connection loss sont Transient après configuration valide. Config, migration/schema mismatch, wrong network, data invalid, stale revision et retention conflicts sont Terminal. + +### Worker retry + +Le Worker possède le retry : + +```text +default retries 5 +maximum retries 20 +initial backoff 250 ms +max backoff default 5 s +absolute backoff max 30 s +jitter none in first implementation +``` + +Aucune retry queue : un item conserve son persistence slot jusqu'au résultat/exhaustion. L'admission queue existante fournit la backpressure. + +Pendant retry : + +```text +state Running +activity Blocked +health Degraded +``` + +Après exhaustion/Terminal : `Faulted/Unhealthy`. + +### Transport reconnect + +Les acteurs Transport existants restent propriétaires. Le principal gap statique identifié est l'absence de borne maximale KSP explicite sur `WsReconnectSettings::max_retries`, alors que Yellowstone possède `MAX_GRPC_RECONNECT_RETRIES = 100`. + +Reconnect, resubscribe/from_slot, replay delivery et coverage restent des concepts distincts. + +### Backfill + +Aucune dépendance Worker <-> Backfill. Les nouveaux contrats sont ajoutés comme traits séparés afin de ne pas casser `RawTransactionWrite` et les producteurs actuels. + +## Migration + +Une V004 additive est justifiée après les contrats `pre.002`. + +V000/V001/V002/V003 restent byte/checksum-identiques. + +V004 doit au minimum matérialiser : + +```text +participants +history append-only +projection de résolution +support physique de rétention de variante réellement nécessaire +``` + +## Découpage recalibré + +```text +pre.001 audit + architecture + sizing + plan +pre.002 Store API variants/conflicts/history/actions + StoreErrorClass +pre.003 PostgreSQL V004 additive + bootstrap legacy +pre.004 PostgreSQL lifecycle/actions/CAS/races/history +pre.005 variant retention/archive/purge guards/restore +pre.006 Store facade + physical error classification + Backfill compatibility +pre.007 Worker Store retry/backpressure/Blocked/cancellation +pre.008 Transport/Config reconnect bounds/hardening +pre.009 technical/live final gate +pre.010 documentation reconciliation +pre.011 publication preparation +rel.001 stable mechanical publication +``` + +## Hors périmètre + +```text +Store Desk -> 0.3.18 +Backfill multi-route -> 0.3.19 +Backfill Desk -> 0.3.20 +RAW -> STRUCTURAL +STRUCTURAL persistence +DECODED / DOMAIN +provider majority/priority +heuristic merge +unbounded/hidden Store retry +Worker network stack +Worker <-> Backfill dependency +network rollback +invented operator authentication +``` + +## Validations exécutées après assemblage + +Après création de cette tranche : + +```text +General Rust rule audit clean +Rust export completeness audit 0 candidate(s) +KSP workspace Rust rule audit clean +Markdown table audit clean (352 table(s), 965 file(s)) +workspace.package.version 0.3.17-pre.1 +byte-diff contre archive source PASS + fichiers ajoutés exactement 3 + fichiers modifiés exactement Cargo.toml + fichiers supprimés 0 +Cargo gates NOT RUN (toolchain absent) +``` + +## Gate demandé après application + +```bash +cargo fmt --all -- --check +python3 scripts/audit_rust_workspace_rules.py +python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas +cargo check --workspace +cargo clippy --workspace --all-targets --all-features -- -D warnings +``` + +Aucun test PostgreSQL live n'est requis pour `pre.001`. + +La tranche suivante, seulement après gate propre, est : + +```text +0.3.17-pre.002 +``` diff --git a/docs/plans/039-V0_3_17_RAW_OPERATIONAL_RESILIENCE_PLAN.md b/docs/plans/039-V0_3_17_RAW_OPERATIONAL_RESILIENCE_PLAN.md new file mode 100644 index 0000000..062e04f --- /dev/null +++ b/docs/plans/039-V0_3_17_RAW_OPERATIONAL_RESILIENCE_PLAN.md @@ -0,0 +1,1278 @@ + + + +# Plan `0.3.17` — résilience opérationnelle et cycle de vie complet des variantes RAW + +## 1. Objet + +Ce document ferme le gate d'ouverture `0.3.17-pre.001` demandé par `prompts/036-V0_3_17_START_PROMPT.md`. + +La release part exclusivement de la stable `v0.3.16` et doit compléter les fondations multi-variantes sans développer encore la Store Desk. + +La cible est volontairement backend/runtime : + +```text +Store API + inspection variantes/conflits/historique + actions revisionnées + classification Store Transient / Terminal + +PostgreSQL + conflict cases complets + participants + historique append-only + résolution/reopen concurrent-safe + rétention/purge guards des variantes + +Store facade + dispatch backend-neutral + classification publique sûre + +Worker + retry Store borné + backpressure + activity Blocked + cancellation/drain + +Transport/Config + durcissement des reconnects propriétaires existants + aucune nouvelle pile réseau + reconnect/replay/coverage toujours distincts +``` + +`0.3.18` consommera ensuite ces contrats dans `ksp-app-store-desk`. + +## 2. Base stable et audit d'ouverture + +Baseline logique requise : + +```text +v0.3.16 +workspace.package.version = 0.3.16 +``` + +L'archive reçue contient bien : + +```text +prompts/036-V0_3_17_START_PROMPT.md +prompts/035-V0_3_16_START_PROMPT.md +docs/plans/037-V0_3_16_RAW_RESILIENCE_CONFLICT_HANDOFF.md +docs/plans/038-V0_3_16_RAW_RESILIENCE_CONFLICT_PLAN.md +docs/validation/033-V0_3_16_RAW_RESILIENCE_CONFLICT.md +deltas/0.3.16/pre.001.md ... pre.010.md +deltas/0.3.16/pre.*-fix.* réellement publiés +deltas/0.3.16/rel.001.md +``` + +Le workspace comporte 22 membres et tous les manifests membres héritent de `workspace.package.version` et de l'édition workspace. + +Les audits statiques du dépôt exécutés sur l'archive avant modification sont propres : + +```text +General Rust rule audit: clean +Rust export completeness audit: 0 candidate(s) +KSP workspace Rust rule audit: clean +Markdown table audit: clean (352 table(s), 962 file(s)) +``` + +Le toolchain Cargo n'est pas disponible dans l'environnement d'assemblage de cette tranche. Les commandes Cargo ne sont donc pas déclarées réexécutées ici. Le gate utilisateur de la stable `0.3.16` fourni avec l'archive reste la preuve externe de `fmt/check/clippy` et des suites ciblées de fermeture. + +L'archive binaire a été matérialisée localement et son SHA-256 est enregistré dans `docs/validation/034-V0_3_17_RAW_OPERATIONAL_RESILIENCE.md`. + +L'environnement d'assemblage n'a pas permis une comparaison réseau indépendante du commit du tag Gitea. La provenance « ZIP téléchargé depuis le tag `v0.3.16` » reste donc fournie par l'opérateur ; l'identité stable interne de l'arbre est cohérente avec cette provenance. + +## 3. Règles normatives réauditées + +Les sources suivantes ont été relues dans l'ordre imposé par le prompt : + +```text +RULES.md +ROADMAP.md +CHANGELOG.md +docs/000-README.md + +docs/rules/RULES_GENERAL.md +docs/rules/RULES_KSP.md +docs/rules/RULES_RUST.md +docs/rules/RULES_DEPENDENCIES.md +docs/rules/RULES_DOCUMENTATION.md +docs/rules/FILE_CONTRACTS.md +docs/rules/VERSION_WORKFLOW.md +docs/rules/PROMPT_STRUCTURE.md +``` + +Conséquences bloquantes pour `0.3.17` : + +- Rust 2024 reste la seule édition workspace ; +- `unsafe` reste interdit ; +- `unwrap`, `expect`, `panic` et retours implicites restent interdits selon les lints KSP ; +- aucun `pub mod` n'est ajouté ; +- tout item partagé `pub`/`pub(crate)` est réexporté au crate root et consommé via `crate::Item` intra-crate ; +- les tests unitaires restent sous `unit_tests/`, les tests d'intégration sous `tests/` ; +- V000/V001/V002/V003 sont immuables une fois publiés ; toute évolution physique devient une migration additive suivante ; +- le Store physique reste privé derrière `ksp-store-lib` ; +- le Worker ne crée ni backend Store direct, ni client PostgreSQL, ni seconde pile Transport ; +- le Job Backfill et le Worker restent des producteurs indépendants ; +- aucune queue ou retry non borné n'est autorisé ; +- un PASS live n'est déclaré que lorsqu'il a réellement été exécuté ; +- `pre.001` est un gate de brainstorming/planification et ne doit pas contenir d'implémentation Rust/SQL lourde ; +- la lane technique/live finale, la réconciliation documentaire et la préparation de publication restent trois étapes distinctes ; +- la première prerelease `0.3.17-pre.001` utilise la version Cargo `0.3.17-pre.1`. + +## 4. Baseline fonctionnelle héritée de `0.3.16` + +### 4.1 Store API + +La stable fournit déjà : + +```text +RawTransactionVariantId +RawTransactionVariantReference +RawTransactionVariantOrigin +RawTransactionVariantRelation +RawTransactionVariantRelationReason +RawTransactionVariantComparison +RawTransactionVariantWriteOutcome +RawAcquisitionWriteOutcome::transaction_variant() +RawTransactionConflictStatus { Open, Resolved } +``` + +Le comparateur partagé reste l'autorité automatique et conserve exactement les relations : + +```text +Exact +CompatibleLessComplete +CompatibleMoreComplete +Conflict +Incomparable +``` + +La seule dominance actuellement prouvée est la troncature stricte de `logMessages`. Aucun élargissement de dominance n'est prévu dans `0.3.17`. + +Les capabilities actuelles couvrent canonical transaction, observations, inspection canonical/observations et rétention V001. Elles ne fournissent pas encore de capability publique dédiée aux variantes, conflict cases, historique ou actions de résolution. + +### 4.2 PostgreSQL V003 + +V003 possède quatre objets métier centraux : + +```text +ksp_raw_transaction_variants +ksp_raw_transaction_canonical_selectors +ksp_raw_transaction_observation_variants +ksp_raw_transaction_conflicts +``` + +Le conflict case V003 est volontairement minimal : une ligne courante par signature, avec : + +```text +status +revision +canonical_variant_id +incoming_variant_id +latest_relation +latest_reason_code +created_at +updated_at +``` + +Il ne contient ni participant ledger complet, ni journal append-only, ni résolution explicite vers une variante, ni historique d'actions. + +### 4.3 Promotion et conflit actuels + +L'acquisition PostgreSQL sérialise d'abord l'identité canonical V001 avec `FOR UPDATE`, bootstrap le ledger V003, compare les bytes et : + +```text +Exact -> réutilise le canonique +CompatibleLessComplete -> conserve la variante reçue, canonique inchangé +CompatibleMoreComplete -> promotion atomique selector + projection V001 +Conflict / Incomparable -> conserve la variante, ouvre/met à jour le conflict case +``` + +Une divergence durable `Conflict/Incomparable` retourne `QuarantinedConflict`, puis l'observation est liée à la variante réellement reçue avant commit. + +### 4.4 Worker actuel + +Le Worker dépend déjà de `ksp-store-lib`, pas du backend PostgreSQL. + +La convergence run-local ne fait que sérialiser une même identité. Chaque acquisition atteint ensuite le Store. + +`QuarantinedConflict` est un succès durable ; le Worker reste `Running` et projette une health `Degraded`. + +Une autre erreur Store est aujourd'hui projetée comme erreur de persistence et devient terminale. Il n'existe pas encore de retry Store dédié. + +`WorkerActivity` contient actuellement : + +```text +Unknown +Idle +Active +``` + +`WorkerHealth` contient : + +```text +Unknown +Healthy +Degraded +Unhealthy +``` + +La notion `Blocked` n'est donc pas encore matérialisée. + +### 4.5 Store error surface actuelle + +`ksp-store-lib` expose des codes sûrs mais plusieurs sont backend-spécifiques : + +```text +postgres_connect_failed +postgres_pool_timeout +postgres_read_failed +postgres_write_failed +postgres_data_invalid +postgres_migration_failed +postgres_migration_mismatch +postgres_schema_newer +postgres_tls_failed +wrong_network +... +``` + +Le backend PostgreSQL redacted les erreurs physiques en `PostgresBackendErrorKind`, mais les échecs `tokio-postgres` de statement/transaction sont encore souvent aplatis en `ReadFailed` ou `WriteFailed` sans conserver le SQLSTATE structuré. + +Cette perte d'information doit être corrigée avant de prétendre classifier sûrement `Transient/Terminal`. + +### 4.6 Transport/Config actuels + +WebSocket et Yellowstone possèdent déjà chacun un reconnect Transport-owned : + +```text +max_retries +initial_backoff +max_backoff +``` + +Config sait déjà projeter les defaults et overrides vers ces settings. + +Yellowstone possède déjà une borne KSP `MAX_GRPC_RECONNECT_RETRIES = 100`, ainsi que connect/unary/close timeouts bornés. + +Le WebSocket valide les backoffs et leur ordre mais ne borne pas encore explicitement `max_retries` à un maximum KSP. Cette asymétrie est une cible de hardening `0.3.17`, pas la justification d'une nouvelle boucle de reconnect. + +Yellowstone conserve déjà des projections distinctes pour : + +```text +reconnect +requested from_slot +replay delivery +replay coverage unproven +SubscribeReplayInfo first_available +``` + +Les contrats gap/coverage de `0.3.14` restent autoritaires. + +## 5. Réaudit upstream utile à `pre.001` + +### 5.1 PostgreSQL + +PostgreSQL 17 documente que les row locks sont tenus jusqu'à la fin de transaction, que les deadlocks sont détectés avec abandon d'une transaction, et recommande un ordre de locks cohérent. Il documente également le retry des `serialization_failure` SQLSTATE `40001` et, lorsque pertinent, des `deadlock_detected` SQLSTATE `40P01`. + +Politique KSP retenue : + +```text +ordre de locks déterministe +transactions courtes +aucune attente opérateur sous transaction +40001 -> Transient +40P01 -> Transient +23505/23P01 -> pas de retry générique ; classifier selon l'invariant KSP concret +``` + +### 5.2 tokio-postgres / deadpool-postgres + +`tokio-postgres` expose un `DbError::code()` structuré de type `SqlState`. Deadpool distingue notamment `Timeout`, `Backend`, `Closed`, `NoRuntimeSpecified` et `PostCreateHook`. + +Politique KSP retenue : + +```text +inspecter type/SQLSTATE avant redaction +ne jamais classifier par texte serveur +ne jamais conserver le message DB arbitraire dans l'erreur publique +projeter uniquement classe publique + code KSP sûr + phase statique +``` + +### 5.3 Solana WebSocket + +Les subscriptions Solana fournissent des notifications de session ; `signatureSubscribe` est explicitement one-shot et supprimé par le serveur après notification. Les docs PubSub ne fournissent pas une garantie générale de replay/coverage après remplacement d'une session. + +Politique KSP retenue : + +```text +reconnect physique -> fait Transport +resubscribe -> restauration logique de subscription +aucune de ces étapes ne prouve la coverage +la réparation/gap ledger Worker reste séparée +``` + +### 5.4 Yellowstone + +Le proto upstream expose `SubscribeRequest.from_slot`; KSP possède déjà `SubscribeReplayInfo` et une projection conservatrice du replay. + +Politique KSP retenue : + +```text +from_slot = demande de replay +ReplayInfo = borne de disponibilité annoncée +replay delivery = preuve de réception de matériau +coverage = preuve distincte et plus forte +``` + +Aucun reconnect réussi n'efface un gap par lui-même. + +### 5.5 Tokio + +Le runtime de retry doit être cancellable. `tokio::select!` annule les branches non retenues ; les primitives à file d'attente équitable comme `Mutex::lock` et `Semaphore::acquire` demandent une attention particulière à la cancellation safety. + +Politique KSP retenue : le backoff Store utilise un sleep stop-preemptible et ne réorganise pas silencieusement l'admission ou les permits de persistence. + +## 6. Modèle conceptuel conflict case retenu + +### 6.1 Identité + +Un conflict case est durablement identifié par la même identité transactionnelle backend-neutral : + +```text +RawTransactionConflictReference + transaction: RawTransactionReference +``` + +Aucun UUID ou identifiant provider n'est nécessaire. + +Le backend PostgreSQL reste lié à un seul network, mais la DTO publique conserve le `RawTransactionReference` complet afin que l'identité publique reste `(network, signature)`. + +### 6.2 Etats + +Aucun troisième état n'est justifié au gate : + +```text +Open +Resolved +``` + +Les notions « stale », « blocked », « archived » ou « unavailable payload » sont des outcomes/états d'autres objets et ne deviennent pas des statuts artificiels du conflict case. + +### 6.3 Revision + +Chaque mutation durable du conflict case incrémente une revision `u64` strictement monotone : + +```text +création Open revision = 1 +nouveau participant/relation revision + 1 +résolution revision + 1 +reopen revision + 1 +nouvelle divergence après Resolved -> reopen atomique + revision + 1 +``` + +Une réobservation strictement identique d'un état Open déjà représenté reste idempotente et ne consomme pas une revision supplémentaire. + +L'épuisement `u64` est terminal/fail-closed ; aucun wrap n'est autorisé. + +### 6.4 Participants + +Le participant ledger est monotone par conflict case : + +```text +(transaction, variant_id) unique +first_seen_revision +first_seen_at +``` + +Le canonique impliqué dans la divergence et chaque variante divergente observée deviennent participants. + +Un participant n'est pas supprimé lors de la résolution ou d'un reopen ; le journal conserve l'audit de la case complète. + +### 6.5 Historique append-only + +Chaque transition réelle produit un événement append-only : + +```text +Opened +ParticipantAdded +ResolvedKeepCanonical +ResolvedPromoteVariant +ResolvedRestoreVariant +Reopened +``` + +Un événement contient seulement les références/codes nécessaires : + +```text +conflict reference +conflict revision +event kind +canonical before +candidate/participant lorsque pertinent +canonical after +relation/reason lorsque pertinent +resolution action lorsque pertinent +timestamp KSP borné +``` + +Aucun payload RAW, SQL, texte serveur ou chaîne acteur libre n'entre dans l'historique. + +`0.3.17` ne possède pas de système d'identité/authentification opérateur. Il ne doit donc pas inventer un `actor` arbitraire. Le journal distingue seulement l'origine sémantique sûre de l'action lorsqu'elle est utile : + +```text +Automatic +Operator +``` + +L'identité humaine authentifiée éventuelle appartient à une couche future disposant réellement de ce contrat. + +### 6.6 Résolution courante + +Le conflict case courant expose : + +```text +status +revision +canonical variant courante +selector revision courante +latest incoming participant +latest relation/reason +resolved canonical variant si Resolved +latest resolution action si Resolved +created_at +updated_at +participant_count +``` + +Le couple `conflict_revision + canonical_revision` évite de confondre évolution du conflit et évolution du selector. + +## 7. Contrats Store API proposés + +Les noms exacts peuvent être ajustés lors de `pre.002` pour respecter les inventaires publics, mais la sémantique suivante est fixée. + +### 7.1 Inspection des variantes + +DTOs backend-neutral : + +```text +RawTransactionVariantSummary +RawTransactionVariantInspectionQuery +RawTransactionVariantDetail +``` + +Le summary reste payload-free et expose au maximum : + +```text +variant reference +origin +slot +block_time +format identity +retention state +payload size si disponible +is_current_canonical +observation count +created_at +``` + +Le `Debug` ne rend pas la signature brute, les bytes ou un hash de contenu sensible lorsque les règles existantes l'interdisent. + +Le detail explicite peut fournir la transaction complète uniquement via une capability dédiée et bornée ; les listes interactives restent payload-free. + +Capability cible : + +```text +RawTransactionVariantInspectionRead +``` + +### 7.2 Inspection des conflict cases + +DTOs : + +```text +RawTransactionConflictReference +RawTransactionConflictSummary +RawTransactionConflictInspectionQuery +RawTransactionConflictDetail +RawTransactionConflictParticipantSummary +``` + +Capability cible : + +```text +RawTransactionConflictInspectionRead +``` + +La liste est random-access bornée comme les inspections Store actuelles. La query peut filtrer au minimum par network/status et, lorsque nécessaire, par transaction exacte ; elle n'invente pas de recherche texte libre. + +### 7.3 Historique + +DTOs : + +```text +RawTransactionConflictEventKind +RawTransactionConflictEventSummary +RawTransactionConflictHistoryQuery +``` + +Capability cible : + +```text +RawTransactionConflictHistoryRead +``` + +L'historique est ordonné par revision et paginé ; aucun event n'embarque de payload RAW. + +### 7.4 Actions mutables + +Une seule capability cohérente peut porter des requests typées : + +```text +RawTransactionConflictActionWrite +``` + +Actions : + +```text +KeepCurrentCanonical +PromoteVariant +RestoreVariant +Resolve +Reopen +``` + +`Resolve` n'est pas une fusion implicite ; il clôt un case avec une décision canonique explicite. `PromoteVariant` et `RestoreVariant` sélectionnent une variante durable donnée. Le nom `Restore` signale l'intention opérateur mais utilise les mêmes garanties atomiques de selector que `Promote`. + +Chaque request mutable contient : + +```text +conflict reference +expected_conflict_revision +expected_canonical_revision +cible variant lorsque pertinent +``` + +Les revisions valent `u64 > 0`. + +### 7.5 Outcomes d'action + +Les outcomes ne transforment pas une concurrence métier attendue en erreur opaque : + +```text +Applied +AlreadyAtTarget +StaleRevision +ConflictNotOpen +ConflictNotResolved +VariantNotParticipant +VariantPayloadUnavailable +``` + +La liste exacte sera minimisée en `pre.002`, mais `StaleRevision` et `AlreadyAtTarget` doivent rester distincts. + +Idempotence retenue : une répétition d'une action dont la cible est déjà exactement l'état durable demandé renvoie `AlreadyAtTarget` sans nouvelle revision. Une requête stale visant un autre état renvoie `StaleRevision` et ne modifie rien. + +## 8. Classification publique Store `Transient / Terminal` + +### 8.1 Type public + +`ksp-store-api` doit posséder un enum backend-neutral non exhaustif : + +```text +StoreErrorClass + Transient + Terminal +``` + +La classification d'une erreur concrète doit être obtenue via un contrat stable, pas par comparaison ad hoc de strings dans le Worker. + +Contrat cible : + +```text +StoreErrorClassifier + classify_store_error(&Error) -> StoreErrorClass +``` + +`ksp-store-lib::Store` implémente ce contrat. Le Worker enrichit son port privé de persistence avec cette capability au lieu de connaître les codes PostgreSQL. + +Fail-closed : un code inconnu est `Terminal` jusqu'à classification explicite. + +### 8.2 Matrice KSP retenue + +`Transient` : + +```text +connexion physique indisponible/reset après configuration valide +pool temporairement indisponible / timeout d'acquisition +SQLSTATE classe connection exception lorsque l'opération est rejouable +40001 serialization_failure +40P01 deadlock_detected +57P01 admin_shutdown +57P02 crash_shutdown +57P03 cannot_connect_now +``` + +`Terminal` : + +```text +configuration/secret/TLS local invalide +backend non compilé +network KSP mismatch +modèle/query invalide +stored data incompatible +migration mismatch +schema newer +constraint révélant une violation d'invariant KSP +legacy raw content conflict +stale revision +retention/pin conflict +variant absent ou payload indisponible pour action locale +page limit non représentable +retention compaction unsupported +``` + +Cas à ne pas généraliser : + +```text +23505 unique_violation +23P01 exclusion_violation +``` + +Ils ne deviennent pas `Transient` par défaut. Une opération KSP spécifique peut prouver qu'un de ces SQLSTATE représente une race retryable ; sinon fail-closed `Terminal`. + +### 8.3 Cancellation + +Une cancellation demandée localement par le Worker n'est pas un outage Store à retryer. Le runtime traite d'abord son token de stop/cancellation ; une erreur résultante n'est jamais recyclée en boucle de retry. + +### 8.4 Refactor PostgreSQL requis + +Le backend ne doit plus convertir immédiatement toute erreur de statement en `ReadFailed/WriteFailed` avant inspection. + +Une fonction privée unique doit : + +```text +inspecter tokio_postgres::Error +extraire DbError::code() si présent +classifier les SQLSTATE explicitement supportés +classifier les erreurs connection/closed/IO de façon sûre +jeter le texte/source externe après projection +retourner PostgresBackendErrorKind + phase statique +``` + +Les détails SQLSTATE restent privés ; la frontière publique ne reçoit que le code KSP redacted et `StoreErrorClass`. + +## 9. Stratégie PostgreSQL V004 + +### 9.1 Principe + +V000/V001/V002/V003 restent byte/checksum-identiques. + +`0.3.17` crée une migration additive V004 uniquement après stabilisation des contrats `pre.002`. + +### 9.2 Objets requis + +La forme physique minimale envisagée : + +```text +extension additive de ksp_raw_transaction_conflicts + resolved_canonical_variant_id nullable + latest_resolution_action nullable + resolved_at_unix_millis nullable + +ksp_raw_transaction_conflict_participants + transaction_signature + variant_id + first_seen_revision + first_seen_at_unix_millis + +ksp_raw_transaction_conflict_events + transaction_signature + revision + event_kind + canonical_before_variant_id + candidate_variant_id nullable + canonical_after_variant_id + relation nullable + reason_code nullable + resolution_action nullable + action_origin + created_at_unix_millis +``` + +Les colonnes exactes sont validées dans `pre.003` avec les contraintes réelles ; aucun champ libre n'est ajouté « au cas où ». + +### 9.3 Bootstrap legacy V003 + +Pour un conflict case déjà présent avant V004 : + +```text +conserver status/revision V003 +créer les participants prouvables depuis canonical_variant_id + incoming_variant_id +créer un événement BaselineImported ou équivalent explicitement qualifié legacy +ne pas inventer les transitions antérieures perdues +ne pas prétendre reconstruire un ancien canonical non prouvé +``` + +Si `BaselineImported` n'apporte pas assez de valeur, `pre.003` peut retenir un marqueur `history_complete_from_revision` sur la projection. Il est interdit de fabriquer un historique détaillé. + +## 10. Ordre de locks et CAS + +Ordre global retenu pour toute mutation d'une transaction RAW concernée : + +```text +1. ksp_raw_transactions identité V001 FOR UPDATE +2. canonical selector FOR UPDATE / CAS +3. conflict current row FOR UPDATE / CAS +4. variants nécessaires ordre variant_id croissant si plusieurs locks sont requis +5. participant/event/pin sidecars inserts/updates déterministes +6. projection V001 + selector + conflict même transaction +7. commit +``` + +Aucune interaction UI, sleep, réseau ou retry n'a lieu à l'intérieur de cette transaction. + +La résolution manuelle doit relire et comparer : + +```text +expected_conflict_revision +expected_canonical_revision +``` + +avant toute mutation. + +La promotion automatique `CompatibleMoreComplete` doit utiliser le même ordre de locks et continuer de ne s'appliquer qu'à une dominance prouvée. + +## 11. Matrice de races + +### Ingestion pendant résolution + +La première transaction qui détient l'identité V001 décide. La seconde relit selector/conflict après acquisition du lock et réévalue sa request/comparaison. Aucun résultat calculé avant lock n'est appliqué aveuglément. + +### Promotion automatique pendant résolution manuelle + +Même lock order. Si la promotion automatique gagne, la `canonical_revision` change et la résolution manuelle devient stale sauf si sa cible est déjà exactement atteinte. + +### Deux résolutions concurrentes + +Une seule peut satisfaire les deux expected revisions. L'autre retourne `StaleRevision`, sauf répétition exacte de la cible déjà durable -> `AlreadyAtTarget`. + +### Nouvelle divergence après Resolved + +L'ingestion ouvre de nouveau la même conflict identity, incrémente la conflict revision, ajoute le participant si nouveau et écrit `Reopened`/`ParticipantAdded` de manière append-only. La décision précédente reste historique. + +### Rétention pendant résolution + +La rétention acquiert d'abord l'identité V001 selon le même ordre et réévalue les guards sous lock. Elle ne peut purger les bytes d'une variante requise par le selector courant ou un conflict case Open. + +### ForceRehydrate + +Le contrat V001 reste : la tombstone purgée conserve assez d'identité/hash pour refuser une rehydratation incompatible. `ForceRehydrate` ne choisit jamais automatiquement une autre variante et ne devient pas un mécanisme de rollback réseau. + +## 12. Rétention des variantes + +### 12.1 Deux notions distinctes + +Le plan distingue : + +```text +identity retention +payload retention +``` + +L'identité d'une variante référencée par selector, observation mapping ou historique n'est jamais supprimée. + +Les bytes peuvent suivre : + +```text +Full -> Archived -> Purged +``` + +uniquement lorsque les guards l'autorisent. + +### 12.2 Hard payload pins + +Purge interdite lorsque la variante est : + +```text +canonical selector courant +participant d'un conflict case Open +cible d'une mutation en cours sous transaction +``` + +Une observation mapping et l'historique conservent l'identité mais ne pin pas éternellement le payload à eux seuls. + +Cette décision évite qu'un historique append-only transforme toute divergence passée en rétention infinie des bytes. + +### 12.3 Resolution et rollback + +Après résolution : + +```text +la variante canonique reste pin par le selector +les variantes perdantes peuvent être archivées +une purge reste une action explicite de rétention, jamais automatique dans Store +``` + +`RestoreVariant` fonctionne uniquement avec une variante dont les bytes sont encore localement disponibles `Full` ou `Archived`. + +Si le payload a été explicitement purgé : + +```text +VariantPayloadUnavailable +``` + +est retourné ; le Store ne consulte pas le réseau et ne fabrique pas les bytes. + +L'historique continue de pointer vers l'identité de la variante purgée et rend donc la perte de capacité de rollback visible, non silencieuse. + +### 12.4 Archive physique des variantes + +V003 sait représenter `retention_state` sur les variantes mais ne possède pas encore le sidecar d'archive équivalent au canonical V001. + +`pre.005` doit décider/implémenter l'objet physique minimal permettant : + +```text +Full variant payload inline +Archived variant payload local sidecar +Purged variant payload absent mais metadata/identity conservées +``` + +Aucune suppression de row de variante n'est nécessaire. + +## 13. Worker Store retry + +### 13.1 Ownership + +Le backend classe et retourne. + +Le Worker décide : + +```text +retry count +backoff +stop preemption +backpressure +health/activity +exhaustion +``` + +Aucune boucle retry cachée n'est ajoutée dans PostgreSQL ou `ksp-store-lib`. + +### 13.2 Settings ciblés + +Extension de `RawTransactionIngestSettings` : + +```text +store_retry_max_retries +store_retry_initial_backoff +store_retry_max_backoff +``` + +Valeurs proposées : + +```text +default max_retries = 5 +maximum max_retries = 20 +default initial_backoff = 250 ms +default max_backoff = 5 s +minimum backoff = 10 ms +maximum backoff setting = 30 s +``` + +`max_retries = 0` désactive le retry et conserve un comportement terminal immédiat sur une erreur `Transient`. + +Aucun jitter n'est introduit dans la première implémentation. L'ownership reste explicitement Worker et peut être réaudité plus tard si les mesures live montrent un herd effect réel. La concurrence de persistence et l'admission bornée constituent déjà la première limite anti-storm. + +### 13.3 Backoff + +Backoff exponentiel borné : + +```text +initial * 2^(attempt - 1) +cap = max_backoff +``` + +Le calcul est overflow-safe et saturé au cap. + +Le sleep est stop-preemptible avec `tokio::select!`. + +### 13.4 Aucune retry queue supplémentaire + +Une acquisition admise conserve son slot de travail pendant toute sa séquence de retry. + +Il n'existe pas de queue de retry distincte : + +```text +source -> admission queue bornée -> persistence concurrency permit -> attempt/backoff/attempt -> durable outcome ou exhaustion +``` + +Ainsi : + +- le nombre de persistence sequences actives reste borné par `persistence_concurrency` ; +- lorsque ces séquences sont bloquées sur Store, l'admission queue se remplit puis applique naturellement la backpressure existante ; +- aucune croissance mémoire sans borne n'est possible. + +### 13.5 Ambiguous commit + +Un échec de connexion après envoi peut être ambigu quant au commit serveur. + +Le retry est autorisé uniquement parce que l'acquisition RAW Store est conçue idempotente : identité canonical, variante exacte et observation key déterministe convergent vers le même état durable. + +Le Worker ne mémorise jamais « failed donc absent ». + +Après retry, un résultat durable `AlreadyPresent/AlreadyPresent` est un succès normal. + +### 13.6 Blocked / health + +`Blocked` est une activité, pas une health. + +Evolution prévue de `ksp-worker-api::WorkerActivity` : + +```text +Unknown +Idle +Active +Blocked +``` + +Projection : + +```text +retry Store en attente/backoff avec travail durable non terminé + state = Running + activity = Blocked + health = Degraded + +Store récupéré et pipeline reprend + state = Running + activity = Active/Idle selon travail + health = politique normale, sauf autre dégradation + +retry exhausté / erreur Terminal + state = Faulted(code) + health = Unhealthy +``` + +Le snapshot RAW ajoute des compteurs bornés/checked nécessaires, sans code PostgreSQL physique : + +```text +store_retry_total +store_transient_failure_total +store_retry_exhaustion_total +store_blocked_in_flight +``` + +Les noms définitifs seront minimisés en `pre.007`. + +### 13.7 Stop et drain + +Pendant backoff : stop interrompt immédiatement le sleep et passe au drain normal. + +Une persistence déjà envoyée au Store n'est pas relancée aveuglément en parallèle. La séquence attend son résultat ou le mécanisme de drain/abort existant, puis aucune nouvelle tentative n'est admise après la décision de stop. + +## 14. Transport/Config `0.3.17` + +### 14.1 Pas de nouvelle abstraction universelle + +Les acteurs existants restent propriétaires : + +```text +WsSession -> reconnexion WebSocket +Yellowstone subscribe stream -> reconnexion/replay gRPC +HTTP runtime -> retry HTTP existant +``` + +Le Worker ne reçoit aucun socket ou channel bas niveau. + +### 14.2 WebSocket + +Hardening prévu : + +```text +ajouter une borne KSP explicite à reconnect.max_retries +aligner les validations Config/Transport +vérifier overflow-safe du backoff +conserver stop preemption et resubscribe existants +conserver remote subscription IDs privés +``` + +Aucun changement de semantics de coverage. + +### 14.3 Yellowstone + +Le gRPC possède déjà les primitives requises. `0.3.17` doit principalement vérifier : + +```text +bornes Config identiques aux bornes Transport +connect/session deadlines réellement appliquées +close/stop toujours bornés +reconnect attempt snapshot cohérent +from_slot/ReplayInfo conservateurs +aucune fermeture de gap par simple reconnect +``` + +### 14.4 Reset d'un budget de reconnect + +Le comportement existant reset le budget après un reconnect réussi. Le gate technique doit vérifier qu'un flapping durable reste observable et ne produit pas une boucle CPU : chaque perte de session réapplique le backoff borné et les compteurs de lifecycle restent visibles. + +Aucun `reset_after_stable_duration` supplémentaire n'est justifié dans cette release sans défaut concret. + +## 15. Compatibilité Job Backfill + +Le Job Backfill reste inchangé dans son ownership : + +```text +Transport observé -> canonicalisation RAW commune -> Store facade +``` + +Il doit continuer à : + +- traiter `QuarantinedConflict` selon son contrat actuel ; +- ne pas dépendre des nouvelles capacités opérateur pour persister ; +- ne pas implémenter le retry live Worker ; +- ne pas recevoir de dépendance Worker ; +- rester compilable lorsque les traits Store existants sont étendus par de nouveaux traits séparés plutôt que par modification incompatible des traits déjà implémentés. + +C'est une raison supplémentaire pour ajouter des capabilities fines séparées au lieu d'ajouter des méthodes obligatoires à `RawTransactionWrite`. + +## 16. Sécurité et redaction + +Les nouvelles surfaces ne doivent jamais exposer : + +```text +PostgreSQL URI/password +SQL statement +DbError message/detail/hint +endpoint complet avec secret +payload RAW dans summary/history/error +signature brute dans Debug lorsque la politique actuelle la redacted +content hash dans Debug si la surface existante l'interdit +backend row/client/transaction handle +``` + +Les SQLSTATE sont utilisés en interne pour classification, pas comme dépendance publique du Worker. + +Les reason/action codes publics sont des enums/codes KSP bornés, jamais des chaînes serveur ou saisies utilisateur libres. + +## 17. Tests et preuves nécessaires + +### 17.1 Store API + +Canaris attendus : + +```text +constructibilité crate-root +object safety des nouveaux traits +non-exhaustive des enums évolutifs +validation des revisions non nulles +queries/histories bornées +Debug payload-free/redacted +external backend peut implémenter les nouveaux contrats sans dépendance runtime +classification inconnue fail-closed Terminal +``` + +### 17.2 V004/PostgreSQL + +Canaris : + +```text +V000/V001/V002/V003 bytes/checksums inchangés +registry V004 exacte +bootstrap legacy sans historique fabriqué +participants monotones +history append-only +revision CAS +selector/projection atomiques +ordre de locks stable +résolution concurrente +reopen concurrent +rollback complet sur erreur tardive +purge guard sous race +variant archive/purge shape exact +``` + +### 17.3 Worker + +Canaris : + +```text +Transient -> retry borné +Terminal -> aucun retry +max_retries=0 -> faute immédiate +backoff cap/overflow exact +stop pendant sleep +stop pendant attempt +queue bornée sous outage prolongé +activity Blocked + health Degraded +recovery -> reprise sans respawn Worker +exhaustion -> Faulted/Unhealthy +ambiguous durable success -> idempotence, pas double observation +QuarantinedConflict reste succès non terminal +``` + +### 17.4 Transport + +Canaris : + +```text +WS reconnect max_retries borné +WS/grpc backoff borné et stop-preemptible +Config projection exacte defaults/overrides +reconnect != replay delivery +replay delivery != coverage +Yellowstone from_slot/ReplayInfo non régressés +``` + +### 17.5 Live PostgreSQL + +La lane technique finale doit exécuter un test opt-in PostgreSQL réel couvrant au minimum : + +```text +open conflict -> participants/history +résolution CAS +stale concurrent action +reopen par nouvelle divergence +restore d'une variante locale +purge guard d'un participant Open +archive puis restore lorsque supporté +transient-classification path observable sans secret +rollback transactionnel +``` + +La version serveur réellement observée est journalisée de manière sûre ; aucune URI n'est rendue. + +### 17.6 Outage/recovery Store + +Une preuve ciblée doit simuler ou provoquer de manière déterministe : + +```text +Store temporairement indisponible +Worker Running/Blocked/Degraded +admission bornée +recovery avant exhaustion +persistence idempotente +retour à activité normale +``` + +Si une panne PostgreSQL réelle contrôlée n'est pas raisonnablement automatisable, le test déterministe sur le port privé Worker prouve la politique et le live PostgreSQL prouve séparément la classification physique. + +## 18. Découpage recalibré de `0.3.17` + +Le découpage initial `pre.001 -> pre.009` reste trop dense autour du couple contrats/migration/backend. Il est subdivisé sans fusionner les lanes de fermeture. + +### `0.3.17-pre.001` + +Gate présent : lecture, audit, upstream re-audit, modèle conceptuel, contrats proposés, error classification, retry/reconnect policy, sizing et plan. Aucun Rust/SQL lourd. + +### `0.3.17-pre.002` + +Store API : DTOs/capabilities variantes/conflicts/history/actions, expected revisions, outcomes stale/idempotent et type/contrat `StoreErrorClass` backend-neutral. + +### `0.3.17-pre.003` + +PostgreSQL V004 additive : participants, history, projection de résolution et bootstrap legacy, avec canaris byte/checksum V000-V003. + +### `0.3.17-pre.004` + +PostgreSQL lifecycle : inspection, resolve/promote/keep/restore/reopen, CAS double revision, lock order, races ingestion/résolution et history append-only. + +### `0.3.17-pre.005` + +Rétention des variantes : archive locale, payload states, purge guards, interaction selector/open conflict, restore local et ForceRehydrate non régressé. + +### `0.3.17-pre.006` + +`ksp-store-lib` : dispatch des nouvelles capabilities, classification publique, raffinement SQLSTATE/backend privé, erreurs redacted et régression Job Backfill. + +### `0.3.17-pre.007` + +Worker : settings Store retry, backoff borné, `WorkerActivity::Blocked`, backpressure, cancellation, exhaustion, recovery et snapshots/counters sûrs. + +### `0.3.17-pre.008` + +Transport/Config : bornage WebSocket manquant, cohérence reconnect settings, hardening session/stop et non-régression Yellowstone replay/coverage. + +### `0.3.17-pre.009` + +Gate technique/live final : races cross-layer, Store outage/recovery, retry, reconnect, PostgreSQL live, tests workspace pertinents et graphes de dépendances requis. + +### `0.3.17-pre.010` + +Réconciliation documentaire finale uniquement : plan, validation, README/USAGE et architectures durables concernées. Aucun nouveau comportement runtime. + +### `0.3.17-pre.011` + +Préparation de publication minimale : `CHANGELOG.md`, `ROADMAP.md`, prompt `0.3.18`, version et delta. + +### `0.3.17-rel.001` + +Publication stable mécanique après gates propres. + +La prévision reste souple : un défaut local peut produire un `fix.*`; une tranche trop lourde peut être subdivisée, mais la lane technique/live doit rester avant la réconciliation documentaire et la publication prep. + +## 19. Fichiers attendus par tranche + +Le plan ne pré-approuve pas des fichiers non nécessaires, mais l'ownership attendu est : + +```text +pre.002 ksp-store-api +pre.003 ksp-store-postgres-lib migration/schema registry/resources +pre.004 ksp-store-postgres-lib runtime/SQL/tests +pre.005 ksp-store-api + ksp-store-postgres-lib retention variant +pre.006 ksp-store-lib + PostgreSQL classification + Backfill regression tests +pre.007 ksp-worker-api + ksp-worker-raw-transaction-ingest-lib +pre.008 ksp-onchain-transport-lib + ksp-config-lib +pre.009 tests/hardening/live only selon besoins prouvés +pre.010 documentation durable +pre.011 release metadata/prompt +``` + +Toute dépendance nouvelle doit être justifiée séparément avant ajout. Aucune nouvelle crate n'est prévue. + +## 20. Hors périmètre confirmé + +`0.3.17` ne contient pas : + +```text +Store Desk frontend/backend Tauri +Backfill multi-route/multi-stratégie +Backfill Desk multi-route +RAW -> STRUCTURAL +STRUCTURAL persistence +DECODED / DOMAIN +majority voting provider +provider priority +arbitrage canonique par provenance +fusion heuristique JSON +retry Store illimité +retry caché dans le backend +nouveau client PostgreSQL dans Worker +nouvelle pile WebSocket/gRPC dans Worker +nouvelle dépendance Worker <-> Backfill +reconstruction réseau comme rollback normal +identité opérateur/authentication inventée +``` + +## 21. Critères de fermeture du gate `pre.001` + +Après ce plan, le gate conceptuel est considéré fermé lorsque la validation associée confirme : + +```text +base stable cohérente +règles relues et audits statiques propres +contrats Store API proposés et bornés +conflict lifecycle complet défini +revision/CAS et races définis +migration V004 justifiée et additive +rétention/pins/rollback définis +Transient/Terminal défini sans texte libre +Worker retry/backpressure/Blocked défini +Transport reconnect réutilise les mécanismes existants +Backfill reste indépendant et compatible +preuves techniques/live identifiées +prereleases recalibrées +hors périmètre explicite +``` + +L'implémentation commence seulement en `0.3.17-pre.002`. diff --git a/docs/validation/034-V0_3_17_RAW_OPERATIONAL_RESILIENCE.md b/docs/validation/034-V0_3_17_RAW_OPERATIONAL_RESILIENCE.md new file mode 100644 index 0000000..d9f94d3 --- /dev/null +++ b/docs/validation/034-V0_3_17_RAW_OPERATIONAL_RESILIENCE.md @@ -0,0 +1,570 @@ + + + +# Validation `0.3.17` — résilience opérationnelle et cycle de vie des variantes RAW + +## 1. Objet + +Ce document suit les preuves de `0.3.17`. + +La révision initiale correspond au gate `0.3.17-pre.001`. Elle valide le point de départ, l'audit des règles, le modèle conceptuel, le sizing et la matrice de preuves à construire. Elle ne déclare aucune implémentation future comme acquise. + +## 2. Base source reçue + +Archive source fournie pour l'ouverture : + +```text +khadhroony-solana-project-v0.3.16.zip +``` + +SHA-256 local de l'archive reçue : + +```text +db3cc1b3fc3fb07ee864b02e2a7346c673d4cf63b33e8e872aca6c6458856ca0 +``` + +L'arbre extrait déclare : + +```text +workspace.package.version = 0.3.16 +edition = 2024 +workspace members = 22 +``` + +Le handoff stable est complet : `pre.001` à `pre.010`, les `fix.*` réellement publiés et `rel.001` sont présents sous `deltas/0.3.16/`. + +L'archive contient également le prompt `036` attendu et les plans/validations de fermeture `0.3.16`. + +## 3. Limite de vérification du tag + +La provenance annoncée par l'opérateur est le ZIP téléchargé depuis le tag Gitea `v0.3.16`. + +L'environnement local d'assemblage ne dispose pas d'un accès Git/DNS exploitable vers le dépôt permettant de comparer indépendamment le SHA du commit taggé avec cette archive. + +Le gate ne transforme donc pas cette absence de comparaison réseau en faux PASS. + +Contrôles réellement établis sur les bytes fournis : + +```text +version stable interne = 0.3.16 +rel.001 présent +prompt 036 présent +inventaire deltas 0.3.16 complet +manifests membres cohérents +aucun lockfile versionné +aucun target/node_modules/.git distribué +aucune clé .pem/.key détectée +``` + +## 4. Audit des règles avant modification + +Commandes réellement exécutées dans l'environnement d'assemblage : + +```bash +python3 scripts/audit_rust_workspace_rules.py +python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas +``` + +Résultat : + +```text +General Rust rule audit: clean +Rust export completeness audit: 0 candidate(s) +KSP workspace Rust rule audit: clean +Markdown table audit: clean (352 table(s), 962 file(s)) +``` + +Contrôle statique complémentaire : + +```text +workspace_version = 0.3.16 +workspace_members = 22 +delta_0.3.16_count = 21 +manifest members inherit workspace version/edition = PASS +mandatory rule/handoff files present = PASS +forbidden distribution artifacts = absent +root rust/clippy lint contract = exact +``` + +## 5. Gate Cargo de la stable fourni par l'opérateur + +Le log joint à la session de démarrage documente sur la stable `0.3.16` : + +```text +cargo fmt --all +cargo fmt --all -- --check +Rust audits +Markdown audit +cargo check --workspace +cargo clippy --workspace --all-targets --all-features -- -D warnings +``` + +ainsi que les suites ciblées : + +```text +ksp-store-api +ksp-store-lib +ksp-store-postgres-lib +ksp-job-backfill-lib +ksp-worker-raw-transaction-ingest-lib +``` + +Ces résultats appartiennent au checkout utilisateur `0.3.16` et ne sont pas présentés comme réexécutés dans l'environnement d'assemblage. + +## 6. Limitation Cargo de `pre.001` + +Le binaire `cargo` n'est pas disponible dans l'environnement d'assemblage. + +Les commandes suivantes sont donc `NOT RUN` ici après le bump `0.3.17-pre.1` : + +```text +cargo fmt --all -- --check +cargo check --workspace +cargo clippy --workspace --all-targets --all-features -- -D warnings +cargo test ... +``` + +`pre.001` ne modifie aucun Rust. Le gate opérateur demandé après application reste néanmoins obligatoire. + +## 7. Sources internes auditées + +Relues conformément au prompt : + +```text +gouvernance/règles complètes +handoff 0.3.16 complet +Store API README/USAGE/src/tests +Store lib README/USAGE/src/tests +PostgreSQL README/USAGE/src/tests/unit_tests/resources +Common RAW README/USAGE/src/tests +Worker API + Worker RAW README/USAGE/src/tests/unit_tests +Transport README/USAGE/src/tests +Config README/USAGE/src/tests +Backfill README/USAGE + persistence/tests ciblés +architectures 004/009/011 +validation 033 +``` + +## 8. Constat Store API + +Acquis : + +```text +variant identity/reference/origin +variant relation + reason +shared fail-closed comparator +variant-aware acquisition outcome +minimal conflict status Open/Resolved +canonical/observation inspection +canonical retention contracts +``` + +Manquant pour `0.3.17` : + +```text +variant inspection capability +conflict inspection capability +participant projection +history read capability +resolution/reopen action capability +expected conflict revision +expected canonical revision +stable stale-action outcome +backend-neutral StoreErrorClass +``` + +Verdict du gate : + +```text +CONTRACT GAP CONFIRMED +``` + +## 9. Constat PostgreSQL V003 + +V003 est additive et fournit : + +```text +variants +canonical selector + revision +observation -> variant mapping +minimal conflict current row +``` + +La table conflit courante est une projection, pas un historique complet. + +Manquant : + +```text +participants monotones +append-only transitions +resolved target/action +legacy bootstrap qualification +operator CAS actions +variant archive sidecar/purge guards +``` + +Verdict : + +```text +V004 ADDITIVE REQUIRED +V000/V001/V002/V003 MUST REMAIN IMMUTABLE +``` + +## 10. Constat locks/races + +L'acquisition actuelle verrouille d'abord la row canonical V001 puis manipule V003 dans la même transaction. La promotion canonique utilise un selector revisionné et CAS ; le conflict current row utilise lui aussi une revision monotone. + +Le gate fixe l'ordre cible : + +```text +V001 transaction identity +-> selector +-> conflict current row +-> variants ordered by variant_id if lock needed +-> participant/history/retention sidecars +-> projection updates +-> commit +``` + +Aucune attente externe n'est autorisée sous transaction. + +Scénarios obligatoires : + +```text +ingestion vs resolution +promotion auto vs manual resolution +resolution A vs resolution B +resolved -> new divergence -> reopen +retention vs resolution +ForceRehydrate exact/different +late failure -> full rollback +``` + +Verdict : + +```text +CAS MODEL DEFINED +RACE MATRIX DEFINED +IMPLEMENTATION PENDING pre.003-pre.005 +``` + +## 11. Constat rétention + +V003 conserve un `retention_state` sur chaque variante mais ne matérialise pas encore l'archive locale complète des payloads de variante. + +Politique validée au gate : + +```text +variant row identity never deleted when referenced +current canonical payload cannot be purged +Open conflict participant payload cannot be purged +history retains identity but is not an eternal payload pin by itself +archive is allowed when guards permit +purge is explicit, never automatic Store policy +restore uses local Full/Archived bytes only +purged restore target -> explicit VariantPayloadUnavailable +network is not normal rollback +ForceRehydrate does not select a historical variant +``` + +Verdict : + +```text +RETENTION/PIN SEMANTICS DEFINED +PHYSICAL ARCHIVE IMPLEMENTATION PENDING pre.005 +``` + +## 12. Constat Store errors + +Aujourd'hui : + +```text +Deadpool timeout -> PoolTimeout +Deadpool backend/closed -> ConnectFailed +statement/transaction errors -> frequently ReadFailed/WriteFailed +``` + +La dernière catégorie perd trop tôt le SQLSTATE et ne permet pas une classification retry sûre. + +Politique retenue : + +```text +StoreErrorClass = Transient | Terminal +classifier contract backend-neutral +unknown code -> Terminal +SQLSTATE inspected privately before redaction +no free-form DB text in public errors +``` + +Transient initialement autorisés : + +```text +connection/reset/unavailable after valid config +pool timeout/unavailable +40001 serialization_failure +40P01 deadlock_detected +57P01 admin_shutdown +57P02 crash_shutdown +57P03 cannot_connect_now +``` + +Terminal initialement autorisés : + +```text +config/secret/TLS local invalid +backend not compiled +wrong network +model/query/data invalid +migration mismatch/schema newer +KSP invariant constraint failure +legacy raw conflict +stale revision +retention/pin conflict +missing/unavailable variant for local action +``` + +`23505` et `23P01` ne sont jamais generic-retry par défaut. + +Verdict : + +```text +CLASSIFICATION POLICY DEFINED +PHYSICAL MAPPING PENDING pre.006 +``` + +## 13. Constat Worker retry/Blocked + +Le Worker a aujourd'hui : + +```text +admission queue bounded: default 256, max 65536 +persistence concurrency: default 8, max 64 +shutdown drain timeout: default 10 s, max 30 s +no Store retry settings +WorkerActivity = Unknown/Idle/Active +``` + +Politique retenue : + +```text +retry owned by Worker +no retry queue +persistence permit held across retry sequence +max_retries default 5, maximum 20 +initial backoff default 250 ms +max backoff default 5 s +absolute settings backoff max 30 s +no jitter in first implementation +sleep stop-preemptible +Transient -> Running/Blocked/Degraded +recovery -> Running/Active-or-Idle +exhaustion/Terminal -> Faulted/Unhealthy +``` + +Verdict : + +```text +RETRY/BACKPRESSURE MODEL DEFINED +WORKER IMPLEMENTATION PENDING pre.007 +``` + +## 14. Constat Transport/Config + +WebSocket : reconnect déjà existant avec max retries/backoff/resubscribe et stop-aware actor. + +Yellowstone : reconnect déjà existant, `from_slot`, `SubscribeReplayInfo`, compteurs replay et distinction explicite `replay_coverage_unproven`. + +Config projette déjà les settings/overrides reconnect. + +Gap observé : + +```text +Yellowstone max_retries possède une borne KSP explicite = 100 +WebSocket max_retries ne possède pas encore de borne maximale KSP équivalente +``` + +Décision : durcir la pile existante en `pre.008`, sans nouveau reconnect Worker. + +Verdict : + +```text +RECONNECT OWNERSHIP ALREADY CORRECT +BOUNDS/PARITY HARDENING REQUIRED +COVERAGE SEMANTICS UNCHANGED +``` + +## 15. Réaudit upstream + +Faits confirmés : + +```text +PostgreSQL 17 + row locks et deadlock detection documentés + consistent lock order recommandé + serialization_failure SQLSTATE 40001 retryable + deadlock_detected SQLSTATE 40P01 retryable lorsque pertinent + +tokio-postgres + DbError::code() -> SqlState structuré + +deadpool-postgres + PoolError distingue Timeout/Backend/Closed/config-hook classes + +Solana PubSub + subscription semantics documentées + signatureSubscribe one-shot auto-cancel serveur + aucune preuve générale de replay/coverage après reconnect + +Yellowstone upstream + SubscribeRequest.from_slot existe + replay availability est une notion distincte + +Tokio + cancellation safety doit être raisonnée autour de select!/queues fair +``` + +Politique KSP correspondante documentée dans le plan 039. + +## 16. Compatibilité Job Backfill + +Le Backfill actuel passe par les contrats Store communs et ne dépend ni du Worker ni du backend PostgreSQL direct. + +Décision : + +```text +nouveaux traits séparés +pas d'ajout obligatoire à RawTransactionWrite +pas de retry live Worker dans Job +QuarantinedConflict reste compatible +pas de scope multi-route dans 0.3.17 +``` + +Verdict : + +```text +BACKFILL BOUNDARY PRESERVED BY DESIGN +REGRESSION TESTS REQUIRED pre.006/pre.009 +``` + +## 17. Migration sizing + +Migration prévue : + +```text +V004 additive +participants +append-only history +resolved projection fields +variant archive support if needed by final retention design +``` + +La migration doit conserver strictement les resources/checksums V000-V003. + +Le bootstrap V003 -> V004 ne peut créer que des faits prouvables depuis l'état courant ; aucun historique antérieur détaillé n'est reconstruit artificiellement. + +## 18. Prévision de prereleases validée par le gate + +```text +pre.001 audit + architecture + sizing + plan +pre.002 Store API variants/conflicts/history/actions + StoreErrorClass +pre.003 PostgreSQL V004 additive + bootstrap legacy +pre.004 PostgreSQL lifecycle/actions/CAS/races/history +pre.005 variant retention/archive/purge guards/restore +pre.006 Store facade + physical error classification + Backfill compatibility +pre.007 Worker Store retry/backpressure/Blocked/cancellation +pre.008 Transport/Config reconnect bounds/hardening +pre.009 technical/live final gate +pre.010 documentation reconciliation +pre.011 publication preparation +rel.001 stable mechanical publication +``` + +Cette prévision peut recevoir des `fix.*` ou subdivisions si une tranche ne peut pas rester petite et validable. + +## 19. Matrice de preuve à remplir + +Etat au gate `pre.001` : + +```text +base archive internal consistency PASS +rules static audit PASS +Markdown audit baseline PASS +Cargo post-bump in assembly environment NOT RUN (toolchain absent) +Store API contract model DEFINED +conflict lifecycle model DEFINED +revision/CAS model DEFINED +race matrix DEFINED +retention/pin model DEFINED +Store Transient/Terminal policy DEFINED +Worker retry/backpressure/Blocked policy DEFINED +Transport reconnect ownership/bounds DEFINED +Backfill compatibility strategy DEFINED +V004 migration strategy DEFINED +PostgreSQL live 0.3.17 PENDING pre.009 +Store outage/recovery proof PENDING pre.009 +reconnect live/deterministic proof PENDING pre.009 +workspace final technical gate PENDING pre.009 +documentation reconciliation PENDING pre.010 +publication preparation PENDING pre.011 +``` + +## 20. Hors périmètre validé + +```text +Store Desk Tauri -> 0.3.18 +Backfill multi-route -> 0.3.19 +Backfill Desk multi-route -> 0.3.20 +RAW -> STRUCTURAL +STRUCTURAL persistence +DECODED / DOMAIN +provider voting/priority +heuristic JSON merge +unbounded Store retry +hidden backend retry loop +Worker-owned network stack +Worker <-> Backfill dependency +network rollback of old variant +free-form operator identity without auth contract +``` + +## 21. Gate `pre.001` + +Le gate conceptuel est fermé : + +```text +archive/rules audit CLOSED +Store API proposal CLOSED +conflict lifecycle CLOSED +revision/races CLOSED +retention/pins CLOSED +Transient/Terminal CLOSED +Worker retry/Blocked CLOSED +Transport reconnect ownership CLOSED +Backfill compatibility CLOSED +migration strategy CLOSED +live proof plan CLOSED +prerelease sizing CLOSED +out-of-scope CLOSED +``` + +Aucune implémentation Rust/SQL lourde n'est autorisée dans `pre.001`. + +La tranche suivante est : + +```text +0.3.17-pre.002 — Store API variants/conflicts/history/actions + StoreErrorClass +``` + +## 22. Gate opérateur après application de `pre.001` + +Comme seuls `Cargo.toml` et des Markdown sont modifiés/ajoutés : + +```bash +cargo fmt --all -- --check +python3 scripts/audit_rust_workspace_rules.py +python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas +cargo check --workspace +cargo clippy --workspace --all-targets --all-features -- -D warnings +``` + +Aucun test live n'est requis par cette tranche documentaire de planning.