# Delta `0.2.8-pre.007-fix.002` — observation scheduler bornée du heartbeat ## 1. Base et défaut restant Base appliquée : ```text 0.2.8-pre.7.fix.1 ``` Le replay opérateur du `fix.001` ferme le lint Clippy et le canari close : `fmt`, audit Rust, `cargo check` et `cargo clippy` sont verts. Transport atteint **330/331 unit** ; un seul canari reste en échec : ```text helius_heartbeat_sends_ping_at_sixty_seconds_and_rearms ``` Le reste des preuves heartbeat passe, notamment Helius-only, intervalle 60 s, absence standard, write failure/reconnect, close avant deadline et réarmement après reconnexion. Aucun défaut runtime supplémentaire n'est démontré. ## 2. Version technique ```text workspace.package.version = 0.2.8-pre.7.fix.2 commit attendu = v0.2.8-pre.007-fix.002 Git tag = aucun tag prerelease ``` Le header root `Cargo.toml` passe en version `229`. ## 3. Cause exacte Après : ```rust tokio::time::advance(std::time::Duration::from_secs(1)).await; ``` le deadline heartbeat est bien expiré, mais l'observation du Ping nécessite encore plusieurs étapes asynchrones : ```text actor WsSession -> websocket.send(Ping) -> socket local -> serveur fixture websocket.next() -> ping_tx.send(()) -> ping_rx.try_recv() ``` Un `try_recv()` après un nombre fixe faible de `yield_now()` reste une course scheduler. Le défaut est donc dans le protocole d'observation du test, pas dans la cadence runtime. ## 4. Correction déterministe Le fichier de tests ajoute un helper privé local : ```rust async fn wait_for_observed_ping(...) ``` Il effectue au maximum 256 tours : ```text try_recv() == Ok(()) -> succès immédiat try_recv() == Empty -> yield_now().await puis nouvelle tentative try_recv() == Disconnected -> échec immédiat ``` Après épuisement de la borne, le test échoue explicitement si aucun Ping n'est observé. Ce helper n'appelle volontairement : ```text ni tokio::time::advance() ni tokio::time::sleep() ni tokio::time::timeout() ``` L'horloge virtuelle reste donc exactement à `t=60 s` ou `t=120 s` pendant l'attente de propagation. Le canari ne peut pas réussir en observant accidentellement un heartbeat ultérieur. ## 5. Cadence vérifiée inchangée Le scénario reste : ```text t=59 s aucun Ping t=60 s premier Ping observé après propagation scheduler bornée t=119 s aucun second Ping t=120 s second Ping observé après propagation scheduler bornée ``` Aucune cadence de test spéciale n'est introduite. ## 6. Runtime explicitement inchangé Ce fix ne modifie pas : ```text crates/ksp-onchain-transport-lib/src/ws_session.rs HELIUS_WS_HEARTBEAT_INTERVAL = 60 s WebSocket Ping control frame WsProtocolKind WsSessionSettings Config / schema / env transactionSubscribe / transactionNotification lifecycle reconnect / remap / backpressure ``` Il n'ajoute aucune dépendance, feature ou visibilité. ## 7. Fichiers modifiés ```text Cargo.toml crates/ksp-onchain-transport-lib/unit_tests/ws_session.rs docs/plans/015-V0_2_8_HELIUS_LASERSTREAM_WEBSOCKET_PLAN.md docs/validation/011-V0_2_8_HELIUS_LASERSTREAM_WEBSOCKET.md deltas/0.2.8/pre.007-fix.002.md ``` ## 8. Gate 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 ``` Critère de fermeture : zéro warning, audit clean, **331 unit / 40 public API / 29 completeness / 4 doctests** et workspace vert. `pre.008` reste bloqué jusque-là.