0.3.5-alpha.8.fix.1
This commit is contained in:
@@ -28,7 +28,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.3.5-alpha.8"
|
version = "0.3.5-alpha.8.fix.1"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://git.sasedev.com/Sasedev/games"
|
repository = "https://git.sasedev.com/Sasedev/games"
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ Workspace expérimental puis productif pour des jeux multiplateformes principale
|
|||||||
|
|
||||||
Version stable de référence : `0.3.4`.
|
Version stable de référence : `0.3.4`.
|
||||||
|
|
||||||
Version active : `0.3.5-alpha.8`. `0.3.2` reste différée.
|
Version active : `0.3.5-alpha.8.fix.1`. `0.3.2` reste différée.
|
||||||
|
|
||||||
La stable `0.3.4` livre la première baseline realtime : contrat binaire transport-neutral, backend WebSocket Tokio/tokio-tungstenite, limites et deadlines, tests loopback/robustesse, smoke runtime localhost public et frontières de dépendances empêchant moteurs et gameplay de dépendre d'un backend concret. `0.3.5-alpha.8` conserve les chemins WebTransport natif et navigateur validés, puis ajoute une preuve de composition WebTransport-first avec fallback WebSocket strictement classifié. Le fallback n'est autorisé que pour `Timeout`/`Io`; les erreurs ambiguës ou sensibles, notamment `Connect`, `InvalidConfiguration` et `Protocol`, restent visibles et ne déclenchent aucun downgrade silencieux. Les datagrams et les mesures comparatives restent réservés aux tranches suivantes.
|
La stable `0.3.4` livre la première baseline realtime : contrat binaire transport-neutral, backend WebSocket Tokio/tokio-tungstenite, limites et deadlines, tests loopback/robustesse, smoke runtime localhost public et frontières de dépendances empêchant moteurs et gameplay de dépendre d'un backend concret. `0.3.5-alpha.8.fix.1` conserve les chemins WebTransport natif et navigateur validés, puis ajoute une preuve de composition WebTransport-first avec fallback WebSocket strictement classifié. Le fallback n'est autorisé que pour `Timeout`/`Io`; les erreurs ambiguës ou sensibles, notamment `Connect`, `InvalidConfiguration` et `Protocol`, restent visibles et ne déclenchent aucun downgrade silencieux. Les datagrams et les mesures comparatives restent réservés aux tranches suivantes.
|
||||||
|
|
||||||
Les deux premiers jeux sont des POC structurels : `game-reflex-poc` et `game-snake-poc`. Ils existent d'abord pour valider les frontières du workspace, le moteur, les assets et le packaging multiplateforme.
|
Les deux premiers jeux sont des POC structurels : `game-reflex-poc` et `game-snake-poc`. Ils existent d'abord pour valider les frontières du workspace, le moteur, les assets et le packaging multiplateforme.
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
//! Executable localhost smoke for WebTransport-first realtime composition with classified WebSocket fallback.
|
//! Executable localhost smoke for WebTransport-first realtime composition with classified WebSocket fallback.
|
||||||
|
|
||||||
use game_realtime_transport_lib::RealtimeConnection; // rust-rules: trait-import
|
|
||||||
use game_realtime_transport_lib::RealtimeReceiver; // rust-rules: trait-import
|
use game_realtime_transport_lib::RealtimeReceiver; // rust-rules: trait-import
|
||||||
use game_realtime_transport_lib::RealtimeSender; // rust-rules: trait-import
|
use game_realtime_transport_lib::RealtimeSender; // rust-rules: trait-import
|
||||||
|
|
||||||
@@ -107,15 +106,19 @@ async fn run_webtransport_preferred_branch() -> std::result::Result<(), String>
|
|||||||
return session.accept_primary_connection().await;
|
return session.accept_primary_connection().await;
|
||||||
};
|
};
|
||||||
let client = select_preferred_transport(
|
let client = select_preferred_transport(
|
||||||
|| async {
|
|| {
|
||||||
let session = match game_realtime_webtransport_lib::connect(&client_config).await {
|
return async {
|
||||||
std::result::Result::Ok(value) => value,
|
let session = match game_realtime_webtransport_lib::connect(&client_config).await {
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
|
};
|
||||||
|
return session.open_primary_connection().await;
|
||||||
};
|
};
|
||||||
return session.open_primary_connection().await;
|
|
||||||
},
|
},
|
||||||
|| async {
|
|| {
|
||||||
return game_realtime_websocket_lib::connect(UNUSED_WEBSOCKET_ENDPOINT).await;
|
return async {
|
||||||
|
return game_realtime_websocket_lib::connect(UNUSED_WEBSOCKET_ENDPOINT).await;
|
||||||
|
};
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
let (server_result, client_result) = tokio::join!(server, client);
|
let (server_result, client_result) = tokio::join!(server, client);
|
||||||
@@ -146,13 +149,17 @@ async fn run_websocket_fallback_branch() -> std::result::Result<(), String> {
|
|||||||
let endpoint = format!("ws://{}/fallback", listener.local_addr());
|
let endpoint = format!("ws://{}/fallback", listener.local_addr());
|
||||||
let server = listener.accept();
|
let server = listener.accept();
|
||||||
let client = select_preferred_transport(
|
let client = select_preferred_transport(
|
||||||
|| async {
|
|| {
|
||||||
return std::result::Result::<game_realtime_webtransport_lib::WebTransportConnection, game_realtime_transport_lib::TransportError>::Err(
|
return async {
|
||||||
game_realtime_transport_lib::TransportError::new(game_realtime_transport_lib::TransportErrorKind::Timeout, FORCED_FALLBACK_DETAIL),
|
return std::result::Result::<game_realtime_webtransport_lib::WebTransportConnection, game_realtime_transport_lib::TransportError>::Err(
|
||||||
);
|
game_realtime_transport_lib::TransportError::new(game_realtime_transport_lib::TransportErrorKind::Timeout, FORCED_FALLBACK_DETAIL),
|
||||||
|
);
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|| async {
|
|| {
|
||||||
return game_realtime_websocket_lib::connect(endpoint.as_str()).await;
|
return async {
|
||||||
|
return game_realtime_websocket_lib::connect(endpoint.as_str()).await;
|
||||||
|
};
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
let (server_result, client_result) = tokio::join!(server, client);
|
let (server_result, client_result) = tokio::join!(server, client);
|
||||||
@@ -184,15 +191,19 @@ async fn run_non_fallback_error_branch() -> std::result::Result<(), String> {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
let result = select_preferred_transport(
|
let result = select_preferred_transport(
|
||||||
|| async {
|
|| {
|
||||||
let session = match game_realtime_webtransport_lib::connect(&client_config).await {
|
return async {
|
||||||
std::result::Result::Ok(value) => value,
|
let session = match game_realtime_webtransport_lib::connect(&client_config).await {
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
|
};
|
||||||
|
return session.open_primary_connection().await;
|
||||||
};
|
};
|
||||||
return session.open_primary_connection().await;
|
|
||||||
},
|
},
|
||||||
|| async {
|
|| {
|
||||||
return game_realtime_websocket_lib::connect("ws://127.0.0.1:1/non-fallback-must-not-run").await;
|
return async {
|
||||||
|
return game_realtime_websocket_lib::connect("ws://127.0.0.1:1/non-fallback-must-not-run").await;
|
||||||
|
};
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|||||||
@@ -19,34 +19,46 @@ fn fallback_policy_is_narrow_and_excludes_ambiguous_connect_failures() {
|
|||||||
#[tokio::test(flavor = "current_thread")]
|
#[tokio::test(flavor = "current_thread")]
|
||||||
async fn selector_prefers_webtransport_and_falls_back_only_for_classified_errors() {
|
async fn selector_prefers_webtransport_and_falls_back_only_for_classified_errors() {
|
||||||
let preferred = super::select_preferred_transport(
|
let preferred = super::select_preferred_transport(
|
||||||
|| async {
|
|| {
|
||||||
return std::result::Result::<u8, game_realtime_transport_lib::TransportError>::Ok(7);
|
return async {
|
||||||
|
return std::result::Result::<u8, game_realtime_transport_lib::TransportError>::Ok(7);
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|| async {
|
|| {
|
||||||
return std::result::Result::<u8, game_realtime_transport_lib::TransportError>::Err(test_error("fallback must not run"));
|
return async {
|
||||||
|
return std::result::Result::<u8, game_realtime_transport_lib::TransportError>::Err(test_error("fallback must not run"));
|
||||||
|
};
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
assert!(matches!(preferred, std::result::Result::Ok(super::SelectedConnection::WebTransport(7))), "WebTransport success must remain preferred");
|
assert!(matches!(preferred, std::result::Result::Ok(super::SelectedConnection::WebTransport(7))), "WebTransport success must remain preferred");
|
||||||
let fallback = super::select_preferred_transport(
|
let fallback = super::select_preferred_transport(
|
||||||
|| async {
|
|| {
|
||||||
return std::result::Result::<u8, game_realtime_transport_lib::TransportError>::Err(game_realtime_transport_lib::TransportError::new(
|
return async {
|
||||||
game_realtime_transport_lib::TransportErrorKind::Timeout,
|
return std::result::Result::<u8, game_realtime_transport_lib::TransportError>::Err(game_realtime_transport_lib::TransportError::new(
|
||||||
"forced timeout",
|
game_realtime_transport_lib::TransportErrorKind::Timeout,
|
||||||
));
|
"forced timeout",
|
||||||
|
));
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|| async {
|
|| {
|
||||||
return std::result::Result::<u8, game_realtime_transport_lib::TransportError>::Ok(9);
|
return async {
|
||||||
|
return std::result::Result::<u8, game_realtime_transport_lib::TransportError>::Ok(9);
|
||||||
|
};
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
assert!(matches!(fallback, std::result::Result::Ok(super::SelectedConnection::WebSocket(9))), "classified timeout must select WebSocket fallback");
|
assert!(matches!(fallback, std::result::Result::Ok(super::SelectedConnection::WebSocket(9))), "classified timeout must select WebSocket fallback");
|
||||||
let non_fallback = super::select_preferred_transport(
|
let non_fallback = super::select_preferred_transport(
|
||||||
|| async {
|
|| {
|
||||||
return std::result::Result::<u8, game_realtime_transport_lib::TransportError>::Err(test_error("visible primary error"));
|
return async {
|
||||||
|
return std::result::Result::<u8, game_realtime_transport_lib::TransportError>::Err(test_error("visible primary error"));
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|| async {
|
|| {
|
||||||
return std::result::Result::<u8, game_realtime_transport_lib::TransportError>::Ok(11);
|
return async {
|
||||||
|
return std::result::Result::<u8, game_realtime_transport_lib::TransportError>::Ok(11);
|
||||||
|
};
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|||||||
123
deltas/0.3.5/alpha.8.fix.1.md
Normal file
123
deltas/0.3.5/alpha.8.fix.1.md
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
<!-- file: deltas/0.3.5/alpha.8.fix.1.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta 0.3.5-alpha.8.fix.1
|
||||||
|
|
||||||
|
## Base requise
|
||||||
|
|
||||||
|
`0.3.5-alpha.8`.
|
||||||
|
|
||||||
|
La gate utilisateur du 2026-09-22 valide :
|
||||||
|
|
||||||
|
- fmt et les trois audits ;
|
||||||
|
- `cargo check --workspace` ;
|
||||||
|
- les suites `game-realtime-transport-lib`, `game-realtime-websocket-lib` et `game-realtime-webtransport-lib` ;
|
||||||
|
- les deux tests du launcher fallback ;
|
||||||
|
- le smoke WebSocket ;
|
||||||
|
- le smoke WebTransport ;
|
||||||
|
- le smoke de composition fallback, terminé par `game-realtime-transport-fallback-smoke: PASS` ;
|
||||||
|
- les graphes de dépendances attendus.
|
||||||
|
|
||||||
|
La gate complète échoue uniquement sur Clippy strict dans `game-realtime-transport-fallback-smoke` :
|
||||||
|
|
||||||
|
```text
|
||||||
|
unused import: game_realtime_transport_lib::RealtimeConnection
|
||||||
|
missing return statement
|
||||||
|
```
|
||||||
|
|
||||||
|
Les erreurs `implicit-return` portent uniquement sur des closures qui retournent une future `async`.
|
||||||
|
|
||||||
|
`alpha.8` n'est donc pas historisée comme validée avant ce correctif.
|
||||||
|
|
||||||
|
## Cause
|
||||||
|
|
||||||
|
Le launcher importe explicitement `RealtimeConnection` alors que ses bounds utilisent le chemin qualifié complet : l'import n'est jamais consommé.
|
||||||
|
|
||||||
|
En parallèle, la règle workspace `clippy::implicit-return` s'applique aussi à la valeur de retour des closures. Les formes :
|
||||||
|
|
||||||
|
```rust
|
||||||
|
|| async { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
doivent donc être écrites :
|
||||||
|
|
||||||
|
```rust
|
||||||
|
|| return async { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
même lorsque le corps `async` contient déjà ses propres `return` explicites.
|
||||||
|
|
||||||
|
## Correction
|
||||||
|
|
||||||
|
La version technique passe à :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.3.5-alpha.8.fix.1
|
||||||
|
```
|
||||||
|
|
||||||
|
Le correctif :
|
||||||
|
|
||||||
|
- supprime l'import inutilisé `RealtimeConnection` de `src/main.rs` ;
|
||||||
|
- rend explicite le `return` de toutes les closures async du sélecteur dans `src/main.rs` ;
|
||||||
|
- applique la même forme aux closures async des tests unitaires.
|
||||||
|
|
||||||
|
Aucun comportement runtime, backend, contrat, mapping d'erreur, endpoint, framing, TLS/pinning ou dépendance n'est modifié.
|
||||||
|
|
||||||
|
La politique de fallback reste strictement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Timeout -> fallback autorisé
|
||||||
|
Io -> fallback autorisé
|
||||||
|
Connect -> fallback interdit
|
||||||
|
```
|
||||||
|
|
||||||
|
et toutes les autres catégories restent non-fallback.
|
||||||
|
|
||||||
|
## Fichiers modifiés
|
||||||
|
|
||||||
|
```text
|
||||||
|
Cargo.toml
|
||||||
|
README.md
|
||||||
|
crates/apps/game-realtime-transport-fallback-smoke/src/main.rs
|
||||||
|
crates/apps/game-realtime-transport-fallback-smoke/unit_tests/fallback.rs
|
||||||
|
docs/plans/005-V0_3_5_WEBTRANSPORT_QUIC_POC_PLAN.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Nouveau fichier :
|
||||||
|
|
||||||
|
```text
|
||||||
|
deltas/0.3.5/alpha.8.fix.1.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Aucun fichier `history/0.3.5/alpha.8.md` n'est créé avant une gate entièrement verte.
|
||||||
|
|
||||||
|
## Plan
|
||||||
|
|
||||||
|
Le forecast fonctionnel ne change pas. Après validation de ce fix, `0.3.5-alpha.9` reste la tranche suivante pour le POC datagram isolé si elle demeure justifiée par l'API/backend réellement disponibles.
|
||||||
|
|
||||||
|
## Validation attendue
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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 Android Web deltas history
|
||||||
|
python3 scripts/audit_distribution_layout.py
|
||||||
|
|
||||||
|
cargo check --workspace
|
||||||
|
cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||||
|
|
||||||
|
cargo test -p game-realtime-transport-fallback-smoke --all-targets --all-features
|
||||||
|
cargo run -p game-realtime-transport-fallback-smoke
|
||||||
|
```
|
||||||
|
|
||||||
|
Le smoke doit terminer par :
|
||||||
|
|
||||||
|
```text
|
||||||
|
game-realtime-transport-fallback-smoke: PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Après validation
|
||||||
|
|
||||||
|
Créer `history/0.3.5/alpha.8.fix.1.md` à partir des résultats réellement fournis, puis ouvrir directement `0.3.5-alpha.9` si le POC datagram reste pertinent.
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
<!-- file: docs/plans/005-V0_3_5_WEBTRANSPORT_QUIC_POC_PLAN.md -->
|
<!-- file: docs/plans/005-V0_3_5_WEBTRANSPORT_QUIC_POC_PLAN.md -->
|
||||||
<!-- version: 12 -->
|
<!-- version: 13 -->
|
||||||
|
|
||||||
# Plan 0.3.5 — POC WebTransport/QUIC et fallback WebSocket
|
# Plan 0.3.5 — POC WebTransport/QUIC et fallback WebSocket
|
||||||
|
|
||||||
## Statut
|
## Statut
|
||||||
|
|
||||||
Plan actif créé pendant `0.3.5-alpha.1` à partir de l'archive taggée `v0.3.4`, puis réconcilié pour l'implémentation native de `0.3.5-alpha.2`, son correctif de validation `0.3.5-alpha.2.fix.1`, le chemin fiable validé de `0.3.5-alpha.3`, la robustesse validée par `0.3.5-alpha.4.fix.1`, le smoke natif validé de `0.3.5-alpha.5`, le chemin client WASM validé de `0.3.5-alpha.6`, le smoke navigateur validé par `0.3.5-alpha.7.fix.2` et la composition/fallback WebSocket candidate de `0.3.5-alpha.8`.
|
Plan actif créé pendant `0.3.5-alpha.1` à partir de l'archive taggée `v0.3.4`, puis réconcilié pour l'implémentation native de `0.3.5-alpha.2`, son correctif de validation `0.3.5-alpha.2.fix.1`, le chemin fiable validé de `0.3.5-alpha.3`, la robustesse validée par `0.3.5-alpha.4.fix.1`, le smoke natif validé de `0.3.5-alpha.5`, le chemin client WASM validé de `0.3.5-alpha.6`, le smoke navigateur validé par `0.3.5-alpha.7.fix.2` et la composition/fallback WebSocket corrigée par `0.3.5-alpha.8.fix.1`.
|
||||||
|
|
||||||
Le cadrage détaillé et la comparaison des stacks actuelles sont conservés dans `docs/studies/026-V0_3_5_WEBTRANSPORT_QUIC_STACK_AUDIT.md`. Le présent document porte les décisions opérationnelles, le scope, les gates et le forecast vivant de la version.
|
Le cadrage détaillé et la comparaison des stacks actuelles sont conservés dans `docs/studies/026-V0_3_5_WEBTRANSPORT_QUIC_STACK_AUDIT.md`. Le présent document porte les décisions opérationnelles, le scope, les gates et le forecast vivant de la version.
|
||||||
|
|
||||||
@@ -535,6 +535,8 @@ Tranche candidate matérialisée avec :
|
|||||||
|
|
||||||
Le smoke navigateur `alpha.7` reste inchangé et continue d'échouer explicitement si WebTransport échoue ; le fallback n'existe que dans le launcher de composition `alpha.8`.
|
Le smoke navigateur `alpha.7` reste inchangé et continue d'échouer explicitement si WebTransport échoue ; le fallback n'existe que dans le launcher de composition `alpha.8`.
|
||||||
|
|
||||||
|
Correctif `0.3.5-alpha.8.fix.1` : la gate utilisateur de `alpha.8` valide fmt, audits, `cargo check --workspace`, les suites transport/WebSocket/WebTransport, les trois smokes runtime et les graphes de dépendances. Le smoke fallback termine bien par `PASS`, mais Clippy strict échoue uniquement sur un import de trait devenu inutilisé et sur les closures `|| async { ... }` qui doivent respecter la règle workspace `clippy::implicit-return`. Le correctif retire l'import inutile et écrit explicitement `|| return async { ... }` dans le launcher et ses tests, sans modifier la politique de fallback ni aucun backend.
|
||||||
|
|
||||||
### `0.3.5-alpha.9` — datagram POC isolé
|
### `0.3.5-alpha.9` — datagram POC isolé
|
||||||
|
|
||||||
Uniquement si le backend fiable et le navigateur sont suffisamment stables :
|
Uniquement si le backend fiable et le navigateur sont suffisamment stables :
|
||||||
|
|||||||
Reference in New Issue
Block a user