v0.2.7-pre.006-fix.001

This commit is contained in:
2026-08-22 20:08:55 +02:00
parent 8721e54b18
commit 435126f67a
4 changed files with 145 additions and 5 deletions

View File

@@ -1,12 +1,12 @@
# file: Cargo.toml
# version: 199
# version: 200
[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.6"
version = "0.2.7-pre.6.fix.1"
edition = "2024"
license = "MIT"
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-onchain-transport-lib/src/ws_session.rs
// version: 4
// version: 5
use futures_util::SinkExt; // rust-rules: trait-import
use futures_util::StreamExt; // rust-rules: trait-import
@@ -531,7 +531,7 @@ where
return WsActorIoOutcome::Continue;
},
};
let (state_tx, state_rx) = tokio::sync::watch::channel(crate::WsSubscriptionState::Requested);
let (state_tx, _) = tokio::sync::watch::channel(crate::WsSubscriptionState::Requested);
subscriptions.insert(
subscription_id.get(),
crate::WsSubscriptionRuntime {

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-onchain-transport-lib/unit_tests/ws_session.rs
// version: 3
// version: 4
use futures_util::SinkExt; // rust-rules: trait-import
use futures_util::StreamExt; // rust-rules: trait-import
@@ -493,6 +493,8 @@ async fn websocket_unknown_remote_subscription_notification_is_ignored_without_a
send_result(&mut websocket, &subscribe, serde_json::json!(3)).await;
send_notification(&mut websocket, "rootNotification", 999, serde_json::json!(100)).await;
send_notification(&mut websocket, "rootNotification", 3, serde_json::json!(101)).await;
let request = read_request(&mut websocket).await;
send_result(&mut websocket, &request, serde_json::json!(true)).await;
});
let session = crate::WsSession::connect(local_endpoint(url.as_str())).await.expect("client handshake must succeed");
let mut subscription = session
@@ -504,6 +506,9 @@ async fn websocket_unknown_remote_subscription_notification_is_ignored_without_a
let notification = subscription.recv().await.expect("valid notification must exist").expect("valid notification must decode");
assert_eq!(notification, serde_json::json!(101));
assert_eq!(subscription.state(), crate::WsSubscriptionState::Active);
assert_eq!(session.state(), crate::WsSessionState::Active);
let result = session.execute_json_rpc("afterUnknownRemote", std::vec::Vec::new()).await.expect("physical session must remain usable");
assert_eq!(result, serde_json::json!(true));
server.await.expect("local server task must complete");
}
@@ -543,6 +548,8 @@ async fn websocket_typed_notification_decode_failure_fails_only_one_subscription
let subscribe = read_request(&mut websocket).await;
send_result(&mut websocket, &subscribe, serde_json::json!(8)).await;
send_notification(&mut websocket, "rootNotification", 8, serde_json::json!("not-a-slot")).await;
let request = read_request(&mut websocket).await;
send_result(&mut websocket, &request, serde_json::json!(true)).await;
});
let session = crate::WsSession::connect(local_endpoint(url.as_str())).await.expect("client handshake must succeed");
let mut subscription = session
@@ -560,5 +567,7 @@ async fn websocket_typed_notification_decode_failure_fails_only_one_subscription
assert_eq!(error.code(), crate::ERROR_CODE_INVALID_RESPONSE);
wait_for_subscription_state(&subscription, crate::WsSubscriptionState::Failed).await;
assert_eq!(session.state(), crate::WsSessionState::Active);
let result = session.execute_json_rpc("afterDecodeFailure", std::vec::Vec::new()).await.expect("physical session must remain usable");
assert_eq!(result, serde_json::json!(true));
server.await.expect("local server task must complete");
}

View File

@@ -0,0 +1,131 @@
<!-- file: deltas/0.2.7/pre.006-fix.001.md -->
<!-- version: 1 -->
# Delta `0.2.7-pre.006-fix.001` — synchronisation des canaris d'isolation WS + warning watch
## 1. Base requise
```text
0.2.7-pre.006 appliqué
workspace.package.version = 0.2.7-pre.6
```
Le checkpoint opérateur de `pre.006` a établi :
```text
cargo fmt --all exécuté
python3 scripts/audit_rust_workspace_rules.py clean
cargo check --workspace réussi avec 1 warning
cargo clippy --workspace --all-targets réussi avec le même warning
cargo test -p ksp-onchain-transport-lib 273 réussis, 2 échoués
```
Les deux échecs concernent les canaris :
```text
unknown remote subscription id -> session attendue Active
typed notification decode failure -> session attendue Active
```
## 2. Diagnostic
Les chemins runtime concernés dans `handle_subscription_notification` renvoient déjà `WsActorIoOutcome::Continue` :
- un remote subscription ID inconnu ou stale est ignoré sans modifier la session ;
- une erreur du decoder typed ferme uniquement la subscription locale en `Failed`.
Le `Failed` observé par les deux tests provenait ensuite du fixture serveur local : après l'envoi des notifications, la tâche serveur se terminait immédiatement et abandonnait le socket sans Close frame. Le contrat de session acquis classe correctement cette fin physique sans Close comme une défaillance de connexion.
Le test mélangeait donc deux événements distincts : l'anomalie logique à isoler et une déconnexion physique ultérieure.
## 3. Correctif des canaris
Les deux fixtures maintiennent désormais le peer local ouvert après l'anomalie et attendent une requête JSON-RPC témoin :
```text
afterUnknownRemote
afterDecodeFailure
```
Le caller vérifie successivement :
1. le résultat attendu de la notification ou de l'erreur typed ;
2. l'état logique attendu de la subscription ;
3. `WsSessionState::Active` ;
4. la réussite d'une nouvelle requête JSON-RPC sur la même session physique.
Le serveur peut seulement terminer ensuite.
Cette synchronisation supprime la course du fixture et prouve plus fortement la propriété recherchée : la session reste réellement utilisable après une anomalie locale de subscription.
## 4. Warning `state_rx`
Le receiver initial créé uniquement pour construire le `watch::Sender` n'est pas consommé par l'actor. Le binding inutile :
```text
state_rx
```
est supprimé. Le handle public continue à recevoir son propre `watch::Receiver` lors de l'ACK subscribe via `state_tx.subscribe()`.
Aucune lifecycle state, API ou politique de notification n'est modifiée.
## 5. Signal technique
Le fix modifie du Rust et des tests. Conformément aux règles de version KSP :
```text
livraison = 0.2.7-pre.006-fix.001
workspace.package.version = 0.2.7-pre.6.fix.1
commit = v0.2.7-pre.006-fix.001
```
Aucun tag prerelease.
## 6. Fichiers modifiés
```text
Cargo.toml
crates/ksp-onchain-transport-lib/src/ws_session.rs
crates/ksp-onchain-transport-lib/unit_tests/ws_session.rs
```
## 7. Fichier ajouté
```text
deltas/0.2.7/pre.006-fix.001.md
```
## 8. Fichiers supprimés
Aucun.
## 9. Invariants préservés
Le fix ne change pas :
- `WsSession` ou `WsSubscription<T>` publics ;
- les IDs locaux ou remote ;
- le mapping remote vers local ;
- les neuf familles standard ;
- la capacité des channels ;
- les règles unsubscribe ;
- la classification d'une vraie perte physique comme `Failed` ;
- les frontières de dépendances ;
- la façade de logging KSP ;
- le scope reconnect/resubscribe réservé à `pre.007`.
## 10. Validation sandbox
Le sandbox ne permet pas de revendiquer les gates Cargo opérateur. Les validations statiques disponibles sont exécutées sur le workspace reconstruit avec ce fix.
## 11. Validation opérateur attendue
```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
```