# Delta 0.3.13-pre.006 — HTTP live block polling run-local ## Base requise ```text livraison précédente : 0.3.13-pre.005-fix.001 Cargo base : 0.3.13-pre.5.fix.1 delta base : deltas/0.3.13/pre.005-fix.001.md archive delta base : ksp-general-0.3.13-pre.005-fix.001.zip ``` Le gate opérateur reçu le 10 septembre 2026 sur cette base est entièrement vert pour les commandes exécutées : `cargo fmt`, `cargo fmt --check`, audits Rust/Markdown, `cargo check --workspace`, Clippy strict et toutes les suites de `ksp-worker-raw-transaction-ingest-lib` (`84` unit, `4` cross-layer, `12` dependency-boundary, `19` hardening, `13` public-api, `4` release-completeness, `0` doc-test). Cette exécution ne contenait ni le test Transport ni les commandes `cargo tree`; ils ne sont donc pas déclarés PASS pour ce gate précis. ## Objectif Ajouter la source HTTP live prévue par la tranche sans la transformer en Backfill : ```text getSlot(commitment) -> borne inférieure du run getBlocksWithLimit(next_scan_slot, bounded_limit, commitment) -> découverte des blocs disponibles getBlock observed(slot, Full/Base64/maxV1/rewards=false) -> Common RAW direct Legacy/V0/V1 -> admission centrale Worker -> Store ``` Le polling ne remonte jamais avant la borne observée au démarrage et ne crée aucun checkpoint durable inter-run. ## Version ```text livraison : 0.3.13-pre.006 workspace.package.version : 0.3.13-pre.6 archive : ksp-general-0.3.13-pre.006.zip ``` `Cargo.toml` passe également de la version de fichier `549` à `550`. ## Source HTTP Block Polling Nouvelle surface publique Worker : ```text RawTransactionIngestHttpBlockPollingSource RawTransactionIngestRuntimeResources::from_http_block_polling_source RawTransactionIngestRuntimeResources::try_push_http_block_polling_source DEFAULT_RAW_TRANSACTION_INGEST_HTTP_POLL_INTERVAL MIN_RAW_TRANSACTION_INGEST_HTTP_POLL_INTERVAL MAX_RAW_TRANSACTION_INGEST_HTTP_POLL_INTERVAL DEFAULT_RAW_TRANSACTION_INGEST_HTTP_POLL_MAX_BLOCKS_PER_CYCLE MIN_RAW_TRANSACTION_INGEST_HTTP_POLL_MAX_BLOCKS_PER_CYCLE MAX_RAW_TRANSACTION_INGEST_HTTP_POLL_MAX_BLOCKS_PER_CYCLE ``` La construction caller-owned reçoit un `HttpTransportPool`, un `HttpRoleName` et un commitment `Confirmed` ou `Finalized`. Elle est sans I/O et vérifie statiquement que le rôle possède des routes compatibles `getSlot`, `getBlocksWithLimit` et `getBlock` sur un même réseau. Bornes publiques : ```text poll interval : défaut 1 s ; min 100 ms ; max 30 s max discovered blocks/cycle : défaut 128 ; min 1 ; max 1024 ``` Les limitations physiques, la sélection d'endpoint, le rate-limit, les retries et le backoff HTTP restent possédés par `ksp-onchain-transport-lib`. ## Borne de run et progression Au démarrage : ```text start_slot = getSlot(commitment) next_scan_slot = start_slot ``` Le Worker ne demande aucun slot inférieur à `start_slot`. Pour chaque cycle, il relit le tip courant, puis appelle `getBlocksWithLimit` depuis `next_scan_slot`. La réponse doit être strictement croissante et ne peut contenir aucun slot inférieur à la frontier de scan. Chaque slot listé est matérialisé par `getBlock observed`. Si le résultat vaut `null` : ```text next_scan_slot = slot aucun settled aucun Common RAW reprise au cycle suivant ``` Le cas `null` n'est donc ni assimilé à un bloc vide ni converti en progression silencieuse. Les slots non listés par `getBlocksWithLimit` sont traités comme non produits/skipped dans le run courant. Lorsque la liste reçue ne remplit pas le batch demandé et qu'aucun slot n'est bloqué par `null`, la frontier peut rejoindre le tip observé du cycle sans lancer de scan historique implicite. ## RAW-direct et version Le poller demande exactement : ```text encoding = Base64 transactionDetails = Full maxSupportedTransactionVersion = 1 showRewards = false commitment = Confirmed | Finalized ``` Chaque transaction qualifiée produit directement `RawTransactionMaterial` avec : ```text signature embarquée dans le wire Base64 slot blockTime meta version Legacy | 0 | 1 transactionIndex dérivé de la position dans le bloc ``` Les cas suivants sont fail-closed : transaction non Base64, transactions omises/nulles, version omise/nulle, version `>1`, signature invalide ou index non représentable en `u32`. La voie HTTP Block Polling est RAW-direct et n'utilise pas le coordinateur d'hydration. Le slot n'est marqué `settled` qu'après l'envoi réussi de toutes les transactions du bloc vers l'admission centrale ; `hydration_pending` n'est jamais artificiellement incrémenté. ## Provenance observée La provenance de chaque transaction reprend l'endpoint et le provider réellement gagnants de `getBlock observed` : ```text origin = Live protocol = solana_http method = block_polling_get_block endpoint = endpoint observé provider = provider observé commitment = source commitment capture session = WorkerId ``` Aucun URL, header HTTP ou corps brut n'est exposé par `HttpObservedValue` ou les diagnostics Worker. ## Identité logique Le `source_key` HTTP polling est dérivé de : ```text domain KSP live-source v1 family = http_block_polling network polling role commitment private route-profile fingerprint SHA-256 -> [u8; 32] ``` L'empreinte de profil est construite à partir des identités sûres cluster/provider/endpoint et des capacités pertinentes de chaque route active, après tri déterministe. La cadence et le batch de découverte n'entrent volontairement pas dans l'identité logique : modifier un réglage de timing ne crée pas une deuxième source conceptuelle. Le bound global `1..32`, l'invariant réseau unique et le rejet transactionnel des doublons de `pre.002` s'appliquent à cette famille. ## Activation runtime Le contrat reste volontairement mono-source avant `pre.007` : ```text 1 source Yellowstone : productif 1 source Standard Logs : productif 1 source Standard Block : productif 1 source Helius Transaction : productif 1 source HTTP Block Polling : productif 2..32 sources : composition validable start 2..32 : runtime_resources.multi_source_activation_pending avant spawn ``` Aucune source configurée n'est ignorée silencieusement. ## Preuves ajoutées Unit tests Worker : ```text bornes cadence/batch et commitment présence exacte des capacités HTTP requises source_key stable malgré changement de timing et différent si route logique différente redaction URL sensible dans Debug validation stricte de la découverte croissante qualification Legacy/V0/V1 et rejet version ambiguë/future rejet transactionnel d'une identité HTTP polling dupliquée ``` Canaris externes : ```text public_api : constructeurs/from/push et bornes publiques disponibles depuis crate root dependency_boundary : Transport facade uniquement, aucun Config/Backfill/backend/direct HTTP client hardening : run-local, timer borné, getBlock observed unique, aucun task par tick, aucun hydration_pending direct release_completeness : exports et canaris pre.006 obligatoires ``` Les anciens canaris qui interdisaient globalement `get_block_observed` ou `tokio::time::sleep` sont resserrés sur le coordinateur d'hydration : ces primitives restent interdites dans l'hydration mais sont désormais légitimes, explicites et bornées dans la seule source HTTP polling. ## Documentation `README.md`, le README du crate, `USAGE.md`, le plan et le journal de validation sont alignés sur la cinquième famille productive. `USAGE.md` reste version-neutral : il décrit la construction et le comportement sans journaliser le numéro de prerelease. Une incohérence du README de crate, qui classait encore le live HTTP polling parmi les éléments hors périmètre, est corrigée dans cette tranche. ## Fichiers modifiés ```text Cargo.toml README.md crates/ksp-worker-raw-transaction-ingest-lib/README.md crates/ksp-worker-raw-transaction-ingest-lib/USAGE.md 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/dependency_boundary.rs crates/ksp-worker-raw-transaction-ingest-lib/tests/hardening.rs crates/ksp-worker-raw-transaction-ingest-lib/tests/public_api.rs crates/ksp-worker-raw-transaction-ingest-lib/tests/release_completeness.rs crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/runtime_resources.rs docs/plans/034-V0_3_13_MULTI_SOURCE_LIVE_CONVERGENCE_PLAN.md docs/validation/030-V0_3_13_MULTI_SOURCE_LIVE_CONVERGENCE.md ``` ## Fichiers ajoutés ```text deltas/0.3.13/pre.006.md ``` ## Fichiers supprimés ```text aucun ``` ## Non-claims `pre.006` ne prétend pas : ```text supervision simultanée de plusieurs sources checkpoint durable du polling Backfill implicite scan de slots antérieurs au start_slot du run retry/backoff HTTP implémenté dans Worker client reqwest direct dans Worker Config lu dans Worker getBlock=null assimilé à un bloc vide transaction version >1 ou ambiguë acceptée modification de ksp-onchain-transport-lib nouvelle dépendance externe ``` ## Validation dans l'environnement d'assemblage Le toolchain Rust/Cargo/Rustfmt n'est pas disponible dans l'environnement d'assemblage. Les commandes Cargo post-`pre.006` restent donc `NON EXÉCUTÉ LOCAL`. Les audits statiques KSP, le scan normatif, le contrôle des versions de fichiers et le diff exhaustif sont exécutés avant packaging. ## Gate opérateur requis avant pre.007 ```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 cargo test -p ksp-worker-raw-transaction-ingest-lib cargo tree -p ksp-worker-raw-transaction-ingest-lib --edges normal cargo tree -p ksp-worker-raw-transaction-ingest-lib -e features cargo tree --duplicates ``` `pre.007` ne doit pas commencer si ce gate révèle une anomalie. ## Résultats statiques finaux de l'assemblage Les valeurs ci-dessous sont mises à jour après la dernière passe d'audits sur l'état exact livré. ```text General Rust rule audit: clean Rust export completeness audit: 0 candidate(s) KSP workspace Rust rule audit: clean Markdown table audit: clean (340 tables, 839 files) Normative rule definitions: 489 Unique normative IDs: 489 Duplicates: 0 ``` ## Contrôle exhaustif avant packaging Comparaison avec la base `0.3.13-pre.005-fix.001` : ```text added : 1 deltas/0.3.13/pre.006.md modified : 13 Cargo.toml README.md crates/ksp-worker-raw-transaction-ingest-lib/README.md crates/ksp-worker-raw-transaction-ingest-lib/USAGE.md 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/dependency_boundary.rs crates/ksp-worker-raw-transaction-ingest-lib/tests/hardening.rs crates/ksp-worker-raw-transaction-ingest-lib/tests/public_api.rs crates/ksp-worker-raw-transaction-ingest-lib/tests/release_completeness.rs crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/runtime_resources.rs docs/plans/034-V0_3_13_MULTI_SOURCE_LIVE_CONVERGENCE_PLAN.md docs/validation/030-V0_3_13_MULTI_SOURCE_LIVE_CONVERGENCE.md deleted : 0 ``` Les `13/13` fichiers existants modifiés portent une version de fichier exactement `+1`. Le diff de `Cargo.toml` est limité au header `549 -> 550` et à `workspace.package.version = "0.3.13-pre.6"`; aucun membre workspace ni aucune dépendance n'est modifié. La revue statique ciblée confirme dans le code Worker : ```text get_block_observed( : 1 occurrence, source HTTP polling uniquement get_blocks_with_limit( : 1 occurrence, source HTTP polling uniquement tokio::time::sleep : 1 occurrence, cadence du poller uniquement aucun reqwest/tokio-tungstenite/tonic/yellowstone-grpc-proto direct aucun ksp-config-lib/ksp-job-backfill-lib/ksp-store-postgres-lib direct aucun unbounded_channel ```