# Delta `0.3.14-pre.003` — observabilité latest-value de continuité WebSocket ## Base requise ```text 0.3.14-pre.002-fix.002 workspace.package.version = 0.3.14-pre.2.fix.2 deltas/0.3.14/pre.002-fix.002.md présent ``` ## Gate de la base Le gate opérateur de `0.3.14-pre.002-fix.002` est validé avant ouverture de cette tranche : ```text cargo fmt --all : PASS cargo fmt --all -- --check : PASS audit Rust workspace rules : PASS audit Markdown tables : PASS cargo check --workspace : PASS cargo clippy --workspace --all-targets --all-features -- -D warnings : PASS cargo test -p ksp-worker-raw-transaction-ingest-lib --all-targets --all-features : PASS ``` Le gate Worker comprend notamment : ```text 114 unit tests : PASS cross_layer_completeness : 8 PASS dependency_boundary : 19 PASS hardening : 27 PASS public_api : 20 PASS release_completeness : 5 PASS ``` ## Objectif Implémenter strictement la tranche `pre.003` du plan `035` : ```text ajouter dans Transport une source latest-value sûre pour les snapshots WebSocket typés la déléguer depuis les façades Standard et Helius existantes observer les incidents reconnect/overflow dans les sources Standard Logs, Standard Block et Helius du Worker ancrer un incident sur le dernier slot réellement observé par cette source borner l'incident avec le premier slot post-incident réellement reçu conserver une terminalité conservative tant que les tranches de replay/repair ultérieures ne sont pas présentes ne créer aucun nouveau socket, actor, retry ou repair HTTP ``` Cette tranche ne remplace pas encore un gap WebSocket par une réparation. Elle rend l'incident explicitement observable et bornable en réutilisant exclusivement les snapshots déjà publiés par l'actor Transport existant. ## Transport — source latest-value WebSocket `WsSession` possède déjà un `tokio::sync::watch::Receiver` interne utilisé pour `snapshot()` et `state()`. `pre.003` ajoute `WsSessionSnapshotSource`, un observateur cloneable qui encapsule uniquement un clone de ce receiver et expose : ```text current() -> dernier WsSessionSnapshot sûr wait_for_change() -> prochaine valeur latest-value, ou None si l'actor disparaît ``` Le type ne contient ni URL, ni credentials, ni socket, ni payload de notification. Son `Debug` reste fondé sur la projection sûre existante. `WsSession::snapshot_source()` clone le receiver existant. Les façades : ```text SolanaStandardWsSession HeliusLaserStreamWsSession ``` délèguent cette méthode au même `WsSession` physique. Aucun second runtime WebSocket n'est créé. ## Worker — ancre d'incident WebSocket Le Worker introduit un contrat crate-private `RawTransactionIngestWebSocketIncidentAnchor`. Il conserve uniquement : ```text start_slot end_slot optionnel compteurs de continuity gap / overflow observés au moment de l'incident motifs reconnect et/ou overflow ``` La borne de début respecte le plan `035` : ```text start_slot = dernier slot réellement observé par la source avant l'incident ``` Aucun timestamp, frontier dérivé d'une autre source ou `last_slot + 1` n'est utilisé comme preuve de début. La borne de fin est : ```text end_slot = premier slot réellement reçu par la même source après l'incident ``` Le range reste inclusif. Les validations réutilisent les bornes run-local introduites en `pre.002` et rejettent notamment inversion, dépassement de plage et compteur régressif. Si un reconnect/overflow est observé avant qu'un slot source n'ait été vu, le Worker ne fabrique pas d'historique : l'incident est `source.websocket_incident_unbounded`. ## Worker — observation des snapshots `RawTransactionIngestProcessingFrontierReporter` observe maintenant les `WsSessionSnapshot` Transport et maintient : ```text continuity gap total WebSocket notification overflow total ancre d'incident WebSocket éventuelle projection de lifecycle source ``` Une source redevenue `Active` reste projetée `Reconnecting` tant qu'un incident ouvert n'a pas reçu sa borne post-incident. Cela évite de publier transitoirement une continuité saine alors que le gap n'est pas encore borné. Aucune mutation de la politique de reconnect Transport n'est effectuée par le Worker. ## Standard Logs et Helius transaction Les deux sources réutilisent maintenant `session.snapshot_source()` en parallèle de la réception métier et des tâches d'hydration existantes. Lorsqu'un incident est détecté : ```text le premier signal post-incident borne le range de manière inclusive ce signal est quand même admis dans le coordinator d'hydration existant aucune nouvelle notification n'est admise après cette borne dans pre.003 les signaux/tâches déjà admis sont drainés la source termine ensuite par source.continuity_gap_proven ``` Ce séquencement évite de perdre artificiellement le premier événement post-incident tout en restant fail-closed tant que les tranches de repair ne sont pas implémentées. ## Standard Block La source Standard Block observe le même snapshot latest-value Transport. Le premier bloc post-incident : ```text borne l'incident projette et envoie toutes ses ingresses via l'admission centrale existante marque ensuite le slot settled termine enfin par source.continuity_gap_proven ``` Un bloc sans transaction qualifiée peut quand même fermer la borne de slot et est settled avant la terminalité conservative. ## Yellowstone Le chemin Yellowstone existant reste inchangé fonctionnellement dans cette tranche : ```text aucune mutation Worker de from_slot aucun ownership Worker de SubscribeReplayInfo aucune nouvelle tentative de replay aucune modification du contrat Transport Yellowstone ``` La preuve de replay native et la distinction `replay_attempt` / couverture effective restent réservées à `pre.004`. ## Tests ajoutés/étendus Transport couvre : ```text source latest-value courante publication d'un changement fermeture de la source lorsque l'actor publisher disparaît accessibilité crate-root de WsSessionSnapshotSource présence des délégations Standard/Helius réutilisation du watch actor existant sans second runtime ``` Worker couvre : ```text ancre inclusive et bornée extension monotone d'un incident reconnect/overflow rejet d'un incident sans motif rejet de range inversé ou surdimensionné incident sans slot précédent -> erreur explicite compteurs WebSocket régressifs -> erreur explicite reconnect et overflow successifs -> même ancre la plus ancienne absence de socket/retry/from_slot possédé par le Worker ``` ## Fichiers ajoutés ```text deltas/0.3.14/pre.003.md ``` ## Fichiers modifiés ```text Cargo.toml crates/ksp-onchain-transport-lib/src/lib.rs crates/ksp-onchain-transport-lib/src/ws_protocol_session.rs crates/ksp-onchain-transport-lib/src/ws_session.rs crates/ksp-onchain-transport-lib/tests/public_api.rs crates/ksp-onchain-transport-lib/tests/release_completeness.rs crates/ksp-onchain-transport-lib/unit_tests/ws_session.rs crates/ksp-worker-raw-transaction-ingest-lib/src/continuity.rs crates/ksp-worker-raw-transaction-ingest-lib/src/lib.rs crates/ksp-worker-raw-transaction-ingest-lib/src/runtime_resources.rs crates/ksp-worker-raw-transaction-ingest-lib/tests/hardening.rs crates/ksp-worker-raw-transaction-ingest-lib/tests/release_completeness.rs crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/continuity.rs crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/runtime_resources.rs ``` ## Fichiers supprimés ```text aucun ``` ## Version Cargo Conformément à `VER-ID-009`, cette nouvelle prerelease synchronise la version workspace : ```text header Cargo.toml : 569 -> 570 workspace.package.version : 0.3.14-pre.2.fix.2 -> 0.3.14-pre.3 ``` Versions des fichiers modifiés : ```text ksp-onchain-transport-lib/src/lib.rs : 47 -> 48 ksp-onchain-transport-lib/src/ws_protocol_session.rs : 6 -> 7 ksp-onchain-transport-lib/src/ws_session.rs : 14 -> 15 ksp-onchain-transport-lib/tests/public_api.rs : 52 -> 53 ksp-onchain-transport-lib/tests/release_completeness.rs : 44 -> 45 ksp-onchain-transport-lib/unit_tests/ws_session.rs : 11 -> 12 ksp-worker-raw-transaction-ingest-lib/src/continuity.rs : 3 -> 4 ksp-worker-raw-transaction-ingest-lib/src/lib.rs : 28 -> 29 ksp-worker-raw-transaction-ingest-lib/src/runtime_resources.rs : 29 -> 30 ksp-worker-raw-transaction-ingest-lib/tests/hardening.rs : 26 -> 27 ksp-worker-raw-transaction-ingest-lib/tests/release_completeness.rs : 21 -> 22 ksp-worker-raw-transaction-ingest-lib/unit_tests/continuity.rs : 2 -> 3 ksp-worker-raw-transaction-ingest-lib/unit_tests/runtime_resources.rs : 23 -> 24 ``` ## Frontières préservées ```text aucun repair HTTP aucun nouveau socket WebSocket aucun second actor WebSocket aucun retry Worker aucune nouvelle dépendance aucune nouvelle feature aucun changement Config aucun changement Store aucun changement Job Backfill aucun backend Store physique depuis Worker aucune mutation Worker de Yellowstone from_slot aucun SubscribeReplayInfo possédé par Worker aucun élargissement de la façade publique Worker src/lib.rs Worker reste exempt de ReplayInfo/from_slot/repair/backfill ``` ## Validations exécutées Dans le sandbox de préparation : ```text python3 scripts/audit_rust_workspace_rules.py python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas scan exact ReplayInfo/from_slot/repair/backfill dans Worker src/lib.rs scan de possession WebSocket directe dans Worker scan set_from_slot dans les sources de production Worker comparaison exacte 0.3.14-pre.002-fix.002 -> 0.3.14-pre.003 contrôle des versions de fichier modifiées contrôle du contenu de l'archive delta unzip -t de l'archive delta réapplication de l'archive sur la base puis comparaison byte-exacte ``` ## Validations non exécutées Le sandbox de préparation ne fournit pas le toolchain Cargo/Rust. Les gates suivants restent donc explicitement à exécuter côté opérateur : ```text cargo fmt --all cargo fmt --all -- --check cargo check --workspace cargo clippy --workspace --all-targets --all-features -- -D warnings cargo test -p ksp-onchain-transport-lib --all-targets --all-features cargo test -p ksp-worker-raw-transaction-ingest-lib --all-targets --all-features ``` ## Décisions prises ```text l'observabilité WS réutilise le watch latest-value déjà possédé par Transport le Worker observe mais ne pilote pas la reconnexion WebSocket un gap WS commence au dernier slot source réellement observé, inclusivement le premier slot source post-incident est la borne de fin inclusive aucune histoire antérieure n'est inventée sans ancre source le premier événement post-incident est admis avant la terminalité conservative pre.003 reste fail-closed et ne prétend pas encore réparer le gap ``` ## Questions ouvertes ```text aucune pour pre.003 ``` ## Tranche suivante `pre.004` reste dédiée à la preuve de replay natif Yellowstone : qualification de `SubscribeReplayInfo`, preuve de couverture effective et conservation stricte de l'ownership `from_slot` dans Transport. ## Gate opérateur après application ```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 deltas cargo check --workspace cargo clippy --workspace --all-targets --all-features -- -D warnings cargo test -p ksp-onchain-transport-lib --all-targets --all-features cargo test -p ksp-worker-raw-transaction-ingest-lib --all-targets --all-features ```