From 2ca56dbaf43d9bea8dbe87f6681c711b3cb549bf Mon Sep 17 00:00:00 2001 From: SinuS Von SifriduS Date: Fri, 11 Sep 2026 19:37:36 +0200 Subject: [PATCH] v0.3.14-pre.001 --- Cargo.toml | 4 +- deltas/0.3.14/pre.001.md | 218 ++++ ..._MULTI_SOURCE_GAP_REPAIR_HARDENING_PLAN.md | 1060 +++++++++++++++++ ..._3_14_MULTI_SOURCE_GAP_REPAIR_HARDENING.md | 594 +++++++++ 4 files changed, 1874 insertions(+), 2 deletions(-) create mode 100644 deltas/0.3.14/pre.001.md create mode 100644 docs/plans/035-V0_3_14_MULTI_SOURCE_GAP_REPAIR_HARDENING_PLAN.md create mode 100644 docs/validation/031-V0_3_14_MULTI_SOURCE_GAP_REPAIR_HARDENING.md diff --git a/Cargo.toml b/Cargo.toml index e8be44e..8c817c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ # file: Cargo.toml -# version: 565 +# version: 566 [workspace] resolver = "3" members = ["crates/ksp-app-backfill-desk", "crates/ksp-app-config-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.13" +version = "0.3.14-pre.1" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" diff --git a/deltas/0.3.14/pre.001.md b/deltas/0.3.14/pre.001.md new file mode 100644 index 0000000..cc87f45 --- /dev/null +++ b/deltas/0.3.14/pre.001.md @@ -0,0 +1,218 @@ + + + +# Delta `0.3.14-pre.001` — audit / brainstorming / sizing gap repair + +## Base requise + +```text +v0.3.13 +workspace.package.version = 0.3.13 +deltas/0.3.13/rel.001.md présent +``` + +Archive opérateur auditée : + +```text +khadhroony-solana-project-v0.3.13.zip +SHA-256 253fa7ef5e0a6b2721da66fc3ea8dc7695d991d3a00bcc7d34396d9a32b65245 +``` + +L'archive ne contient pas `.git`; l'identité interne stable est contrôlée mais l'objet du tag Git ne peut pas être comparé localement. + +## Objet + +Exécuter le gate `pre.001` imposé par `prompts/033-V0_3_14_START_PROMPT.md` avant tout codage de repair : + +```text +lecture complète des règles et du handoff 0.3.13 +audit archive/base stable +audit Worker/Transport/frontier/replay/continuity +audit externe courant Solana/Yellowstone/providers +matrice des cinq familles live +modèle gap/range/coverage run-local +sémantique repaired/unresolved +health candidate +threat model +smokes accessibles +Cargo/features cible +sizing et décision maintien/rescission +plan 035 + validation 031 +``` + +Aucun moteur de repair, failover/degraded runtime, modification Transport/Config ou adapter EARLY n'est implémenté. + +## Décisions + +```text +reconnect != replay_attempt != replay_covered != repaired +replay accepté ne prouve pas la coverage +transaction-only source ne prouve pas l'absence d'événements manquants +repair final qualifié au niveau slot/range +skipped seulement après discovery slots réussie +getBlock null sur slot produit reste unresolved +getTransaction null n'est pas une preuve d'inexistence +Common RAW/admission/ksp-store-lib restent le chemin unique +retry/reconnect restent Transport-owned +required/redundant est une relation de coverage dynamique, pas un flag provider +processing frontier != continuity frontier +pool HTTP d'hydration != capability de scan : getTransaction seul est garanti pour Yellowstone/Logs/Helius +les façades WS doivent exposer une observation latest-value de leur snapshot avant le repair ; aucun nouveau socket/actor +range gap inclusif pour ne pas perdre la fin d'un slot déjà partiellement observé ; aucun slot dérivé d'un timestamp +TargetCoverage = réunion conservative des scopes configurés ; source perdue non respawnée par Worker +FullLedgerTransactions et ExactSourceScope sont distingués ; aucune équivalence cross-family implicite +getBlocksWithLimit ne ferme une plage que si la fenêtre cible est explicitement prouvée complète +scan full-block interdit s'il élargirait silencieusement un run uniquement filtré +EARLY hors scope 0.3.14 +aucune nouvelle dépendance +Config inchangé en pre.001 ; modification future seulement sur besoin démontré +0.3.14 maintenue sans rescission sous le scope des cinq familles existantes +``` + +Forecast recalibré : `pre.002 -> pre.016`, avec une tranche WS continuity dédiée, replay Yellowstone et coverage redondante séparés, puis HTTP/hydration/reconciliation/health/fairness/observability/races/gates/docs/publication. Cette extension remplace le forecast initial `pre.002 -> pre.014` afin de respecter les slices 15–20 minutes. + +Bornes cibles à matérialiser dans les tranches techniques : + +```text +MAX_OPEN_REPAIR_GAPS = 64 +MAX_REPAIR_RANGE_SLOTS = 4096 +MAX_REPAIR_DISCOVERY_WINDOW_SLOTS = 512 +MAX_REPAIR_BLOCK_FETCH_IN_FLIGHT = 4 +MAX_REPAIR_ACTIVE_GAPS = 1 +``` + +## Audit externe — points structurants + +Le proto Yellowstone utilisé expose `from_slot` et `SubscribeReplayInfo/first_available`. `yellowstone-grpc-proto 12.7.0` est courant au moment de l'audit. + +Le changelog upstream du 22 juillet 2026 documente un bug corrigé où un replay `blocks` pouvait accepter `from_slot`, ne rejouer aucun block, puis reprendre au head live avec un gap. La preuve KSP ne peut donc pas être « request accepted ». + +PublicNode expose actuellement Solana Mainnet/Testnet RPC/WS/Yellowstone et archive data, mais sa profondeur de replay/first_available n'est pas suffisamment documentée pour être revendiquée. + +OrbitFlare expose Yellowstone et Devnet gRPC sur ses tiers courants ; un exemple récent utilise `from_slot`, mais la profondeur/first_available de l'endpoint KSP reste à prouver par smoke. + +Helius documente LaserStream gRPC avec replay jusqu'à 24 h, mais cette surface est distincte du `transactionSubscribe` WSS stable et n'est pas ajoutée à `0.3.14`. + +QuickNode documente un `fromSlot` provider-specific jusqu'à 3000 slots ; cette comparaison confirme qu'une profondeur de replay ne peut pas être généralisée entre providers. + +## Fichiers ajoutés + +```text +docs/plans/035-V0_3_14_MULTI_SOURCE_GAP_REPAIR_HARDENING_PLAN.md +docs/validation/031-V0_3_14_MULTI_SOURCE_GAP_REPAIR_HARDENING.md +deltas/0.3.14/pre.001.md +``` + +## Fichiers modifiés + +```text +Cargo.toml +``` + +## Fichiers supprimés + +```text +aucun +``` + +## Version Cargo + +```text +header : 565 -> 566 +workspace.package.version : 0.3.13 -> 0.3.14-pre.1 +``` + +Aucune dépendance, feature ou autre ligne fonctionnelle du manifest racine n'est changée. + +## Surfaces explicitement inchangées + +```text +crates/** +config/** +.env.example +README.md +RULES.md +ROADMAP.md +CHANGELOG.md +docs/architecture/** +prompts/** +deltas/0.3.13/** +``` + +## Validations base exécutées + +Localement avant modification : + +```text +unzip -t : PASS +archive safety : PASS +workspace/crate inventory : 21/21 +General Rust rule audit : clean +Rust export completeness audit : 0 candidate(s) +KSP workspace Rust rule audit : clean +Markdown table audit : clean (340 tables / 855 files) +489 définitions de règles / 489 IDs uniques / 0 doublon +``` + +Le journal opérateur fourni pour la stable contient : + +```text +cargo fmt/check : terminé sans erreur visible +cargo check --workspace : terminé sans erreur visible +cargo clippy --workspace --all-targets --all-features -- -D warnings : terminé sans erreur visible +cargo test --workspace --all-targets --all-features : 1 850 PASS / 0 failed / 15 ignored +cargo tree Worker direct/features : produit +cargo tree --duplicates : produit +``` + +Cette preuve concerne `0.3.13`, pas le bump `0.3.14-pre.1`. + +## Validations post-modification + +```text +General Rust rule audit : clean +Rust export completeness audit : 0 candidate(s) +KSP workspace Rust rule audit : clean +Markdown table audit : clean (340 tables / 858 files) +rule definitions : 489 / 489 uniques / 0 doublon +comparaison stable -> worktree : 3 ajouts / 1 modification / 0 suppression +Cargo fmt/check : NON EXÉCUTÉ LOCAL — cargo absent du sandbox +``` + +Le `scripts/__pycache__` recréé par les audits Python est supprimé avant packaging et ne fait pas partie de la livraison. + +Archive de livraison attendue : + +```text +ksp-general-0.3.14-pre.001.zip +``` + +## Archive d’échange + +Conformément à `VER-ARCHIVE-001`, `VER-ARCHIVE-004` et `VER-ARCHIVE-005`, la livraison est une archive `general` minimale : + +```text +ksp-general-0.3.14-pre.001.zip +``` + +Contenu attendu, exactement quatre fichiers : + +```text +Cargo.toml +docs/plans/035-V0_3_14_MULTI_SOURCE_GAP_REPAIR_HARDENING_PLAN.md +docs/validation/031-V0_3_14_MULTI_SOURCE_GAP_REPAIR_HARDENING.md +deltas/0.3.14/pre.001.md +``` + +Aucun fichier inchangé de la stable, cache, lockfile, secret, sortie de compilation ou artefact d’audit n’est inclus. + +## Gate opérateur 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/0.3.14 +cargo check --workspace +``` + +Si ce gate est propre, `pre.002` peut matérialiser uniquement les contrats gap/range/coverage prévus par le plan `035`. diff --git a/docs/plans/035-V0_3_14_MULTI_SOURCE_GAP_REPAIR_HARDENING_PLAN.md b/docs/plans/035-V0_3_14_MULTI_SOURCE_GAP_REPAIR_HARDENING_PLAN.md new file mode 100644 index 0000000..190efff --- /dev/null +++ b/docs/plans/035-V0_3_14_MULTI_SOURCE_GAP_REPAIR_HARDENING_PLAN.md @@ -0,0 +1,1060 @@ + + + +# Plan v0.3.14 — gap repair / hardening multi-source du Worker RawTransaction + +## 1. But de la version + +`0.3.14` ferme la continuité du Worker `RawTransaction` **à l'intérieur du run actif** sans le transformer en Job historique, sans déplacer le reconnect/retry hors de Transport et sans créer un second pipeline de persistence. + +Le chemin durable reste : + +```text +source live + -> signal ou matériau source/protocole + -> hydration si nécessaire + -> ksp-raw-transaction-lib + -> RawTransaction + RawTransactionObservation + -> admission centrale Worker + -> ksp-store-lib +``` + +Le repair s'ajoute autour de ce chemin : + +```text +preuve de discontinuité du run + -> intervalle run-local borné + -> replay Transport si adressable + -> contribution de sources redondantes si leur coverage est prouvable + -> découverte HTTP des slots produits/skipped + -> getBlock/getTransaction uniquement pour le matériau encore manquant + -> même Common RAW / admission / Store + -> repaired ou unresolved explicite +``` + +Aucune campagne historique arbitraire n'entre dans ce Worker. `ksp-job-backfill-lib` reste le producteur historique indépendant. + +## 2. Base autoritaire auditée en pre.001 + +```text +archive : khadhroony-solana-project-v0.3.13.zip +SHA-256 : 253fa7ef5e0a6b2721da66fc3ea8dc7695d991d3a00bcc7d34396d9a32b65245 +ZIP bytes : 8431253 +ZIP entries : 2012 +ZIP files : 1818 +workspace members : 21 +crate manifests : 21 +workspace.package.version : 0.3.13 +stable delta : deltas/0.3.13/rel.001.md +prompt : prompts/033-V0_3_14_START_PROMPT.md +``` + +L'archive fournie ne contient pas `.git`. Son identité interne stable `0.3.13`, son delta `rel.001` et son contenu peuvent être contrôlés ; l'égalité cryptographique avec le tag Git `v0.3.13` ne peut pas être prouvée depuis cette archive seule. + +## 3. Contrôle d'intégrité de l'archive + +```text +unzip -t : PASS +entrée absolue : 0 +path traversal : 0 +doublon d'entrée ZIP : 0 +symlink ZIP : 0 +répertoire target : 0 +répertoire node_modules : 0 +répertoire dist : 0 +répertoire .git : 0 +Cargo.lock : 0 +package-lock.json : 0 +pnpm-lock.yaml : 0 +yarn.lock : 0 +rust-toolchain.toml : 0 +.env : 0 +crate manifests : 21 +workspace members : 21 +workspace members sans crate : 0 +crates hors workspace : 0 +``` + +L'archive est donc exploitable comme base stable autoritaire de la tranche. + +## 4. Audit des règles actives + +Les documents normatifs demandés par le prompt ont été relus depuis `RULES.md` puis `docs/rules/`. + +Contraintes bloquantes retenues : + +```text +Rust 2024 +unsafe interdit +unwrap / expect / panic interdits selon les règles KSP +? interdit en production +retours explicites +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] +pas de pub mod +pub/pub(crate) partagés réexportés jusqu'à la crate root +accès intra-crate partagé via crate::Item +unit_tests/ pour le privé ; tests/ pour l'API/cross-layer +Config possède fichiers, profils, endpoints et secrets +Transport possède clients, retry, reconnect, resubscribe et replay natif +Worker ne dépend ni de Config ni de Job Backfill ni d'un backend Store physique +Worker persiste via ksp-store-lib seulement +aucun actor/socket/client Transport parallèle dans Worker +aucun provider SDK de contournement +une prerelease non-fix synchronise workspace.package.version +un fichier texte modifié incrémente son header de version +un delta publié n'est pas réécrit rétrospectivement +une commande non exécutée n'est jamais PASS +archive d'échange = delta minimal +``` + +Le scan des **définitions** normatives, et non de leurs simples références croisées, donne : + +```text +489 définitions +489 identifiants uniques +0 doublon de définition +``` + +Un scan conservateur de headers sur les textes historiques signale quelques deltas anciens publiés avant les règles actuelles ainsi qu'un fichier de font tiers/documentaire. Ces fichiers ne sont pas touchés : l'immuabilité des deltas publiés prime sur une normalisation rétroactive et l'audit officiel du workspace reste l'autorité mécanique de la règle. + +## 5. Preuve opérateur stable reçue + +Le journal opérateur fourni avec l'ouverture de `0.3.14` exécute sur `0.3.13` : + +```text +cargo clean +cargo fmt --all +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 +cargo test --workspace --all-targets --all-features +cargo tree -p ksp-worker-raw-transaction-ingest-lib --edges normal +cargo tree -p ksp-worker-raw-transaction-ingest-lib -e features +cargo tree --duplicates +``` + +Les résultats enregistrés sont : + +```text +General Rust rule audit : clean +Rust export completeness audit : 0 candidate(s) +KSP workspace Rust rule audit : clean +Markdown table audit : clean, 340 tables / 855 files +cargo test : 1 850 PASS / 0 failed / 15 ignored +``` + +Cette preuve qualifie la stable d'entrée. Elle ne devient pas une exécution locale post-bump de `pre.001`. + +## 6. État stable à préserver + +`0.3.13` fournit déjà : + +```text +RawTransactionIngestRuntimeResources avec 1..32 sources logiques +invariant même réseau +identités de sources uniques +Yellowstone productif +Standard Logs -> getTransaction observed +Standard Block -> RAW-direct +Helius Transaction -> getTransaction observed +HTTP Block Polling -> RAW-direct +hydration globale coalescée Yellowstone / Logs / Helius +convergence (network, signature, commitment) avant hydration +sérialisation run-local de l'acquisition canonique (network, signature) +observations multiples +content conflict explicite +backpressure/fairness multi-source +processing frontier run-local +health source-neutral conservative +shutdown/fault/drain/abort+join bornés +``` + +Le graphe runtime direct du Worker stable reste : + +```text +ksp-core-lib +ksp-logging-lib +ksp-onchain-transport-lib +ksp-raw-transaction-lib +ksp-store-lib +ksp-worker-api +sha2 +tokio +``` + +Il ne contient ni `ksp-config-lib`, ni `ksp-job-backfill-lib`, ni `ksp-store-postgres-lib`, ni `reqwest`, ni `tonic`, ni `yellowstone-grpc-proto` comme dépendance Worker directe. + +## 7. Modèle interne actuel de continuité + +### 7.1 Yellowstone + +Transport expose déjà une session `Subscribe` bornée et une snapshot sûre : + +```text +state +reconnect_count +continuity_gap_count +replay_attempt_count +last_requested_from_slot +last_observed_slot +terminal_error_code +``` + +Le compteur `continuity_gap_count` n'est incrémenté que lorsque `SubscribeReplayInfo.first_available` prouve que le slot demandé est plus ancien que le premier slot retenu par l'endpoint. Cette preuve signifie **replay indisponible avant first_available** ; elle ne prouve pas qu'une transaction correspondant au filtre existait réellement dans cet intervalle. + +Transport avance `from_slot` au moins jusqu'au plus haut slot observé lors d'un reconnect. Le Worker observe ces compteurs mais, en `0.3.13`, toute hausse de `continuity_gap_count` devient immédiatement `source.continuity_gap_proven` terminal. + +### 7.2 WebSocket standard et Helius Transaction + +L'acteur WebSocket Transport possède reconnect/resubscribe. Son `WsSessionSnapshot` expose notamment : + +```text +state +continuity_gap_count +overflow_count +subscriptions +``` + +Une reconnexion physique incrémente conservativement `continuity_gap_count`. Le protocole WebSocket standard n'offre pas de `from_slot` natif ni de frontière de replay. La reprise du socket ne prouve donc jamais la couverture de l'intervalle perdu. + +Les façades `SolanaStandardWsSession` et `HeliusLaserStreamWsSession` exposent une `snapshot()` sûre, mais pas de `snapshot_source()` watch équivalent à Yellowstone. Le Worker stable ne surveille actuellement pas ces snapshots WS : ses compteurs agrégés de reconnect/gap sont donc alimentés par Yellowstone, pas par les sources WS. Pour détecter un incident WS indépendamment d'une notification transaction/bloc ultérieure, une petite surface Transport additive d'observation latest-value est retenue avant la logique de repair ; elle réutilise le `watch::Receiver` déjà possédé par l'acteur et ne crée aucun socket/actor/retry. + +### 7.3 HTTP Block Polling + +La source stable possède déjà une borne initiale de run et utilise : + +```text +getSlot +getBlocksWithLimit +getBlock observed +``` + +Les slots absents d'une réponse réussie `getBlocksWithLimit` sont déjà traités comme skipped/non produits. Un slot explicitement listé dont `getBlock` renvoie `null` n'est pas franchi silencieusement. + +### 7.4 Capabilities HTTP réellement garanties + +La présence d'un `HttpTransportPool` dans une source reference-bearing ne constitue **pas** une capability de scan. Les constructeurs Yellowstone, Standard Logs et Helius Transaction valident uniquement une route same-network compatible `getTransaction`. Les fixtures unitaires stables construisent d'ailleurs des rôles qui n'annoncent que `get_transaction`. + +À l'inverse, `RawTransactionIngestHttpBlockPollingSource` valide explicitement `getSlot` + `getBlocksWithLimit` + `getBlock`. `RawTransactionIngestStandardBlockSource` ne possède aucun pool HTTP. Les profils Config committés utilisent souvent des rôles wildcard, mais cette propriété de configuration n'est pas un invariant de l'API Worker. + +Conséquence : `0.3.14` doit construire un inventaire **run-wide et conditionnel** des capabilities de repair à partir des snapshots sûrs des pools/rôles déjà fournis. Une source qui possède seulement `getTransaction` conserve uniquement l'hydration de références connues ; elle ne gagne pas implicitement un scan de slots/blocs. + +### 7.5 Processing frontier + +Le `processing_frontier_slot` existant décrit le plus haut slot **réellement observé** qui n'est pas bloqué par du travail source plus ancien encore pending. Il n'est ni une preuve de coverage réseau, ni un checkpoint durable, ni une preuve d'absence de gap. + +`0.3.14` ne doit pas réutiliser ce nom pour une sémantique différente. La continuité réparée reçoit une projection/ledger distincte, puis la snapshot agrège les deux notions. + +## 8. Audit externe courant du 11 septembre 2026 + +### 8.1 Solana HTTP + +Les références RPC courantes confirment : + +```text +getSlot -> slot au commitment demandé +getBlocks -> blocs confirmés sur une plage, plage maximale RPC 500 000 slots +getBlocksWithLimit -> blocs confirmés depuis un start slot, limite RPC 500 000 +getBlock -> bloc confirmé ou null +getTransaction -> transaction confirmée par signature ou null +``` + +Sources primaires : + +```text +https://solana.com/docs/rpc/http/getslot +https://solana.com/docs/rpc/http/getblocks +https://solana.com/docs/rpc/http/getblockswithlimit +https://solana.com/docs/rpc/http/getblock +https://solana.com/docs/rpc/http/gettransaction +``` + +Les plafonds RPC de `500 000` ne deviennent pas des bornes Worker. Le repair KSP reste beaucoup plus petit et borné au run actif. + +La documentation anglaise courante type `maxSupportedTransactionVersion` comme nombre, tandis que certaines pages localisées affichent encore `0` comme seule valeur documentée et leurs exemples utilisent `0`. La stable KSP garde son contrat Legacy/V0/V1 acquis ; `0.3.14` n'élargit ni ne réduit ce contrat sur la seule base de cette divergence documentaire. Les smokes qualifiant une version `1` provider restent une preuve distincte à obtenir lorsqu'un endpoint approprié est disponible. + +### 8.2 Solana WebSocket + +`logsSubscribe` confirme les filtres : + +```text +all +allWithVotes +mentions avec exactement un pubkey +``` + +La notification transporte slot/signature/err/logs, pas un RAW complet. + +`blockSubscribe` reste explicitement instable et nécessite l'activation validator correspondante. Il peut livrer des blocs full, mais son protocole ne fournit pas de replay historique standard après reconnexion. + +Sources primaires : + +```text +https://solana.com/docs/rpc/websocket/logssubscribe +https://solana.com/docs/rpc/websocket/blocksubscribe +``` + +### 8.3 Yellowstone upstream et crate réellement utilisée + +Le proto courant expose : + +```text +Subscribe +SubscribeDeshred +SubscribeReplayInfo +SubscribeRequest.from_slot +SubscribeReplayInfoResponse.first_available +``` + +KSP dépend de : + +```text +yellowstone-grpc-proto = ^12.7 +``` + +`12.7.0`, publié le 29 août 2026, est la version courante visible sur docs.rs au moment de l'audit. Aucun bump de dépendance n'est requis par `pre.001`. + +Sources : + +```text +https://docs.rs/crate/yellowstone-grpc-proto/12.7.0 +https://github.com/rpcpool/yellowstone-grpc/blob/master/yellowstone-grpc-proto/proto/geyser.proto +https://github.com/rpcpool/yellowstone-grpc/blob/master/CHANGELOG.md +``` + +Le changelog upstream est bloquant pour le modèle de preuve : `yellowstone-grpc-geyser 14.2.1` du 22 juillet 2026 a corrigé un défaut où un abonnement `blocks` avec `from_slot` pouvait accepter le replay puis ne livrer aucun `Message::Block`, avant de reprendre au head live avec un gap silencieux. Le protocole et l'acceptation d'un `from_slot` ne suffisent donc pas à prouver que le matériau demandé a été rejoué. + +Conclusion : KSP doit séparer strictement `replay_attempt` de `replay_covered` et vérifier le coverage par le contenu/les slots effectivement réconciliés. + +### 8.4 PublicNode + +La surface publique courante annonce pour Solana : + +```text +Mainnet RPC +Mainnet WS RPC +Mainnet Yellowstone gRPC +Testnet RPC +Testnet WS RPC +Testnet Yellowstone gRPC +archive data available +``` + +Source : + +```text +https://solana.publicnode.com/ +``` + +La profondeur exacte de replay Yellowstone, la sémantique `first_available` et l'auth gRPC exploitable par le profil KSP ne sont pas contractualisées publiquement de façon suffisante dans les sources trouvées. Elles restent **NON PROUVÉES** jusqu'à smoke provider réel. Le texte « archive data available » ne doit pas être converti en profondeur de replay live. + +### 8.5 OrbitFlare + +La documentation et le pricing courants distinguent Yellowstone et Jetstream. Yellowstone est présenté comme full data fidelity ; le pricing courant annonce Devnet gRPC sur les tiers Free/Developer et du gRPC partagé à partir de 500 USD/mois pour l'accès complet. La documentation Yellowstone courante expose les familles Accounts/Transactions/Slots/Blocks/Entries, mais l'exemple de requête principal ne documente pas explicitement `from_slot` ni `SubscribeReplayInfo`. + +Une publication OrbitFlare récente montre toutefois un indexer qui reprend depuis `MAX(slot)` via `from_slot`. Cela rend le mécanisme candidat crédible, mais ne prouve ni la profondeur de rétention, ni `first_available`, ni le comportement exact du serveur provisionné pour KSP. + +Sources : + +```text +https://docs.orbitflare.com/data-streaming/yellowstone +https://orbitflare.com/products/solana-grpc +https://orbitflare.com/pricing +https://orbitflare.com/blog/developers/build-grpc-indexer +``` + +Conclusion : le profil OrbitFlare Devnet existant peut servir à un smoke de replay si le credential opérateur est disponible, mais aucun `PASS` de replay n'est revendiqué en `pre.001`. + +### 8.6 Helius + +La matrice courante Helius indique : + +```text +LaserStream WSS standard : disponible sur les tiers courants +WSS transactionSubscribe : Developer et plus +LaserStream gRPC Devnet : Developer et plus +LaserStream gRPC Mainnet : Business et plus +LaserStream gRPC : replay historique annoncé jusqu'à 24 h +``` + +Sources : + +```text +https://www.helius.dev/pricing +https://www.helius.dev/blog/laserstream-websockets +https://www.helius.dev/laserstream +``` + +Le Worker stable possède déjà Helius `transactionSubscribe`, mais pas une source LaserStream gRPC Helius configurée comme nouveau provider de repair. Ajouter cette surface augmenterait Config, Transport/provider validation et smokes. Ce n'est pas nécessaire pour fermer le repair des cinq familles existantes ; elle reste donc hors implémentation `0.3.14` sauf réouverture explicite future. + +### 8.7 QuickNode, comparaison seulement + +La documentation QuickNode mise à jour le 3 septembre 2026 documente `fromSlot` jusqu'à `3000` slots récents, environ `20` minutes dans son estimation, avec Solana gRPC disponible selon plan/add-on. + +Source : + +```text +https://www.quicknode.com/guides/solana-development/tooling/solana-grpc/solana-grpc +``` + +QuickNode n'est pas un provider configuré de la stable. Cette donnée démontre que la profondeur de replay est provider-specific ; elle ne justifie pas l'ajout de QuickNode dans `0.3.14`. + +## 9. Distinction formelle des états de continuité + +Les termes suivants deviennent non interchangeables : + +```text +reconnect + Transport a remplacé/rétabli une session physique. + Aucune coverage n'est impliquée. + +replay_attempt + Transport a demandé une reprise depuis un slot. + Aucune delivery complète n'est impliquée. + +replay_covered + le matériau attendu sur l'intervalle est démontré par une preuve de coverage KSP. + L'acceptation d'un from_slot ne suffit pas. + +repair + KSP a utilisé un ou plusieurs mécanismes pour réconcilier un gap run-local via le pipeline normal. + +repaired + toutes les obligations de coverage du gap sont fermées et le matériau requis est durable/idempotent. + +unresolved_gap + au moins une obligation reste non prouvée après les mécanismes bornés autorisés. +``` + +Une reprise live après reconnect ne change jamais directement `unresolved_gap` en `repaired`. + +## 10. Modèle cible minimal du gap run-local + +### 10.1 Identité privée + +Le ledger privé doit distinguer au minimum : + +```text +gap_id run-local monotone +source_key affectée +network +commitment +start_slot inclusif +end_slot inclusif +coverage_requirement +reason +state +``` + +`gap_id` n'est pas durable et n'est jamais réutilisé pendant le même run. + +Le `source_key` reste privé. Le consumer public n'a pas besoin d'un endpoint/provider pour comprendre qu'un intervalle du run est non réconcilié. + +### 10.2 Origines admises d'un gap + +```text +Yellowstone retention gap prouvé par first_available +reconnect WebSocket avec intervalle source bornable +source failure après qu'un frontier source a été observé +perte/overflow explicitement signalé par Transport +incohérence de scan HTTP sur un slot produit explicitement listé +``` + +Un démarrage sans événement antérieur ne crée pas artificiellement une campagne « depuis le début du réseau ». Si aucune borne de run sûre n'existe, le mécanisme conserve la terminalité conservative au lieu d'inventer un historique. + +### 10.3 Bornes cibles + +Bornes initiales à matérialiser avec tests adversariaux : + +```text +MAX_OPEN_REPAIR_GAPS = 64 +MAX_REPAIR_RANGE_SLOTS = 4096 +MAX_REPAIR_DISCOVERY_WINDOW_SLOTS = 512 +MAX_REPAIR_BLOCK_FETCH_IN_FLIGHT = 4 +MAX_REPAIR_ACTIVE_GAPS = 1 +``` + +Ces limites sont des bornes KSP, indépendantes des maxima beaucoup plus larges des RPC Solana. + +Aucun retry loop Worker n'est ajouté autour d'une requête Transport. Les retries réseau restent Transport-owned. Un résultat applicatif `null`, une rétention insuffisante ou un slot explicitement indisponible sont des résultats de réconciliation, pas des prétextes à une boucle de retry cachée. + +### 10.4 Construction sûre des bornes + +Les ranges sont inclusifs. Le début ne doit pas être artificiellement avancé à `last_observed_slot + 1` : une déconnexion peut perdre des transactions **dans le même slot** que la dernière transaction déjà observée, et Yellowstone rejoue justement depuis le slot observé afin que l'idempotence absorbe les doublons. + +Ordre conservateur des ancres : + +```text +Yellowstone + start = last_requested_from_slot / last_observed_slot pertinent, inclusif + retention gap = [requested_from_slot, first_available - 1] lorsque représentable + end = première progression post-reconnect ou borne de coverage indépendante + +WebSocket transaction/block + start = dernier slot source observé, inclusif + end = premier slot post-incident observé, ou getSlot same-network capturé après incident, ou frontier d'une source coverage-capable + +source sans slot observé avant incident + utiliser seulement une borne de début de run déjà prouvée par une capability same-network ; sinon le gap n'est pas bornable et reste terminal/unresolved +``` + +Une source filtrée très sparse peut produire une sur-approximation importante entre ses deux événements encadrants. Cette sur-approximation est sûre mais ne peut pas dépasser `MAX_REPAIR_RANGE_SLOTS` : au-delà, KSP exige replay/coverage redondante plus forte ou termine, au lieu de scanner un historique arbitraire. Les timestamps muraux ne deviennent jamais des slots. + +## 11. Coverage requirement et capabilities + +### 11.1 Principe + +Une source redondante n'est pas déclarée « équivalente » par son provider, son protocole ou son activité. Elle ne ferme un gap que si KSP peut démontrer qu'elle couvre le même univers transactionnel requis, ou un superset explicite, pour l'intervalle et le commitment concernés. + +### 11.2 Classes minimales + +```text +reference-bearing + la source peut fournir des signatures/slots à hydrater. + Elle contribue au repair mais ne prouve pas à elle seule qu'aucune référence n'a été perdue. + +block-material + la source fournit un bloc et les transactions correspondantes. + Elle peut fermer le matériau d'un slot produit lorsque le filtre est compatible. + +slot-enumerating + la source peut prouver quels slots de la plage possèdent un bloc confirmé. + getBlocks/getBlocksWithLimit est la référence initiale. + +coverage-capable + combinaison suffisante pour classifier chaque slot de la plage comme produit+matérialisé ou skipped/non produit. +``` + +La preuve finale est construite au niveau **slot/range**, pas au niveau « socket connecté ». + +### 11.3 Scope d'acquisition et relation de coverage + +Le repair ferme la coverage des **entités `RawTransaction` autorisées par la composition du run**. Il ne cherche pas à fabriquer rétrospectivement chaque observation provider manquante. Une observation d'une source perdue n'est jamais inventée. + +Le modèle initial est volontairement conservateur : + +```text +FullLedgerTransactions + scope actuellement prouvé pour HTTP Block Polling et Standard Block avec filtre All. + une coverage continue de ce scope est un superset de tout scope transactionnel plus étroit du même network/commitment. + +ExactSourceScope + scope opaque mais comparable au sein d'une même famille par la sémantique/empreinte privée déjà possédée. + aucune égalité cross-family n'est déduite d'un hash ou d'un nom de provider. + +KnownReferences + ensemble de signatures/slots déjà observés. + utile pour hydration, mais insuffisant pour prouver qu'aucune notification inconnue n'a été perdue. +``` + +La cible requise du run est la **réunion conservative des scopes configurés** (`TargetCoverage`). Les sources ne deviennent donc ni « primary » ni « optional » : ce sont les scopes de coverage qui sont requis. Une source est redondante uniquement lorsque sa perte ne réduit pas `TargetCoverage` grâce à une autre source/capability qui couvre le même scope ou un superset prouvé, au même network et au même commitment. Aucun ordre implicite `Confirmed`/`Finalized` n'est utilisé comme relation de superset en `0.3.14`. + +Une source `FullLedgerTransactions` restée couverte sur l'intervalle peut donc prouver que les entités RAW d'une source filtrée perdue n'ont pas créé de trou canonique. Le Worker reste néanmoins `Degraded` tant que cette source attendue est absente ; il ne crée pas les observations qu'elle aurait éventuellement produites. + +Pour deux sources filtrées, l'équivalence n'est admise que si `pre.003` matérialise une relation de scope exacte/superset et une **preuve d'intervalle continu**. Deux filtres identiques et deux sockets `Active` ne suffisent pas, à eux seuls, à dater les bornes du gap. Une source transaction-only sans marqueur de progression de chaîne ne ferme donc jamais une plage uniquement par silence. + +Le scan HTTP de blocs complets ne doit pas élargir silencieusement le scope choisi par l'opérateur. Il peut matérialiser toutes les transactions d'une plage seulement si la composition autorise déjà `FullLedgerTransactions`, ou si une future règle de matching de filtre est explicitement implémentée et testée. Aucun matcher provider-générique n'est supposé en `pre.001`. + +## 12. Matrice des cinq familles live + +### 12.1 Yellowstone + +```text +détection gap : OUI via snapshot Transport et frontier source +replay natif : OUI si provider accepte from_slot +first_available : PROTO OUI ; provider à qualifier +preuve replay covered : NON par replay_attempt seul +hydration : OUI via getTransaction observed pour Transaction/TransactionStatus +bloc direct : OUI pour Block qualifié +scan HTTP : CONDITIONNEL — le pool existe, mais le constructeur ne garantit que getTransaction ; le rôle doit aussi prouver les méthodes de scan +reprise : OUI après réconciliation +``` + +Un filtre Yellowstone transaction-only reste `reference-bearing`. Un block filter complet peut fournir du matériau de bloc, mais la preuve skipped/produced de l'intervalle reste séparée tant qu'aucun marqueur complet provider n'est qualifié. + +### 12.2 Standard Logs + +```text +détection gap : OUI via reconnect/gap Transport + frontier source +replay natif : NON +preuve coverage seule : NON +hydration : OUI via getTransaction observed +scan HTTP : CONDITIONNEL — le pool/role d'hydration garantit seulement getTransaction ; les méthodes de scan doivent être prouvées séparément +reprise : OUI après repair +``` + +### 12.3 Standard Block + +```text +détection gap : OUI via reconnect/gap Transport + frontier source +replay natif : NON +bloc direct : OUI +preuve coverage : seulement si l'intervalle complet est prouvé par une autre capability +hydration : NON nécessaire pour le chemin direct normal +scan HTTP : aucun pool attaché à la source ; possible seulement via une capability run-wide fournie par une autre source et un scope compatible +reprise : dépend d'une source/capability redondante ou conserve la terminalité 0.3.13 +``` + +Un Worker composé uniquement d'une Standard Block source n'acquiert pas magiquement un client HTTP de repair. `0.3.14` ne contourne pas cette absence. + +### 12.4 Helius Transaction + +```text +détection gap : OUI via reconnect/gap Transport + frontier source +replay WSS natif : NON prouvé +preuve coverage seule : NON +hydration : OUI via getTransaction observed +scan HTTP : CONDITIONNEL — le pool/role d'hydration garantit seulement getTransaction ; les méthodes de scan doivent être prouvées séparément +reprise : OUI après repair +``` + +Le replay LaserStream gRPC 24 h est une autre surface Helius ; il n'est pas attribué à `transactionSubscribe` WSS. + +### 12.5 HTTP Block Polling + +```text +détection gap : OUI via progression de scanner et erreurs explicites +replay natif : sans objet +preuve slots produits/skipped : OUI lorsque la fenêtre de discovery est explicitement prouvée complète +bloc direct : OUI via getBlock observed +hydration : non nécessaire pour un bloc complet +scan repair : OUI via getSlot + getBlocksWithLimit + getBlock déjà validés ; getBlocks exact est utilisable seulement si le rôle le supporte +reprise : OUI après reconciliation +``` + +Cette famille fournit le modèle de preuve de range le plus déterministe pour la première version du repair. + +## 13. Algorithme borné de repair + +Pour un gap run-local borné : + +```text +1. enregistrer le gap et son coverage_requirement sans arrêter les autres sources ; +2. dériver les capabilities run-wide réellement disponibles sans supposer qu'un pool d'hydration sait scanner ; +3. tenter le replay natif Transport si la source possède déjà cette capability ; +4. accepter le matériau replayé comme candidat, puis prouver séparément la couverture de l'intervalle ; +5. utiliser une source redondante uniquement si scope exact/superset + intervalle continu sont démontrés ; +6. si un repair HTTP est autorisé par le scope, préférer getBlocks(start,end) pour une fenêtre fermée lorsque le rôle le supporte ; +7. sinon, utiliser getBlocksWithLimit seulement avec un critère explicite prouvant que la réponse couvre toute la fenêtre cible ; une page arbitraire ne ferme jamais sa queue ; +8. classifier skipped uniquement à l'intérieur d'une fenêtre dont la discovery est prouvée complète ; +9. pour chaque slot produit non déjà couvert, récupérer getBlock observed ; +10. utiliser getTransaction observed pour les références connues, sans le considérer comme découverte des références inconnues ; +11. ne matérialiser un bloc complet hors filtre que si FullLedgerTransactions est déjà autorisé par la composition ; +12. faire passer tout matériau admis par Common RAW, l'admission centrale et ksp-store-lib ; +13. attendre les durable outcomes requises ; +14. marquer repaired uniquement lorsque le coverage_requirement entier est fermé ; +15. sinon conserver unresolved et appliquer la terminalité décidée sans effacer le gap. +``` + +Le Worker ne construit pas une requête historique caller-driven et n'accepte aucun `start_slot` métier externe pour ce repair. + +## 14. Sémantique skipped / missing / unavailable + +### 14.1 Skipped slot + +Un slot est `skipped/non-produced` uniquement lorsqu'une primitive de discovery qualifiée réussit sur une fenêtre **dont la fermeture est elle-même prouvée** et ne le liste pas. `getBlocks(start,end)` donne naturellement cette fenêtre fermée. `getBlocksWithLimit` compte des blocs, pas des slots : sa seule réussite ne prouve donc pas une queue arbitraire ; le repair doit démontrer qu'il a atteint/dépassé la borne cible ou appliquer un autre critère de complétude testé. + +L'absence de notification WebSocket ou gRPC ne suffit pas. + +### 14.2 Missing block + +Si un slot est explicitement listé comme produit puis `getBlock` renvoie `null`, le slot reste non réconcilié. Il n'est ni reclassé skipped ni franchi silencieusement. + +Transport reste propriétaire de ses retries réseau. Après les mécanismes logiques bornés du repair, ce cas produit `unresolved`. + +### 14.3 Missing transaction + +`getTransaction = null` ne prouve pas que la transaction n'a jamais existé. La référence reste non réconciliée, puis le repair peut privilégier le bloc du slot connu. Si le bloc complet qualifié confirme la transaction et permet la construction Common RAW, le gap peut progresser sans exiger une réponse `getTransaction` séparée. + +### 14.4 Provider retention + +`first_available > requested_from_slot` prouve une limite de rétention du replay de cet endpoint. Cela déclenche/fixe un gap de coverage ; cela ne prouve pas à lui seul la présence d'une transaction filtrée dans les slots perdus. + +## 15. Relation avec les frontiers + +Trois notions restent séparées : + +```text +source frontier + plus haut slot effectivement observé par une source logique. + +processing_frontier_slot + plus haut slot observé non bloqué par du travail déjà admis/pending. + +continuity/reconciled frontier + plus haut intervalle du run dont les gaps antérieurs sont tous repaired ou n'ont jamais existé. +``` + +Le `processing_frontier_slot` ne peut pas fermer un gap. Un repair plus ancien peut se terminer après l'ingestion de slots plus récents ; le ledger doit donc supporter cette réconciliation hors ordre sans faire avancer le continuity frontier au-delà du plus ancien gap ouvert. + +## 16. Politique required / redundant et health + +Aucune source configurée ne devient statiquement « optionnelle ». Toutes restent attendues. La redondance est une **relation de coverage au moment d'un incident**, pas un booléen de configuration provider. + +Politique cible : + +```text +Healthy + Worker Running, toutes les sources attendues actives, aucun gap ouvert, aucun repair pending. + +Degraded + Worker Running avec une source reconnecting/perdue, mais les autres sources actives couvrent encore TargetCoverage pour le présent ET le futur du run, et aucun gap unresolved n'existe. + +Unhealthy + Worker Running pendant une réconciliation bornée dont la coverage n'est pas encore prouvée, ou avec un gap pending non terminal encore traitable. + +Faulted + Worker terminal lorsque la plage est non représentable/trop grande, aucun mécanisme autorisé ne peut la couvrir, le repair s'épuise avec unresolved, ou la perte d'une source laisse un scope requis sans source live capable de le couvrir pour la suite du run, ou une faute terminale existante survient (content conflict, persistence, invariant, counter exhaustion, etc.). +``` + +Le retour à `Healthy` exige : + +```text +source revenue Active si elle est toujours attendue +aucun gap pending/unresolved +aucun repair en cours +coverage/reconciled frontier rattrapé +``` + +Un simple reconnect réussi ne suffit pas. Une source qui a épuisé le reconnect Transport n'est **jamais respawnée par le Worker** : cela recréerait une boucle de reconnexion concurrente. Le supervisor transforme d'abord cette terminaison en incident de coverage ; il peut continuer `Degraded` avec les sources restantes uniquement si `TargetCoverage` demeure assuré. Sinon le Worker termine, même si le segment historique du gap a pu être réparé, car la coverage future ne serait plus garantie. + +Cela implique une évolution explicite du supervisor stable : « première source Failed => stop de toutes » devient « source perdue => décision de coverage bornée => Degraded ou Faulted », sans masquer les erreurs de persistence/content/invariant qui restent terminales immédiatement. Les capabilities de repair nécessaires après la fin d'une tâche source doivent être clonées/extraite des ressources validées **avant** le spawn, sans exposer les clients au public. + +## 17. Backpressure et fairness + +Le repair ne crée ni second pipeline de persistence ni queue Store privilégiée. + +Cibles : + +```text +un seul gap activement réparé à la fois +maximum 4 getBlock logiques simultanés +pages de discovery <= 512 slots +matériaux repair réinjectés dans l'admission centrale existante +hydration repair partage le registre/coalescence globale existant +persistence partage les mêmes limites existantes +compteurs checked, jamais saturating pour masquer un overflow Worker +``` + +Le trafic live continue à être reçu tant que les queues bornées le permettent afin de ne pas créer un second gap par pause globale. Le scheduler de repair doit céder régulièrement au nominal ; la tranche fairness définira un burst borné plutôt qu'un nouveau pool illimité. + +Principe de fermeture : si la capacité existante est `1`, le mécanisme doit encore progresser sans réserver une capacité impossible. La fairness doit donc être temporelle/alternée et non dépendre d'un pourcentage fixe de workers. + +## 18. Observabilité publique cible + +`0.3.15` Desk est un consumer réel imminent. Une surface publique minimale est donc justifiée, mais elle reste source-neutral. + +Projection candidate : + +```text +RawTransactionIngestGapId +RawTransactionIngestGapState +RawTransactionIngestGapReason +RawTransactionIngestRepairMethod +RawTransactionIngestGapSnapshot +``` + +Champs publics utiles : + +```text +gap_id +start_slot +end_slot +state +reason +last_method éventuel +``` + +Agrégats snapshot : + +```text +open_gap_count +repairing_gap_count +repaired_gap_total +unresolved_gap_total +replay_repair_total +redundant_coverage_repair_total +http_scan_repair_total +repair_block_fetch_total +repair_transaction_hydration_total +oldest_open_gap_start_slot éventuel +``` + +Aucun endpoint URL, token, filter value, signature, transaction payload, provider error arbitraire ou source key privée n'est exposé. + +## 19. Transport, Config et dépendances + +### 19.1 Transport + +Le besoin fonctionnel principal est déjà présent : + +```text +Yellowstone Subscribe/from_slot/SubscribeReplayInfo +WebSocket reconnect snapshots +getSlot/getBlocks/getBlocksWithLimit +getBlock observed +getTransaction observed +``` + +Un manque précis est prouvé pour les sources WebSocket : les façades typées exposent `snapshot()` mais pas une source watch publique, alors que l'acteur possède déjà le `watch::Receiver`. `pre.003` doit ajouter une surface latest-value sûre équivalente à Yellowstone (ou une surface strictement équivalente) afin que le Worker observe `continuity_gap_count`/state sans polling réseau et sans attendre la prochaine notification métier. + +Aucune autre API Transport nouvelle n'est actuellement obligatoire : les méthodes RPC de scan/hydration sont déjà publiques. Si une preuve additionnelle manque lors de l'implémentation, elle doit rester additive, sûre et dans une tranche Transport dédiée, sans wire/provider brut. + +### 19.2 Config + +Aucune modification Config n'est retenue pour `pre.001`. Les profils committés pertinents utilisent actuellement des rôles HTTP wildcard capables de servir les méthodes de repair, mais le Worker ne doit jamais transformer ce constat de configuration en invariant d'API : un caller peut fournir un rôle limité à `getTransaction`. + +Le plan nominal commence donc par une détection de capability. Une modification Config ne sera ouverte que si une composition réellement supportée exige un rôle de repair explicite non exprimable avec les profils existants. Les tiers/prix/replay depths restent documentation d'audit et ne deviennent pas du runtime Config sans besoin concret. + +### 19.3 Dépendances + +Aucune nouvelle dépendance Worker n'est planifiée. `yellowstone-grpc-proto ^12.7` est courant et reste possédé par Transport/test surfaces existantes. + +Graphe Worker cible à la fin de `0.3.14` : inchangé par rapport à `0.3.13`. + +## 20. Décision EARLY + +EARLY est **hors scope confirmé pour `0.3.14`**. + +Motifs : + +```text +OrbitFlare Jetstream est une surface shred-level distincte de Yellowstone +la matrice provider courante indique payload minimal et absence de metadata/inner instructions par rapport à Yellowstone +Jetstream V2 est un protocole/provider supplémentaire apparu en 2026 +Helius Raw Shreds/Preconfirmations sont également des surfaces distinctes et payantes +aucune de ces surfaces n'est nécessaire pour réparer la continuité des cinq familles 0.3.13 +les introduire forcerait un audit Transport/Config/protocole, des credentials/tier gates et une tranche dédiée substantielle +``` + +Sources d'audit : + +```text +https://orbitflare.com/products/solana-grpc +https://orbitflare.com/blog/developers/jetstream-v2 +https://www.helius.dev/pricing +``` + +Aucune prerelease EARLY artificielle n'est ajoutée au forecast. + +## 21. Threat model `0.3.14` + +Les tests doivent couvrir au minimum : + +```text +reconnect storm créant des gaps adjacents/recouvrants +gap supérieur à la borne run-local +first_available qui tronque le replay +replay accepté mais ne livrant pas le matériau attendu +source redondante active mais stale +source redondante à filtre non équivalent +HTTP discovery avec skipped slots +slot listé puis getBlock null +getTransaction null puis réparation par bloc +duplicate replay + live du même canonical identity +deux sources réparant la même signature +content conflict pendant repair +backpressure admission pendant scan +Store lent pendant repair +hydratation déjà leader dans le registre global +source qui reconnecte pendant son gap précédent +arrêt pendant replay +arrêt pendant discovery HTTP +arrêt pendant getBlock/getTransaction +arrêt pendant admission/persistence +fault pendant drain +counter exhaustion +snapshot tardive après terminalité +aucune fuite provider/filter/signature dans Debug/error/snapshot +``` + +## 22. Smokes live réellement accessibles + +### 22.1 Keyless + +Les ressources publiques permettent raisonnablement : + +```text +Solana HTTP Devnet : getSlot/getBlocksWithLimit/getBlock/getTransaction selon matériau disponible +Solana WebSocket Devnet : connect/subscribe/unsubscribe existant +``` + +Un smoke Worker repair HTTP end-to-end peut être ajouté en ignored si un Store de test provisionné est disponible. Il ne sera PASS que s'il est réellement exécuté. + +### 22.2 Provider-gated + +```text +OrbitFlare Yellowstone Devnet replay/from_slot : credential requis ; NON EXÉCUTÉ en pre.001 +PublicNode Yellowstone Mainnet/Testnet : auth/replay à qualifier ; NON EXÉCUTÉ en pre.001 +Helius transactionSubscribe : Developer+ et API key ; NON EXÉCUTÉ en pre.001 +Helius LaserStream gRPC : hors surface retenue 0.3.14 ; aucun smoke requis +``` + +Aucun secret n'est placé dans un delta, un log persisté ou une commande de documentation. + +## 23. Sizing et décision de maintien + +Taille constatée des zones principales : + +```text +Worker src : 9 fichiers / 6 317 lignes +Worker tests : 5 fichiers / 2 725 lignes +Worker unit_tests : 6 fichiers / 5 523 lignes +Transport src : 33 fichiers / 22 764 lignes +``` + +Le scope serait trop large si `0.3.14` ajoutait EARLY, un nouveau provider, un nouveau SDK ou une campagne historique. Ces extensions sont exclues. + +Avec les cinq familles actuelles, le pipeline actuel et les primitives Transport déjà disponibles, le scope reste clôturable dans une session de release découpée en tranches de 15–20 minutes environ. Décision `pre.001` : + +```text +MAINTENIR 0.3.14 +NE PAS RESCINDER avant pre.002 +condition : aucune expansion EARLY/provider/historique +``` + +Si une tranche découvre une nécessité de refondre Transport ou d'introduire une capability provider non présente qui dépasse son budget, elle est subdivisée ou reportée ; elle ne grossit pas silencieusement. + +## 24. Forecast recalibré + +L'audit exact des façades WS, des rôles HTTP et du supervisor montre que le forecast initial `pre.002 -> pre.014` mélangeait plusieurs responsabilités susceptibles de dépasser le budget 15–20 minutes. Il est donc **allongé sans rescinder la release** : + +### pre.001 — audit / brainstorming / sizing + +Livraison actuelle : base, règles, audit interne/externe, matrice des cinq familles, modèle de preuve, threat model, health candidate, smokes, dépendances et décision de maintien. Aucun moteur de repair. + +### pre.002 — contrats gap / range / TargetCoverage + +Matérialiser les types run-local, états, raisons, bornes inclusives, scopes (`FullLedgerTransactions` / exact / known references), inventaire privé de capabilities et ledger. Tests de validation, overflow, overlap/adjacency et absence de broadening. Aucun I/O de repair. + +Budget cible : une tranche. + +### pre.003 — observabilité de continuité WebSocket + +Ajouter dans Transport une source latest-value sûre pour les snapshots WS typés, puis la consommer dans Standard Logs/Standard Block/Helius sans nouveau socket ni retry. Matérialiser les ancres d'incident et les tests reconnect/overflow. Aucun repair HTTP. + +Budget cible : une tranche Transport + intégration Worker courte ; si elle dépasse, ouvrir un fix/tranche dédiée avant replay. + +### pre.004 — preuve de replay natif Yellowstone + +Brancher les preuves Transport existantes, distinguer replay attempt/delivery/coverage, couvrir la rétention `first_available` et le cas replay accepté mais incomplet. Le Worker n'écrit jamais `from_slot`. + +Budget cible : une tranche. + +### pre.005 — coverage redondante + +Matérialiser `TargetCoverage`, les relations exact/superset conservatrices et les epochs/ranges de coverage. Autoriser seulement les redondances démontrées ; aucune équivalence cross-family opportuniste. + +Budget cible : une tranche. + +### pre.006 — scan HTTP de réparation + +Détecter les capabilities run-wide réellement disponibles. Réparer une plage autorisée avec fenêtres <= `MAX_REPAIR_DISCOVERY_WINDOW_SLOTS`, `getBlocks(start,end)` lorsqu'il est supporté ou un `getBlocksWithLimit` dont la fermeture de fenêtre est explicitement prouvée, puis `getBlock observed`. Aucun broadening de scope. + +Budget cible : une tranche. + +### pre.007 — hydration de réparation + +Réutiliser `getTransaction observed` et la coalescence globale pour les références connues ; conserver `Missing` comme obligation non fermée et préférer le bloc du slot lorsque cela est autorisé. Aucun second registre d'hydration. + +Budget cible : une tranche. + +### pre.008 — réconciliation et supervisor source-loss + +Unifier replay/redondance/HTTP/hydration dans le ledger, calculer le continuity frontier et remplacer la terminalité « first source failure » uniquement par la décision `TargetCoverage` explicitement prouvée. Aucun respawn Worker d'une source Transport. + +Budget cible : une tranche. + +### pre.009 — health policy multi-source + +Appliquer Healthy/Degraded/Unhealthy/Faulted selon coverage présente et future, avec retour à Healthy seulement après source active + gaps fermés. + +Budget cible : une tranche. + +### pre.010 — backpressure et fairness pendant repair + +Partager admission/hydration/persistence, limiter le fanout HTTP, assurer l'alternance nominal/repair et tester les faibles capacités dont `1`. + +Budget cible : une tranche. + +### pre.011 — snapshots / observabilité + +Stabiliser les types publics gap/repair nécessaires à la Desk suivante et les compteurs checked, sans source/provider sensible. + +Budget cible : une tranche. + +### pre.012 — races / shutdown hardening + +Stop/fault pendant replay, coverage wait, discovery, hydration, admission et persistence ; drain/abort/join, suppression des late publications et invariants de terminalité. + +Budget cible : une tranche. + +### pre.013 — completeness/security cross-layer + +Canaris Worker/Transport/Common RAW/Store, dépendances, API publique, redaction, Legacy/V0/V1 et non-confusion Worker/Backfill. Aucun nouveau comportement. + +Budget cible : une tranche. + +### pre.014 — gate technique/live + +Workspace complet, Clippy strict, tests all-targets/all-features, graphes/duplicates et smokes réellement accessibles. Aucun nouveau scope. + +### pre.015 — réconciliation documentaire + +README/USAGE, plan, validation et architectures réellement affectées. Pas de CHANGELOG/ROADMAP/prompt suivant. + +### pre.016 — préparation publication + +Prompt `0.3.15`, CHANGELOG, ROADMAP et mécanique de version/delta seulement. + +### rel.001 + +Publication stable mécanique après gate validé. + +## 25. Critères de clôture + +`0.3.14` n'est clôturable que si : + +```text +les cinq familles 0.3.13 restent non régressées +un gap possède identité/plage/bornes run-local explicites +reconnect != replay_attempt != replay_covered != repaired +source redondante ne vaut coverage que par preuve structurale +les slots skipped ne sont jamais inventés depuis une absence de notification +le scan HTTP est borné et caller-non-paramétrable +getTransaction/getBlock repair restent Transport-owned +aucun retry réseau Worker parallèle +Common RAW/admission/Store restent uniques +content conflict reste terminal +processing frontier et continuity frontier restent distincts +unresolved gap reste visible +health ne revient pas Healthy sur un simple reconnect +repair et nominal restent fair/bornés +shutdown ne laisse aucune tâche/persistence tardive +aucun Worker -> Config/Job/backend Store +aucune nouvelle dépendance ou provider SDK inutile +EARLY reste hors release +smokes accessibles exécutés ou explicitement NON EXÉCUTÉ +gate workspace/clippy/tests/trees vert +documentation réconciliée +prompt 0.3.15 produit en pre.016 +``` + +## 26. Gate attendu après application de pre.001 + +`pre.001` ne modifie aucun Rust, dépendance, feature, runtime ou Config. Après application du delta : + +```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/0.3.14 +cargo check --workspace +``` + +Le toolchain Cargo n'est pas disponible dans l'environnement d'assemblage actuel. Les commandes Cargo post-bump restent donc `NON EXÉCUTÉ LOCAL` jusqu'au gate opérateur. + +Les audits Python locaux et l'intégrité du delta sont exécutés avant livraison. diff --git a/docs/validation/031-V0_3_14_MULTI_SOURCE_GAP_REPAIR_HARDENING.md b/docs/validation/031-V0_3_14_MULTI_SOURCE_GAP_REPAIR_HARDENING.md new file mode 100644 index 0000000..c04fff5 --- /dev/null +++ b/docs/validation/031-V0_3_14_MULTI_SOURCE_GAP_REPAIR_HARDENING.md @@ -0,0 +1,594 @@ + + + +# Validation v0.3.14 — gap repair / hardening multi-source RawTransaction + +## 1. Rôle + +Ce document enregistre les preuves réellement exécutées, les non-claims et les décisions de validation de `0.3.14`. + +`pre.001` est uniquement le gate d'audit/brainstorming/sizing/planification demandé par `prompts/033-V0_3_14_START_PROMPT.md`. Aucun moteur de repair, aucune policy failover/degraded, aucune modification Transport/Config et aucun adapter EARLY n'est implémenté dans cette tranche. + +## 2. Archive stable contrôlée + +```text +archive : khadhroony-solana-project-v0.3.13.zip +SHA-256 : 253fa7ef5e0a6b2721da66fc3ea8dc7695d991d3a00bcc7d34396d9a32b65245 +bytes : 8431253 +entries : 2012 +files : 1818 +absolute entries : 0 +parent traversal entries : 0 +duplicate entries : 0 +symlink entries : 0 +workspace members : 21 +crate manifests : 21 +workspace.package.version : 0.3.13 +edition : 2024 +stable delta : deltas/0.3.13/rel.001.md +``` + +Artefacts interdits recherchés : + +```text +Cargo.lock : absent +package-lock.json : absent +pnpm-lock.yaml : absent +yarn.lock : absent +rust-toolchain.toml : absent +.env : absent +target/ : absent +node_modules/ : absent +dist/ : absent +.git/ : absent +``` + +Résultat archive : PASS. + +L'absence de `.git` empêche de vérifier localement l'objet du tag `v0.3.13`. L'identité stable interne et le delta `rel.001` sont présents et cohérents ; aucune équivalence cryptographique au tag n'est revendiquée. + +## 3. Preuve opérateur stable fournie + +Le journal opérateur fourni avec la base exécute sur `0.3.13` : + +```text +cargo clean +cargo fmt --all +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 +cargo test --workspace --all-targets --all-features +cargo tree -p ksp-worker-raw-transaction-ingest-lib --edges normal +cargo tree -p ksp-worker-raw-transaction-ingest-lib -e features +cargo tree --duplicates +``` + +Les sorties fournies contiennent : + +```text +General Rust rule audit: clean +Rust export completeness audit: 0 candidate(s) +KSP workspace Rust rule audit: clean +Markdown table audit: clean (340 table(s), 855 file(s)) +``` + +Agrégation des `130` lignes `test result` du journal : + +```text +1 850 passed +0 failed +15 ignored +``` + +Ce gate qualifie la stable d'entrée. Les tests ignored restent ignored ; aucune preuve provider-gated n'est inventée. + +## 4. Audits locaux de la base avant modification + +Exécuté localement : + +```bash +python3 scripts/audit_rust_workspace_rules.py +``` + +Résultat : + +```text +General Rust rule audit: clean +Rust export completeness audit: 0 candidate(s) +KSP workspace Rust rule audit: clean +``` + +Exécuté localement : + +```bash +python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas +``` + +Résultat : + +```text +Markdown table audit: clean (340 table(s), 855 file(s)) +``` + +`cargo`/`rustfmt` ne sont pas disponibles dans l'environnement d'assemblage courant. Aucune commande Cargo locale n'est déclarée PASS. + +## 5. Audit des règles + +Documents normatifs relus : + +```text +RULES.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 +``` + +Scan supplémentaire des définitions normatives : + +```text +489 définitions +489 identifiants uniques +0 doublon +``` + +Un scan générique de tous les textes trouve des headers absents dans quelques deltas historiques et un fichier de font/documentation, ainsi qu'une fin de ligne absente dans un JSON de capability. Ces constats ne sont pas assimilés à une violation nouvelle de la stable : + +```text +les deltas historiques publiés sont immuables +JSON ne supporte pas les headers de commentaire +audit Rust officiel : clean +audit Markdown officiel : clean +``` + +Aucun ancien delta n'est réécrit par `pre.001`. + +## 6. Frontières de dépendances contrôlées + +Le manifest Worker stable contient exactement comme dépendances directes : + +```text +ksp-core-lib +ksp-logging-lib +ksp-onchain-transport-lib +ksp-raw-transaction-lib +ksp-store-lib +ksp-worker-api +sha2 +tokio +``` + +Absences confirmées : + +```text +ksp-config-lib +ksp-job-backfill-lib +ksp-store-postgres-lib +reqwest +tokio-tungstenite +tonic +yellowstone-grpc-proto direct Worker +provider SDK +``` + +La séparation Worker/Job/Transport/Store est donc cohérente avec les règles à l'ouverture. + +## 7. État de continuité interne audité + +### 7.1 Yellowstone Transport + +`YellowstoneGrpcSubscribeSnapshot` expose les compteurs de reconnect/replay et les slots sûrs. Sa documentation précise que `continuity_gap_count` est une preuve de rétention insuffisante lorsque `first_available` dépasse le replay demandé, pas une preuve qu'un événement filtré a réellement été perdu. + +Résultat : conforme au besoin de distinction `replay_attempt != repair`. + +### 7.2 Worker stable + +`RawTransactionIngestProcessingFrontierReporter::observe_source_continuity` : + +```text +rejette les régressions de compteurs +met à jour reconnect/replay/gap +rend source.continuity_gap_proven dès que continuity_gap_total augmente +``` + +Résultat : la terminalité conservative `0.3.13` est bien encore en place ; aucun repair non terminal caché n'existe. + +### 7.3 WebSocket + +L'acteur Transport incrémente le compteur de continuity gap lors d'une reconnexion physique. Il resubscribe les abonnements mais n'a pas de replay standard. + +Les façades typées exposent `snapshot()` mais pas de `snapshot_source()` watch public, et les sources Worker Standard Logs/Standard Block/Helius ne consomment actuellement pas la snapshot WS. Une évolution Transport latest-value additive est donc planifiée avant le repair afin de ne pas dépendre d'une notification métier ultérieure pour voir le gap. + +Résultat : reconnect WebSocket doit être traité comme détection de discontinuité potentielle, jamais comme preuve de couverture. + +### 7.4 HTTP + +Transport possède déjà : + +```text +getSlot +getBlocks +getBlocksWithLimit +getBlock observed +getTransaction observed +``` + +Mais les ressources Worker ne garantissent pas toutes ces méthodes de la même manière : + +```text +Yellowstone / Standard Logs / Helius Transaction : constructeur => getTransaction seulement +HTTP Block Polling : constructeur => getSlot + getBlocksWithLimit + getBlock +Standard Block : aucun pool HTTP attaché +fixtures hydration : rôles volontairement limités à get_transaction présents +``` + +Résultat : aucune nouvelle dépendance HTTP Worker n'est nécessaire pour le plan nominal, mais `pool HTTP présent` ne signifie jamais `repair HTTP disponible`. La capability de scan doit être détectée explicitement sur le rôle same-network réellement fourni. + +## 8. Audit externe — Solana RPC/WebSocket + +Références consultées le 11 septembre 2026 : + +```text +https://solana.com/docs/rpc/http/getslot +https://solana.com/docs/rpc/http/getblocks +https://solana.com/docs/rpc/http/getblockswithlimit +https://solana.com/docs/rpc/http/getblock +https://solana.com/docs/rpc/http/gettransaction +https://solana.com/docs/rpc/websocket/logssubscribe +https://solana.com/docs/rpc/websocket/blocksubscribe +``` + +Constats : + +```text +getBlocks/getBlocksWithLimit énumèrent des slots de blocs confirmés +plage maximale RPC documentée : 500 000 slots +getBlock peut retourner null +getTransaction peut retourner null +logsSubscribe transporte une référence/signature et non Common RAW +blockSubscribe reste instable et validator-gated +aucun replay historique WebSocket standard n'est documenté +``` + +Non-claim : les limites RPC ne sont pas reprises comme bornes Worker. + +Point à requalifier en smoke : les pages localisées Solana affichent encore `maxSupportedTransactionVersion = 0` alors que la stable KSP porte Legacy/V0/V1. `pre.001` ne change pas le contrat stable sur cette divergence documentaire ; il conserve ce point comme gate provider/live distinct. + +## 9. Audit externe — Yellowstone upstream + +Références consultées : + +```text +https://docs.rs/crate/yellowstone-grpc-proto/12.7.0 +https://github.com/rpcpool/yellowstone-grpc/blob/master/yellowstone-grpc-proto/proto/geyser.proto +https://github.com/rpcpool/yellowstone-grpc/blob/master/CHANGELOG.md +``` + +Constats : + +```text +KSP ^12.7 résout la version courante 12.7.0 publiée le 29 août 2026 +SubscribeRequest possède from_slot +Geyser possède SubscribeReplayInfo +SubscribeReplayInfoResponse possède first_available +``` + +Risque upstream prouvé : le changelog du 22 juillet 2026 décrit un bug corrigé où un replay de filtre `blocks` acceptait `from_slot` mais ne livrait aucun `Message::Block` replayé, puis reprenait au live head avec un state gap. + +Décision : une acceptation protocolaire de replay ne sera jamais suffisante comme critère `repaired`. + +## 10. Audit providers courants + +### 10.1 PublicNode + +Référence : + +```text +https://solana.publicnode.com/ +``` + +Prouvé publiquement : + +```text +Solana Mainnet RPC/WS/Yellowstone gRPC +Solana Testnet RPC/WS/Yellowstone gRPC +archive data available +``` + +Non prouvé dans une source primaire suffisamment précise : + +```text +profondeur Yellowstone replay +first_available effectif +rétention du stream +auth exacte du profil KSP actuel +``` + +Statut replay `pre.001` : NON PROUVÉ / smoke requis. + +### 10.2 OrbitFlare + +Références : + +```text +https://docs.orbitflare.com/data-streaming/yellowstone +https://orbitflare.com/products/solana-grpc +https://orbitflare.com/pricing +https://orbitflare.com/blog/developers/build-grpc-indexer +``` + +Prouvé publiquement : + +```text +Yellowstone full-data streaming +Devnet gRPC sur les tiers Free/Developer courants +gRPC complet/shared payant pour les usages plus larges +exemple récent d'utilisation de from_slot pour reprendre depuis MAX(slot) +``` + +Non prouvé : + +```text +profondeur exacte de replay du serveur KSP +SubscribeReplayInfo/first_available réellement activé sur le tier utilisé +équivalence du credential opérateur stable avec la documentation actuelle +``` + +Statut : candidat smoke Devnet, NON EXÉCUTÉ en `pre.001`. + +### 10.3 Helius + +Références : + +```text +https://www.helius.dev/pricing +https://www.helius.dev/blog/laserstream-websockets +https://www.helius.dev/laserstream +``` + +Prouvé publiquement : + +```text +transactionSubscribe WSS : Developer+ +LaserStream gRPC Devnet : Developer+ +LaserStream gRPC Mainnet : Business+ +replay LaserStream annoncé jusqu'à 24 h +``` + +Décision : ne pas attribuer ce replay gRPC à `transactionSubscribe` WSS et ne pas ajouter LaserStream gRPC comme nouvelle source `0.3.14`. + +### 10.4 QuickNode + +Référence comparative : + +```text +https://www.quicknode.com/guides/solana-development/tooling/solana-grpc/solana-grpc +``` + +La documentation du 3 septembre 2026 annonce `fromSlot` jusqu'à `3000` slots récents. QuickNode n'est pas configuré dans KSP ; aucune modification Config n'en découle. + +## 11. Matrice de capabilities validée pour le plan + +### Yellowstone + +```text +gap detect : oui +native replay : oui si provider l'adresse +replay coverage proof : non par attempt seul +reference hydration : oui +block material : oui selon filtre +HTTP repair : conditionnel ; pool présent mais constructeur garantit seulement getTransaction +``` + +### Standard Logs + +```text +gap detect : oui +native replay : non +coverage proof seule : non +reference hydration : oui +HTTP repair : conditionnel ; getTransaction seul est garanti par construction +``` + +### Standard Block + +```text +gap detect : oui +native replay : non +block material : oui +coverage proof intervalle : pas sans preuve complémentaire +HTTP repair attaché : non ; capability run-wide possible via une autre ressource compatible +``` + +### Helius Transaction WSS + +```text +gap detect : oui +native replay WSS : non prouvé +coverage proof seule : non +reference hydration : oui +HTTP repair : conditionnel ; getTransaction seul est garanti par construction +``` + +### HTTP Block Polling + +```text +gap detect : oui +slot enumeration : oui +skipped proof : oui uniquement dans une fenêtre de discovery prouvée complète +block material : oui +HTTP repair : oui +``` + +Interprétation de coverage retenue : `HTTP Block Polling` et `Standard Block(All)` sont les deux scopes `FullLedgerTransactions` immédiatement prouvables par l'état stable. Les autres filtres restent des `ExactSourceScope` privés. Une source full-ledger continue peut couvrir canoniquement un scope plus étroit sans fabriquer l'observation manquante de la source perdue. À l'inverse, un scan full-block n'est pas autorisé à élargir un run composé uniquement de scopes filtrés. + +## 12. Décisions pre.001 + +Décisions fermées : + +```text +1. gap = intervalle de continuité run-local, pas liste présumée de transactions manquantes ; +2. gap privé lié à source/network/commitment/range/coverage requirement ; +3. reconnect, replay_attempt, replay_covered, repaired et unresolved sont distincts ; +4. replay accepté n'est jamais une preuve de repair ; +5. transaction-only source peut fournir du matériau mais pas prouver l'absence d'autres transactions ; +6. le repair vise la coverage des entités RawTransaction autorisées, pas la fabrication des observations provider manquantes ; +7. FullLedgerTransactions est un superset explicite ; un scope filtré reste exact/opaque tant qu'une relation plus forte n'est pas prouvée ; +8. aucune équivalence cross-family n'est déduite d'un provider, d'un hash ou de deux sockets Active ; +9. KnownReferences/getTransaction peut compléter du matériau connu mais ne ferme pas les références inconnues ; +10. un pool/role d'hydration garantit getTransaction seulement ; les méthodes de scan doivent être détectées séparément ; +11. preuve finale de range = fenêtre slot/block explicitement complète ou capability équivalente qualifiée ; +12. getBlocksWithLimit réussi ne ferme pas à lui seul une queue arbitraire car sa limite compte des blocs ; +13. skipped slot seulement dans une fenêtre discovery prouvée complète, jamais depuis silence de stream ; +14. getBlock null sur slot produit = unresolved tant qu'aucune autre preuve ne le ferme ; +15. getTransaction null = missing/unresolved pour la référence connue, avec fallback possible vers le bloc du slot ; +16. un scan full-block ne broadit pas silencieusement un scope filtré ; +17. tout matériau admis repasse par Common RAW, admission centrale et ksp-store-lib ; +18. aucun retry réseau parallèle dans Worker ; +19. toutes les sources restent attendues ; redundant est une relation de coverage dynamique, pas un flag provider statique ; +20. Healthy exige aucune lacune et toutes les sources attendues actives ; +21. Degraded est permis uniquement si coverage canonique reste prouvée malgré reconnect/perte ; +22. Unhealthy signifie coverage temporairement non prouvée pendant repair borné ; unresolved non récupérable fait Faulted ; +23. processing frontier et continuity frontier restent distincts ; les ranges sont inclusifs pour couvrir les pertes intra-slot ; +24. un gap WS sans borne slot sûre n'est pas inventé depuis un timestamp ; il reste unresolved/terminal si aucune autre capability ne le borne ; +25. la snapshot WS doit devenir observable latest-value sans attendre une notification métier, via une petite surface Transport additive ; +26. TargetCoverage est la réunion conservative des scopes configurés au même network/commitment ; aucune relation Confirmed/Finalized implicite ; +27. une source Transport terminale n'est jamais respawnée par le Worker ; Degraded durable exige que les sources restantes couvrent TargetCoverage pour la suite du run ; +28. EARLY est hors scope 0.3.14 ; aucune nouvelle dépendance n'est nécessaire ; Config reste inchangé en pre.001 et ne sera ouvert qu'en cas de besoin prouvé ; +29. 0.3.14 est maintenue sans rescission, mais le forecast est allongé jusqu'à pre.016 pour respecter le budget par tranche. +``` + +## 13. Bornes cibles à tester + +```text +MAX_OPEN_REPAIR_GAPS = 64 +MAX_REPAIR_RANGE_SLOTS = 4096 +MAX_REPAIR_DISCOVERY_WINDOW_SLOTS = 512 +MAX_REPAIR_BLOCK_FETCH_IN_FLIGHT = 4 +MAX_REPAIR_ACTIVE_GAPS = 1 +``` + +Ces valeurs doivent être matérialisées par `pre.002+` avec tests exacts aux limites. Elles ne sont pas encore du runtime en `pre.001`. + +## 14. Threat model enregistré + +À couvrir : + +```text +reconnect storms +overlapping/adjacent gaps +stale redundant source +filter non-equivalence +replay truncation +replay accepted but empty +HTTP holes +skipped slots +getBlock null after produced-slot discovery +getTransaction null +duplicate repair/live +content disagreement +slow Store +admission backpressure +hydration fanout/coalescence +stop/fault pendant replay/scan/hydration/persistence +counter exhaustion +late snapshot publication +secret/provider/filter/signature leakage +``` + +## 15. Smokes accessibles / non accessibles en pre.001 + +```text +Solana HTTP Devnet : accessible en principe, non exécuté dans cette tranche de planification +Solana WebSocket Devnet : déjà qualifié sur stable ; non rejoué en pre.001 +OrbitFlare Yellowstone Devnet replay : credential requis, NON EXÉCUTÉ +PublicNode Yellowstone Mainnet/Testnet replay : auth/replay à qualifier, NON EXÉCUTÉ +Helius transactionSubscribe : tier/API key requis, NON EXÉCUTÉ +Worker repair -> Store end-to-end : moteur non implémenté, NON EXÉCUTÉ +``` + +Aucun smoke n'est présenté comme PASS sans exécution réelle. + +## 16. Sizing + +Mesuré sur la stable : + +```text +Worker src : 9 fichiers / 6 317 lignes +Worker tests : 5 fichiers / 2 725 lignes +Worker unit_tests : 6 fichiers / 5 523 lignes +Transport src : 33 fichiers / 22 764 lignes +``` + +Décision : scope maintenu dans `0.3.14`, mais le forecast initial est allongé de `pre.014` à `pre.016`. L'audit a isolé deux responsabilités qui ne doivent pas être mélangées : observabilité WS latest-value avant repair, puis replay natif et coverage redondante en tranches séparées. Les cinq familles et les primitives Transport restent la base ; les extensions suivantes rendraient le scope trop large et sont interdites dans cette release : + +```text +EARLY/Jetstream/shreds +nouveau provider Config +provider SDK +nouveau client Transport parallèle +campagne historique caller-driven +checkpoint durable Worker/Job +``` + +## 17. Modifications de pre.001 + +```text +Cargo.toml + header 565 -> 566 + workspace.package.version 0.3.13 -> 0.3.14-pre.1 + +docs/plans/035-V0_3_14_MULTI_SOURCE_GAP_REPAIR_HARDENING_PLAN.md + ajouté + +docs/validation/031-V0_3_14_MULTI_SOURCE_GAP_REPAIR_HARDENING.md + ajouté + +deltas/0.3.14/pre.001.md + ajouté +``` + +Aucun code Rust, Config, test, dépendance, feature, README/USAGE, architecture, CHANGELOG, ROADMAP ou prompt n'est modifié. + +## 18. Gate local post-modification + +Exécuté après application effective de `pre.001` : + +```text +General Rust rule audit : clean +Rust export completeness audit : 0 candidate(s) +KSP workspace Rust rule audit : clean +Markdown table audit : clean (340 tables / 858 files) +rule definitions : 489 définitions / 489 IDs uniques / 0 doublon +comparaison archive stable -> worktree : 3 ajouts / 1 modification / 0 suppression +artefacts générés après audit : scripts/__pycache__ supprimé avant packaging +Cargo fmt/check : NON EXÉCUTÉ LOCAL — cargo absent du sandbox +``` + +La comparaison avec le ZIP stable confirme que les seuls fichiers de livraison sont : + +```text +Cargo.toml +docs/plans/035-V0_3_14_MULTI_SOURCE_GAP_REPAIR_HARDENING_PLAN.md +docs/validation/031-V0_3_14_MULTI_SOURCE_GAP_REPAIR_HARDENING.md +deltas/0.3.14/pre.001.md +``` + +Le statut Cargo post-bump reste volontairement non revendiqué. Le journal opérateur fourni qualifie la stable `0.3.13`, pas `0.3.14-pre.1`. + +## 19. Gate opérateur requis avant pre.002 + +```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/0.3.14 +cargo check --workspace +``` + +`pre.001` ne modifie pas de Rust ; un Clippy/test workspace exhaustif n'est pas nécessaire pour prouver le contenu de cette tranche de planification, mais le gate stable complet fourni reste la baseline. Si le gate post-bump révèle un défaut, ouvrir un fix rattaché à `pre.001` avant `pre.002`.