From 3c5786f27379867a68f2f7a4f6fcdffae6dcef79 Mon Sep 17 00:00:00 2001 From: SinuS Von SifriduS Date: Sun, 23 Aug 2026 10:24:28 +0200 Subject: [PATCH] v0.2.7-pre.013 --- Cargo.toml | 4 +- .../tests/workspace_dependencies.rs | 56 ++++++++++- crates/ksp-onchain-transport-lib/README.md | 26 +++-- crates/ksp-onchain-transport-lib/USAGE.md | 24 ++++- .../tests/websocket_devnet_smoke.rs | 57 +++++++++++ deltas/0.2.7/pre.013.md | 99 +++++++++++++++++++ .../014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md | 32 +++++- .../010-V0_2_7_ONCHAIN_WEBSOCKET.md | 96 +++++++++++++----- 8 files changed, 352 insertions(+), 42 deletions(-) create mode 100644 crates/ksp-onchain-transport-lib/tests/websocket_devnet_smoke.rs create mode 100644 deltas/0.2.7/pre.013.md diff --git a/Cargo.toml b/Cargo.toml index ed5c6d1..498d083 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ # file: Cargo.toml -# version: 213 +# version: 214 [workspace] resolver = "3" members = ["crates/ksp-app-config-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-logging-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-wallet-lib"] [workspace.package] -version = "0.2.7-pre.12.fix.1" +version = "0.2.7-pre.13" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" diff --git a/crates/ksp-core-lib/tests/workspace_dependencies.rs b/crates/ksp-core-lib/tests/workspace_dependencies.rs index 72d2bf8..7f98ab9 100644 --- a/crates/ksp-core-lib/tests/workspace_dependencies.rs +++ b/crates/ksp-core-lib/tests/workspace_dependencies.rs @@ -1,5 +1,5 @@ // file: crates/ksp-core-lib/tests/workspace_dependencies.rs -// version: 3 +// version: 4 //! Workspace-level dependency policy canaries owned by the foundational KSP test surface. @@ -68,3 +68,57 @@ fn transport_manifest_preserves_ksp_dependency_firewall() { assert!(manifest.contains("[dev-dependencies]")); assert!(manifest.contains("tokio = { workspace = true, features = [\"net\", \"rt\"] }")); } + +#[test] +fn transport_manifest_runtime_and_dev_dependency_names_are_exact() { + let manifest_path = workspace_root().join("crates/ksp-onchain-transport-lib/Cargo.toml"); + let manifest = std::fs::read_to_string(manifest_path).expect("transport manifest must be readable during workspace integration tests"); + let dependencies_tail = manifest.split("[dependencies]").nth(1); + assert!(dependencies_tail.is_some(), "transport dependencies section must exist"); + let dependencies_tail = match dependencies_tail { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let dependencies = match dependencies_tail.split("[dev-dependencies]").next() { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let dependency_names = manifest_dependency_names(dependencies); + assert_eq!( + dependency_names, + std::vec!["futures-util", "ksp-core-lib", "ksp-logging-lib", "reqwest", "serde", "serde_json", "tokio", "tokio-tungstenite"] + ); + let dev_dependencies_tail = manifest.split("[dev-dependencies]").nth(1); + assert!(dev_dependencies_tail.is_some(), "transport dev-dependencies section must exist"); + let dev_dependencies_tail = match dev_dependencies_tail { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let dev_dependencies = match dev_dependencies_tail.split("[lints]").next() { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + assert_eq!(manifest_dependency_names(dev_dependencies), std::vec!["tokio"]); +} + +fn manifest_dependency_names(section: &str) -> std::vec::Vec<&str> { + let mut names = std::vec::Vec::new(); + for line in section.lines() { + let content = match line.split('#').next() { + std::option::Option::Some(value) => value.trim(), + std::option::Option::None => continue, + }; + if content.is_empty() { + continue; + } + let name = match content.split('=').next() { + std::option::Option::Some(value) => value.trim().trim_end_matches(".workspace"), + std::option::Option::None => continue, + }; + if !name.is_empty() { + names.push(name); + } + } + names.sort_unstable(); + return names; +} diff --git a/crates/ksp-onchain-transport-lib/README.md b/crates/ksp-onchain-transport-lib/README.md index 68fe8f0..c3e1dab 100644 --- a/crates/ksp-onchain-transport-lib/README.md +++ b/crates/ksp-onchain-transport-lib/README.md @@ -1,5 +1,5 @@ - + # `ksp-onchain-transport-lib` @@ -234,22 +234,30 @@ La configuration Logging de référence conserve un fichier dédié Transport à Les tests par défaut sont déterministes et n'exigent pas Internet : fixtures JSON et serveur HTTP local couvrent requêtes, réponses, retry, 429, timeout, redaction et routing. -Deux smokes Devnet opt-in sont séparés par responsabilité : +Trois smokes Devnet opt-in sont séparés par responsabilité : ```text -Transport pur : settings programmatiques -> HttpTransportPool - -> Accounts/Tokens/Cluster représentatifs - -> trois reads Transactions - -> getBlockHeight - -> getInflationRate/getStakeMinimumDelegation +Transport HTTP pur : settings programmatiques -> HttpTransportPool + -> Accounts/Tokens/Cluster représentatifs + -> trois reads Transactions + -> getBlockHeight + -> getInflationRate/getStakeMinimumDelegation + +Transport WebSocket pur : settings programmatiques -> WsSession + -> slotSubscribe + -> une slotNotification sous timeout + -> slotUnsubscribe + -> close Composition historique : Config -> std.transport/devnet_public -> HttpTransportPool -> getHealth/getGenesisHash/getVersion/getBalance ``` -Le smoke Transport utilise pour sa branche Token la forme Devnet documentée `getTokenAccountsByOwner(owner, { programId }, { commitment: finalized, encoding: jsonParsed })`. L'owner est une Pubkey ordinaire de l'exemple officiel ; aucune présence de token account n'est exigée, donc une liste vide reste valide. +Le smoke HTTP Transport utilise pour sa branche Token la forme Devnet documentée `getTokenAccountsByOwner(owner, { programId }, { commitment: finalized, encoding: jsonParsed })`. L'owner est une Pubkey ordinaire de l'exemple officiel ; aucune présence de token account n'est exigée, donc une liste vide reste valide. -Les deux sont `ignored` par défaut. Le smoke Transport appartient durablement à cette crate ; le smoke cross-crates hébergé dans Config reste transitoire jusqu'à l'existence d'une surface KSP d'intégration/orchestration appropriée. +Le smoke WebSocket Transport cible uniquement la famille stable `slotSubscribe` sur l'endpoint public Devnet `wss://api.devnet.solana.com`. Il borne connexion, attente de notification, unsubscribe et fermeture ; il ne transforme aucune famille unstable en gate live. + +Les trois tests sont `ignored` par défaut. Les deux smokes Transport appartiennent durablement à cette crate ; le smoke cross-crates hébergé dans Config reste transitoire jusqu'à l'existence d'une surface KSP d'intégration/orchestration appropriée. Un rate-limit, refus externe ou incident Devnet n'est pas assimilé automatiquement à une régression locale. ## Documentation diff --git a/crates/ksp-onchain-transport-lib/USAGE.md b/crates/ksp-onchain-transport-lib/USAGE.md index 5831550..b338672 100644 --- a/crates/ksp-onchain-transport-lib/USAGE.md +++ b/crates/ksp-onchain-transport-lib/USAGE.md @@ -1,5 +1,5 @@ - + # Utilisation de `ksp-onchain-transport-lib` @@ -334,7 +334,7 @@ La configuration standard route les événements `info` de Transport vers un fic ## 10. Smokes Devnet opt-in -Le smoke **Transport pur** construit ses settings programmatiquement et exerce un sous-ensemble représentatif d'Accounts/Tokens/Cluster, trois reads Transactions, puis des reads Blocks/Economics de la release stable `0.2.4` : +Le smoke **Transport HTTP pur** construit ses settings programmatiquement et exerce un sous-ensemble représentatif d'Accounts/Tokens/Cluster, trois reads Transactions, puis des reads Blocks/Economics de la release stable `0.2.4` : ```bash cargo test -p ksp-onchain-transport-lib --test transport_devnet_smoke -- --ignored --nocapture @@ -342,6 +342,14 @@ cargo test -p ksp-onchain-transport-lib --test transport_devnet_smoke -- --ignor Il appelle `getAccountInfo`, `getTokenAccountsByOwner`, `getEpochInfo`, `getVoteAccounts`, puis `getLatestBlockhash`, `isBlockhashValid`, `getTransactionCount`, `getBlockHeight`, `getInflationRate` et `getStakeMinimumDelegation`. La branche Token suit la forme Devnet documentée : owner Pubkey ordinaire de l'exemple officiel, selector `programId` avec l'ID canonique du programme SPL Token, puis config explicite `commitment: finalized` + `encoding: jsonParsed`. Une réponse vide reste acceptable. La branche Transaction reste read-only : elle ne déclenche ni airdrop ni soumission de transaction et ne remplace pas les fixtures déterministes couvrant les 11 wrappers. +Le smoke **Transport WebSocket pur** utilise l'endpoint public Devnet standard avec des settings programmatiques, ouvre une session physique, crée une subscription stable `slotSubscribe`, attend une notification bornée, vérifie une valeur de slot non nulle, annule la subscription avec son handle puis ferme explicitement la session : + +```bash +cargo test -p ksp-onchain-transport-lib --test websocket_devnet_smoke -- --ignored --nocapture +``` + +Il n'utilise ni `blockSubscribe`, ni `slotsUpdatesSubscribe`, ni `voteSubscribe` : ces familles restent unstable et leur disponibilité dépend des capabilities du validator. Le smoke live n'est donc pas un gate de disponibilité de ces extensions. + Le smoke historique de **composition Config -> Transport** reste également disponible : ```bash @@ -350,4 +358,14 @@ cargo test -p ksp-config-lib --test transport_devnet_smoke -- --ignored --nocapt Il valide le profil committé `devnet_public` et les quatre canaris foundation. Il reste transitoirement hébergé dans Config : les futurs smokes cross-crates ne doivent pas faire de Config leur destination générale et devront migrer vers une surface d'intégration/orchestration dédiée lorsqu'elle existera. -Les endpoints publics Solana sont rate-limités et non destinés à la production. Un échec réseau externe n'est pas assimilé automatiquement à une régression locale ; les fixtures HTTP locales restent les gates reproductibles. +Les endpoints publics Solana sont rate-limités et non destinés à la production. Un échec réseau externe n'est pas assimilé automatiquement à une régression locale ; les fixtures HTTP et WebSocket locales restent les gates reproductibles. + +Pour l'audit final de dépendances de `0.2.7`, inspecter également le graphe effectif après résolution Cargo : + +```bash +cargo tree -p ksp-onchain-transport-lib +cargo tree -p ksp-onchain-transport-lib --duplicates +cargo tree --duplicates +``` + +Le premier graphe doit conserver la frontière `Transport -> Core + Logging + crates techniques`; il ne doit introduire aucune dépendance Config, Wallet, Store, Program ou `tracing` directe. Les sorties `--duplicates` sont un diagnostic de résolution transitive : une duplication n'est pas supprimée aveuglément si elle est imposée par des dépendances upstream incompatibles. diff --git a/crates/ksp-onchain-transport-lib/tests/websocket_devnet_smoke.rs b/crates/ksp-onchain-transport-lib/tests/websocket_devnet_smoke.rs new file mode 100644 index 0000000..466fe4e --- /dev/null +++ b/crates/ksp-onchain-transport-lib/tests/websocket_devnet_smoke.rs @@ -0,0 +1,57 @@ +// file: crates/ksp-onchain-transport-lib/tests/websocket_devnet_smoke.rs +// version: 1 + +//! Opt-in live Devnet smoke for one stable standard Solana WebSocket subscription. + +fn devnet_websocket_endpoint() -> ksp_core_lib::Result { + let url_result = ksp_onchain_transport_lib::WsEndpointUrl::parse("wss://api.devnet.solana.com"); + let url = match url_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let session = ksp_onchain_transport_lib::WsSessionSettings::new( + std::time::Duration::from_secs(10), + std::time::Duration::from_secs(5), + ksp_onchain_transport_lib::WsReconnectSettings::new(0, std::time::Duration::from_millis(250), std::time::Duration::from_secs(2)), + ksp_onchain_transport_lib::WsResubscribePolicy::ActiveSubscriptions, + 16, + 16, + 8, + 8, + 4 * 1024 * 1024, + 4 * 1024 * 1024, + 256 * 1024, + ); + return std::result::Result::Ok(ksp_onchain_transport_lib::WsEndpointSettings::new( + "solana_devnet_public_ws", + true, + ksp_onchain_transport_lib::WsProviderName::new("solana-public"), + ksp_onchain_transport_lib::WsClusterName::new("devnet"), + ksp_onchain_transport_lib::WsProtocolKind::SolanaStandard, + url, + session, + )); +} + +#[tokio::test(flavor = "current_thread")] +#[ignore = "opt-in live Solana Devnet WebSocket smoke; performs an external network connection"] +async fn programmatic_devnet_websocket_reaches_slot_notification_then_unsubscribes_and_closes() { + let endpoint = devnet_websocket_endpoint().expect("programmatic Devnet WebSocket settings must construct an endpoint"); + let session = ksp_onchain_transport_lib::WsSession::connect(endpoint).await.expect("Devnet WebSocket handshake must succeed"); + assert_eq!(session.snapshot().state(), ksp_onchain_transport_lib::WsSessionState::Active); + let mut subscription = session.slot_subscribe().await.expect("Devnet slotSubscribe must succeed"); + assert_eq!(subscription.kind(), ksp_onchain_transport_lib::WsSubscriptionKind::Slot); + assert_eq!(subscription.state(), ksp_onchain_transport_lib::WsSubscriptionState::Active); + let notification_wait = tokio::time::timeout(std::time::Duration::from_secs(20), subscription.recv()).await; + let notification = match notification_wait { + std::result::Result::Ok(std::option::Option::Some(std::result::Result::Ok(value))) => value, + std::result::Result::Ok(std::option::Option::Some(std::result::Result::Err(error))) => panic!("Devnet slot notification decoding failed: {error}"), + std::result::Result::Ok(std::option::Option::None) => panic!("Devnet slot subscription closed before one notification arrived"), + std::result::Result::Err(_) => panic!("Devnet slot notification did not arrive within the smoke timeout"), + }; + assert!(notification.slot() > 0); + let unsubscribed = subscription.unsubscribe().await.expect("Devnet slotUnsubscribe must succeed"); + assert!(unsubscribed); + session.close().await.expect("Devnet WebSocket session must close cleanly"); + assert_eq!(session.snapshot().state(), ksp_onchain_transport_lib::WsSessionState::Closed); +} diff --git a/deltas/0.2.7/pre.013.md b/deltas/0.2.7/pre.013.md new file mode 100644 index 0000000..1e0a8aa --- /dev/null +++ b/deltas/0.2.7/pre.013.md @@ -0,0 +1,99 @@ + + + +# Delta `0.2.7-pre.013` — smoke WebSocket live et audit final des dépendances + +## Base + +Base directe validée par l'opérateur : + +```text +0.2.7-pre.012-fix.001 +Cargo 0.2.7-pre.12.fix.1 +``` + +Les gates `fmt`, audit Python, `check`, `clippy`, les 309 tests unitaires Transport, 36 tests d'API publique, 24 tests de release completeness, 109 tests unitaires Config et le workspace complet sont verts. + +## Signal technique + +```text +livraison = 0.2.7-pre.013 +workspace.package.version = 0.2.7-pre.13 +commit = v0.2.7-pre.013 +tag = aucun +``` + +## Smoke WebSocket Devnet opt-in + +Un nouveau smoke Transport pur `websocket_devnet_smoke.rs` couvre le chemin live stable minimal : + +```text +settings programmatiques + -> wss://api.devnet.solana.com + -> WsSession::connect + -> slotSubscribe + -> une slotNotification sous timeout 20 s + -> slotUnsubscribe via WsSubscription + -> WsSession::close +``` + +Le test est `#[ignore]` par défaut. Il ne lit ni Config ni environnement et n'utilise aucune famille unstable. Les fixtures WebSocket locales restent les gates déterministes du lifecycle, des DTOs et des familles `block`, `slotsUpdates` et `vote`. + +Un échec lié au réseau, au rate-limit ou à l'indisponibilité Devnet ne constitue pas automatiquement une régression KSP. + +## Audit de dépendances + +Le canari workspace de `ksp-core-lib` verrouille désormais les noms directs de dépendances de Transport : + +```text +runtime : futures-util, ksp-core-lib, ksp-logging-lib, reqwest, serde, serde_json, tokio, tokio-tungstenite +dev : tokio +``` + +Il complète le firewall existant contre Config, Store, Program et `tracing` direct. + +L'inspection du graphe résolu doit être faite côté opérateur : + +```bash +cargo tree -p ksp-onchain-transport-lib +cargo tree -p ksp-onchain-transport-lib --duplicates +cargo tree --duplicates +``` + +Les doublons transitifs sont à analyser, pas à supprimer mécaniquement lorsqu'ils résultent de contraintes upstream différentes. + +## Documentation + +`README.md` et `USAGE.md` distinguent désormais les smokes HTTP Transport, WebSocket Transport et le smoke historique Config -> Transport. Le plan et la matrice enregistrent le checkpoint `pre.013`. + +`ROADMAP.md` et `CHANGELOG.md` restent inchangés pendant cette prerelease. + +## Comptages attendus + +```text +Transport unit tests = 309 +Transport public API tests = 36 +Transport release completeness = 24 +Transport WebSocket live smoke = 1 ignored par défaut +Config unit tests = 109 +Core workspace dependency tests = 3 +``` + +## Validation opérateur + +```bash +cargo fmt --all +python3 scripts/audit_rust_workspace_rules.py +cargo check --workspace +cargo clippy --workspace --all-targets +cargo test -p ksp-onchain-transport-lib +cargo test --workspace +cargo tree -p ksp-onchain-transport-lib +cargo tree -p ksp-onchain-transport-lib --duplicates +cargo tree --duplicates +cargo test -p ksp-onchain-transport-lib --test websocket_devnet_smoke -- --ignored --nocapture +``` + +Le smoke live peut être qualifié séparément si Devnet est indisponible ; les autres gates restent déterministes. + +Le sandbox de préparation ne possède pas Cargo/rustc ; les gates Cargo et le smoke live restent à exécuter côté opérateur. diff --git a/docs/plans/014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md b/docs/plans/014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md index cc076cd..a7eed26 100644 --- a/docs/plans/014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md +++ b/docs/plans/014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md @@ -1,5 +1,5 @@ - + # Plan `0.2.7` — WebSocket Solana standard @@ -947,6 +947,34 @@ Les noms JSON-RPC exacts restent verrouillés par le canari interne `websocket_s La preuve Config -> session est volontairement déterministe : le future de connexion est construit à partir d'un `WsEndpointSettings` issu du document V2 committed sans polling. Aucun réseau réel n'est donc nécessaire ; le smoke live reste dans `pre.013`. +## 21.2 Checkpoint smoke live et dépendances `pre.013` + +`pre.013` ne modifie pas le runtime WebSocket. Il ajoute le smoke live Transport pur retenu par le plan et ferme la documentation d'utilisation avant la validation workspace finale. + +Le smoke est volontairement stable et minimal : + +```text +wss://api.devnet.solana.com + -> WsSession::connect + -> slotSubscribe + -> une slotNotification sous timeout + -> WsSubscription::unsubscribe + -> WsSession::close +``` + +Il est `#[ignore]` par défaut, utilise des settings programmatiques et n'accède ni à Config ni à l'environnement. Les trois familles unstable ne participent pas au smoke : leur activation dépend des capabilities du validator et un refus externe ne doit pas devenir une régression locale. + +Le gate de dépendances est renforcé de deux façons : + +```text +source canary -> noms exacts des dépendances runtime/dev de ksp-onchain-transport-lib +opérateur -> cargo tree Transport + cargo tree --duplicates local et workspace +``` + +Le canari source interdit qu'une dépendance nouvelle se glisse silencieusement dans Transport. `cargo tree --duplicates` reste un diagnostic de résolution transitive : le gate demande inspection et justification, pas la suppression mécanique de doublons imposés par upstream. + +README/USAGE documentent désormais les smokes HTTP/WS séparés, les limites du smoke live et les commandes d'audit du graphe Cargo. + ## 22. Forecast recalibré L'inventaire officiel n'impose que 9 familles de subscriptions, mais le lifecycle concurrent est plus coûteux que le forecast initial. Le gate reste **positif sans split de release**, à condition de granulariser les tranches au lieu de compresser le moteur et les wrappers. @@ -964,7 +992,7 @@ pre.009 DONE — wrappers stable lot A : account + program + logs, DTOs/options pre.010 DONE — wrappers stable lot B : signature + slot + root, terminaison signature/KSP-TRANSPORT-007 pre.011 DONE — unstable : block + slotsUpdates + vote, warnings + fallbacks wire/KSP-TRANSPORT-007 pre.012 DONE — compliance 18/18 + canaries public API + composition Config + régressions HTTP -pre.013 smoke live opt-in + README/USAGE + cargo tree/duplicates + dependency audit final +pre.013 DONE — smoke live opt-in + README/USAGE + source dependency canary + cargo tree/duplicates opérateur pre.014 validation workspace finale + docs/compliance + prompt 0.2.8 rel.001 publication stable stricte ``` diff --git a/docs/validation/010-V0_2_7_ONCHAIN_WEBSOCKET.md b/docs/validation/010-V0_2_7_ONCHAIN_WEBSOCKET.md index ae3b826..1e23d76 100644 --- a/docs/validation/010-V0_2_7_ONCHAIN_WEBSOCKET.md +++ b/docs/validation/010-V0_2_7_ONCHAIN_WEBSOCKET.md @@ -1,9 +1,9 @@ - + # Validation `0.2.7` — WebSocket Solana standard -> **Statut : matrice active, `0.2.7-pre.012`.** Les 9 familles standard et leurs 9 opérations unsubscribe sont matérialisées. `pre.012` consolide la compliance 18/18, la surface publique, la composition Config V2 -> Transport et la non-régression HTTP avant le smoke/audit de dépendances final. +> **Statut : matrice active, `0.2.7-pre.013`.** La compliance 18/18 est acquise. `pre.013` ajoute le smoke WebSocket Devnet opt-in, synchronise README/USAGE et renforce le gate de dépendances avant la validation workspace finale. ## 1. Baseline normative @@ -206,33 +206,35 @@ Les votes observés sont gossip/pre-consensus ; aucune garantie d'entrée dans l ## 7. Threat/security compliance initiale -| Invariant | Preuve attendue | Statut | -|---------------------------------------------------|----------------------------------------------|------------------------------------------| -| URL/credentials absents de `Debug` | unit tests URL wrapper | **Done `pre.002`** | -| URL/credentials absents des erreurs | validation URL + connection errors safe | **Done through `pre.004`** | -| URL/credentials absents des logs | actor logs only safe endpoint metadata | Partial `pre.004`, capture finale future | -| snapshots sans URL/raw payload | unit shape + public contract | **Done `pre.002`** | -| frame/message finis | settings bornés + `WebSocketConfig` raccordé | **Done `pre.005`** | -| JSON borné indirectement par message | oversized + malformed fixture | **Done `pre.005`** | -| queues notifications bornées | queue typed bornée + overflow isolé | **Done `pre.008`** | -| pending RPC borné + timeout | map actor bornée + timeout request | **Done `pre.004`** | -| reconnect loop bornée | repeated disconnect fixture | Done `pre.007` | -| unsubscribe pendant reconnect ne resubscribe pas | race fixture | Done `pre.007` | -| signature terminale ne resubscribe pas | terminal fixture | Planned | -| shutdown ne bloque pas | peer hostile/no close ack fixture | **Done `pre.005`** | -| no Store/Program/Wallet/Config dep dans Transport | cargo tree + source canary | Planned | -| no direct `tracing` dans Transport | workspace audit + source audit | **Done `pre.002`** | +| Invariant | Preuve attendue | Statut | +|---------------------------------------------------|----------------------------------------------|--------------------------------------------------------| +| URL/credentials absents de `Debug` | unit tests URL wrapper | **Done `pre.002`** | +| URL/credentials absents des erreurs | validation URL + connection errors safe | **Done through `pre.004`** | +| URL/credentials absents des logs | actor logs only safe endpoint metadata | **Done through `pre.011`, source audit `pre.013`** | +| snapshots sans URL/raw payload | unit shape + public contract | **Done `pre.002`** | +| frame/message finis | settings bornés + `WebSocketConfig` raccordé | **Done `pre.005`** | +| JSON borné indirectement par message | oversized + malformed fixture | **Done `pre.005`** | +| queues notifications bornées | queue typed bornée + overflow isolé | **Done `pre.008`** | +| pending RPC borné + timeout | map actor bornée + timeout request | **Done `pre.004`** | +| reconnect loop bornée | repeated disconnect fixture | Done `pre.007` | +| unsubscribe pendant reconnect ne resubscribe pas | race fixture | Done `pre.007` | +| signature terminale ne resubscribe pas | terminal fixture | **Done `pre.010`** | +| shutdown ne bloque pas | peer hostile/no close ack fixture | **Done `pre.005`** | +| no Store/Program/Wallet/Config dep dans Transport | cargo tree + source canary | **Source gate `pre.013`, cargo tree opérateur requis** | +| no direct `tracing` dans Transport | workspace audit + source audit | **Done `pre.002`** | ## 8. Dependency compliance initiale -Candidate retenue au gate : +Candidates auditées à l'ouverture de release : ```text -tokio-tungstenite 0.30.0 -futures-util 0.3.34 +tokio-tungstenite 0.30.x +futures-util 0.3.x ``` -Features prévues : +Le workspace conserve des contraintes caret `^0.30` et `^0.3` sans lockfile versionné. La version transitive effectivement résolue n'est donc pas figée dans cette matrice ; `pre.013` exige l'inspection du `cargo tree` opérateur correspondant au checkout validé. + +Features retenues : ```text tokio-tungstenite: default-features=false + connect + rustls-tls-webpki-roots @@ -642,6 +644,50 @@ Transport release completeness = 24 Config unit tests = 109 ``` +## 9.11 Checkpoint smoke live et audit de dépendances `pre.013` + +Le smoke WebSocket live retenu est un test Transport pur `#[ignore]` qui ne lit ni Config ni environnement : + +```text +endpoint programmatique wss://api.devnet.solana.com +WsSession::connect +slotSubscribe stable +attente bornée d'une slotNotification +slot > 0 +slotUnsubscribe via handle +close explicite de la session +``` + +Les familles `block`, `slotsUpdates` et `vote` ne sont pas utilisées dans ce smoke car leur disponibilité dépend de capabilities validator unstable. Les fixtures locales restent les preuves reproductibles de ces familles et du lifecycle. + +Le canari workspace de dépendances verrouille désormais les noms directs de Transport : + +```text +runtime : futures-util, ksp-core-lib, ksp-logging-lib, reqwest, serde, serde_json, tokio, tokio-tungstenite +dev : tokio +``` + +Il complète le firewall existant contre Config/Store/Program/tracing direct. L'inspection du graphe résolu reste une preuve opérateur avec : + +```bash +cargo tree -p ksp-onchain-transport-lib +cargo tree -p ksp-onchain-transport-lib --duplicates +cargo tree --duplicates +``` + +Une duplication transitive n'est pas considérée automatiquement comme un défaut : elle doit être comprise et n'est supprimée que si KSP peut la résoudre sans downgrade, pin artificiel ou violation des responsabilités upstream. + +Comptages déterministes attendus inchangés côté Transport, plus un smoke live ignoré et un canari workspace supplémentaire dans Core : + +```text +Transport unit tests = 309 +Transport public API tests = 36 +Transport release completeness = 24 +Transport WebSocket live smoke = 1 ignored par défaut +Config unit tests = 109 +Core workspace dependency tests = 3 +``` + ## 10. Validation du gate `pre.001` Exécuté dans le sandbox : @@ -689,9 +735,9 @@ reconnect/resubscribe/backpressure/shutdown gates verts unstable warnings centralisés HTTP 52+14 non régressé (**Done `pre.012`**) Config V1 backward + V2 WS validés (**Done `pre.003`**, compilation opérateur requise) -smoke live opt-in documenté -cargo tree inspecté +smoke live opt-in documenté (**Done `pre.013`**) +cargo tree inspecté (**preuve opérateur `pre.013` requise**) cargo test --workspace vert -README/USAGE synchronisés +README/USAGE synchronisés (**Done `pre.013`**) prompt 0.2.8 préparé ```