v0.2.8-pre.007-fix.002
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
# file: Cargo.toml
|
||||
# version: 228
|
||||
# version: 229
|
||||
|
||||
[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.8-pre.7.fix.1"
|
||||
version = "0.2.8-pre.7.fix.2"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-onchain-transport-lib/unit_tests/ws_session.rs
|
||||
// version: 9
|
||||
// version: 10
|
||||
|
||||
use futures_util::SinkExt; // rust-rules: trait-import
|
||||
use futures_util::StreamExt; // rust-rules: trait-import
|
||||
@@ -189,6 +189,18 @@ async fn yield_runtime_steps() {
|
||||
return;
|
||||
}
|
||||
|
||||
async fn wait_for_observed_ping(ping_rx: &mut tokio::sync::mpsc::UnboundedReceiver<()>) {
|
||||
for _ in 0..256 {
|
||||
match ping_rx.try_recv() {
|
||||
std::result::Result::Ok(()) => return,
|
||||
std::result::Result::Err(tokio::sync::mpsc::error::TryRecvError::Empty) => tokio::task::yield_now().await,
|
||||
std::result::Result::Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => panic!("heartbeat observation channel disconnected before Ping"),
|
||||
}
|
||||
}
|
||||
assert!(matches!(ping_rx.try_recv(), std::result::Result::Ok(())), "heartbeat Ping was not observed after bounded scheduler progress");
|
||||
return;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn websocket_session_connects_and_round_trips_internal_json_rpc() {
|
||||
let (listener, url) = bind_local_listener().await;
|
||||
@@ -457,14 +469,12 @@ async fn helius_heartbeat_sends_ping_at_sixty_seconds_and_rearms() {
|
||||
yield_runtime_steps().await;
|
||||
assert!(matches!(ping_rx.try_recv(), std::result::Result::Err(tokio::sync::mpsc::error::TryRecvError::Empty)));
|
||||
tokio::time::advance(std::time::Duration::from_secs(1)).await;
|
||||
yield_runtime_steps().await;
|
||||
assert!(matches!(ping_rx.try_recv(), std::result::Result::Ok(())));
|
||||
wait_for_observed_ping(&mut ping_rx).await;
|
||||
tokio::time::advance(std::time::Duration::from_secs(59)).await;
|
||||
yield_runtime_steps().await;
|
||||
assert!(matches!(ping_rx.try_recv(), std::result::Result::Err(tokio::sync::mpsc::error::TryRecvError::Empty)));
|
||||
tokio::time::advance(std::time::Duration::from_secs(1)).await;
|
||||
yield_runtime_steps().await;
|
||||
assert!(matches!(ping_rx.try_recv(), std::result::Result::Ok(())));
|
||||
wait_for_observed_ping(&mut ping_rx).await;
|
||||
session.close().await.expect("Helius heartbeat fixture session must close");
|
||||
tokio::time::resume();
|
||||
server.await.expect("Helius heartbeat fixture server must complete");
|
||||
|
||||
132
deltas/0.2.8/pre.007-fix.002.md
Normal file
132
deltas/0.2.8/pre.007-fix.002.md
Normal file
@@ -0,0 +1,132 @@
|
||||
<!-- file: deltas/0.2.8/pre.007-fix.002.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# 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à.
|
||||
@@ -1,9 +1,9 @@
|
||||
<!-- file: docs/plans/015-V0_2_8_HELIUS_LASERSTREAM_WEBSOCKET_PLAN.md -->
|
||||
<!-- version: 18 -->
|
||||
<!-- version: 19 -->
|
||||
|
||||
# Plan `0.2.8` — Helius LaserStream WebSocket
|
||||
|
||||
> **Statut : `0.2.8-pre.007` est fonctionnellement implémenté mais son gate opérateur révèle un défaut de canaris Tokio pausés et un lint `implicit_return`. `0.2.8-pre.007-fix.001` corrige uniquement le protocole de test ; le runtime heartbeat Helius 60 s reste inchangé.**
|
||||
> **Statut : `0.2.8-pre.007-fix.001` corrige le lint et le canari close, mais le replay laisse un seul canari périodique en course scheduler. `0.2.8-pre.007-fix.002` borne l'observation du Ping sans avancer l'horloge virtuelle ; le runtime heartbeat Helius 60 s reste inchangé.**
|
||||
|
||||
## 1. Objet, base et état courant
|
||||
|
||||
@@ -35,10 +35,11 @@ pre.005-fix.001 correction types de canaris + visibilité/tests/règles val
|
||||
pre.005-fix.002 suppression warnings dead_code via cfg(test) validée
|
||||
pre.006 transactionNotification + lifecycle actor validé
|
||||
pre.007 heartbeat Helius WebSocket / idle — checkpoint gate à corriger
|
||||
pre.007-fix.001 déterminisme canaris Tokio + lint test — préparé
|
||||
pre.007-fix.001 lint + close canary corrigés ; un canari périodique reste non déterministe
|
||||
pre.007-fix.002 observation bornée du Ping après deadline — préparé
|
||||
|
||||
workspace.package.version courant = 0.2.8-pre.7.fix.1
|
||||
commit attendu = v0.2.8-pre.007-fix.001
|
||||
workspace.package.version courant = 0.2.8-pre.7.fix.2
|
||||
commit attendu = v0.2.8-pre.007-fix.002
|
||||
aucun tag prerelease
|
||||
```
|
||||
|
||||
@@ -69,8 +70,8 @@ pre.005 DONE — transactionSubscribe request typed + filters/options/tokenAcco
|
||||
pre.006 DONE — transactionNotification + handle live + actor registry/reconnect/resubscribe/unsubscribe races
|
||||
+ late notifications + backpressure ciblée, sans second actor/socket
|
||||
pre.007 CHECKPOINT — heartbeat Helius WebSocket/idle + timers + interaction reconnect/control frames/shutdown
|
||||
fix.001 PREPARED — actor armé avant avance de l'horloge Tokio + close accepte canal d'observation fermé
|
||||
+ `return;` explicite du helper de yield pour `clippy::implicit_return`
|
||||
fix.001 CHECKPOINT — lint et close canary corrigés ; 330/331 unit, seul le canari périodique reste en course scheduler
|
||||
fix.002 PREPARED — observation positive du Ping bornée en scheduler steps, sans nouvelle avance du temps virtuel
|
||||
pre.008 provider adversarial lifecycle + capability guards + payload/backpressure + security/redaction
|
||||
pre.009 compliance Helius WebSocket + non-régressions Solana standard 18/18 + HTTP 52/14
|
||||
+ Config/API/dependency-firewall canaries
|
||||
@@ -1181,3 +1182,51 @@ helius_explicit_close_cancels_heartbeat_before_deadline
|
||||
|
||||
La constante 60 s, la frame Ping, le branch actor, le reconnect, Config et `WsSessionSettings` restent byte-for-byte hors du fix. `pre.008` reste bloqué jusqu'au replay vert de `pre.007-fix.001`.
|
||||
|
||||
## 17. Replay `pre.007-fix.001` et préparation `pre.007-fix.002`
|
||||
|
||||
Checkpoint opérateur `0.2.8-pre.7.fix.1` reçu le 2026-08-23 :
|
||||
|
||||
```text
|
||||
[x] cargo fmt --all
|
||||
[x] python3 scripts/audit_rust_workspace_rules.py = clean / 0 candidate
|
||||
[x] cargo check --workspace = vert, sans warning
|
||||
[x] cargo clippy --workspace --all-targets = vert, sans warning
|
||||
[!] cargo test -p ksp-onchain-transport-lib = 330/331 unit ; 1 seul échec heartbeat périodique
|
||||
[ ] cargo test --workspace — bloqué tant que Transport n'est pas vert
|
||||
```
|
||||
|
||||
Le `fix.001` a donc bien corrigé :
|
||||
|
||||
```text
|
||||
[x] `clippy::implicit_return` dans yield_runtime_steps
|
||||
[x] close avant deadline : aucun Ping puis Empty/Disconnected acceptés après fermeture
|
||||
[x] policy Helius-only + 60 s
|
||||
[x] absence de heartbeat standard
|
||||
[x] write failure -> reconnect
|
||||
[x] réarmement après reconnect
|
||||
```
|
||||
|
||||
Échec restant :
|
||||
|
||||
```text
|
||||
helius_heartbeat_sends_ping_at_sixty_seconds_and_rearms
|
||||
le timer est expiré à t=60 s, mais l'observation traverse encore plusieurs tâches :
|
||||
actor -> écriture socket -> lecture serveur fixture -> ping_tx -> ping_rx.
|
||||
|
||||
Un `try_recv()` après un nombre fixe faible de yields reste donc une course scheduler.
|
||||
```
|
||||
|
||||
`fix.002` reste test-only et ne modifie pas `src/ws_session.rs`. Il ajoute un helper d'observation bornée qui :
|
||||
|
||||
```text
|
||||
- tente `try_recv()` ;
|
||||
- si Empty, cède explicitement au scheduler ;
|
||||
- répète au maximum 256 tours ;
|
||||
- échoue immédiatement si le canal est Disconnected ;
|
||||
- n'appelle ni `advance()`, ni `sleep()`, ni `timeout()` pendant cette attente.
|
||||
```
|
||||
|
||||
Ainsi, le test peut laisser la propagation async finir **sans faire avancer l'horloge Tokio au-delà de t=60 s / t=120 s**. Le canari continue donc de vérifier la vraie cadence nominale, et ne peut pas réussir grâce à un heartbeat ultérieur.
|
||||
|
||||
Le runtime reste byte-for-byte hors du fix : cadence 60 s, Ping control frame, actor unique, reconnect, shutdown, Config et lifecycle transaction inchangés. `pre.008` reste bloqué jusqu'au replay entièrement vert de `pre.007-fix.002`.
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<!-- file: docs/validation/011-V0_2_8_HELIUS_LASERSTREAM_WEBSOCKET.md -->
|
||||
<!-- version: 17 -->
|
||||
<!-- version: 18 -->
|
||||
|
||||
# Validation `0.2.8` — Helius LaserStream WebSocket
|
||||
|
||||
> **Statut : `pre.007` est implémenté mais son gate est bloqué par un lint de helper test et deux canaris Tokio pausés. `pre.007-fix.001` est préparé comme correction test-only, sans changement runtime heartbeat.**
|
||||
> **Statut : `pre.007-fix.001` passe fmt/audit/check/Clippy et corrige le close canary ; un seul canari périodique reste non déterministe. `pre.007-fix.002` est préparé comme correction test-only d'observation scheduler, sans changement runtime heartbeat.**
|
||||
|
||||
## 1. Références
|
||||
|
||||
@@ -26,6 +26,7 @@ pre.005 dead-code fix deltas/0.2.8/pre.005-fix.002.md
|
||||
pre.006 actor transaction deltas/0.2.8/pre.006.md
|
||||
pre.007 heartbeat Helius deltas/0.2.8/pre.007.md
|
||||
pre.007 test determinism fix deltas/0.2.8/pre.007-fix.001.md
|
||||
pre.007 scheduler observation fix deltas/0.2.8/pre.007-fix.002.md
|
||||
validation standard WS docs/validation/010-V0_2_7_ONCHAIN_WEBSOCKET.md
|
||||
HTTP compliance docs/validation/007-V0_2_4_HTTP_FINAL_COMPLIANCE.md
|
||||
KSP-TRANSPORT-007 docs/validation/005-V0_2_3_KSP_TRANSPORT_007_RETRO_AUDIT.md
|
||||
@@ -344,6 +345,7 @@ Constats `pre.002-fix.001` :
|
||||
|
||||
Aucun split en fichiers supplémentaires n'est retenu : les plans historiques `0.2.5`–`0.2.7` sont de taille comparable ou supérieure, et le contenu du plan `015` reste entièrement centré sur une seule release. Le problème identifié était **l'ordre interne et la duplication de responsabilités**, pas la nécessité d'un nouveau type de document.
|
||||
|
||||
|
||||
## 12. Gates opérateur `pre.004` / `fix.001` / `fix.002`
|
||||
|
||||
Premier passage `pre.004` reçu le 2026-08-23 :
|
||||
@@ -679,3 +681,55 @@ Critères `pre.007-fix.001` :
|
||||
|
||||
Verdict courant : **`pre.007` FIX REQUIRED ; `pre.007-fix.001` PREPARED ; `pre.008` bloqué.**
|
||||
|
||||
## 19. Replay `pre.007-fix.001` et gate `pre.007-fix.002`
|
||||
|
||||
Résultat opérateur `0.2.8-pre.7.fix.1` :
|
||||
|
||||
```text
|
||||
[x] cargo fmt --all
|
||||
[x] audit Rust workspace = clean / 0 candidate
|
||||
[x] cargo check --workspace = vert, sans warning
|
||||
[x] cargo clippy --workspace --all-targets = vert, sans warning
|
||||
[!] Transport unit = 330 passed / 1 failed sur 331
|
||||
```
|
||||
|
||||
Canaris heartbeat verts :
|
||||
|
||||
```text
|
||||
[x] policy provider-owned Helius + 60 s
|
||||
[x] close avant deadline annule le heartbeat
|
||||
[x] SolanaStandard n'émet aucun heartbeat provider
|
||||
[x] write failure du Ping suit le chemin reconnect existant
|
||||
[x] reconnect réussi réarme depuis la connexion de remplacement
|
||||
```
|
||||
|
||||
Seul échec :
|
||||
|
||||
```text
|
||||
[!] helius_heartbeat_sends_ping_at_sixty_seconds_and_rearms
|
||||
`try_recv()` peut encore précéder la propagation actor/socket/fixture/channel après `advance(1 s)`.
|
||||
```
|
||||
|
||||
Critères `pre.007-fix.002` :
|
||||
|
||||
```text
|
||||
[ ] aucune modification de `src/ws_session.rs`
|
||||
[ ] cadence Helius exacte 60 s inchangée
|
||||
[ ] aucune API/Config/dependency supplémentaire
|
||||
[ ] attente positive bornée par scheduler steps uniquement
|
||||
[ ] aucune nouvelle `advance()`, `sleep()` ou `timeout()` pour attendre le Ping
|
||||
[ ] t=59 s reste strictement sans Ping
|
||||
[ ] Ping observé à t=60 s après propagation scheduler bornée
|
||||
[ ] t=119 s reste strictement sans second Ping
|
||||
[ ] second Ping observé à t=120 s après propagation scheduler bornée
|
||||
[ ] canal Disconnected avant Ping = échec explicite
|
||||
[ ] cargo fmt --all
|
||||
[ ] audit Rust = clean
|
||||
[ ] cargo check = sans warning
|
||||
[ ] cargo clippy = sans warning
|
||||
[ ] Transport = 331 unit + 40 public API + 29 completeness + 4 doctests
|
||||
[ ] cargo test --workspace = vert
|
||||
```
|
||||
|
||||
Verdict courant : **`pre.007` FIX REQUIRED ; `pre.007-fix.001` CHECKPOINT ; `pre.007-fix.002` PREPARED ; `pre.008` bloqué.**
|
||||
|
||||
|
||||
Reference in New Issue
Block a user