Files
khadhroony-solana-project/docs/validation/032-V0_3_15_RAW_TRANSACTION_INGEST_DESK.md
2026-09-19 08:36:52 +02:00

80 KiB
Raw Permalink Blame History

Validation v0.3.15 — Raw Transaction Ingest Desk

1. Rôle

Ce document enregistre les preuves et non-claims de 0.3.15. En pre.001, aucune crate Desk, aucun Start/Stop Worker et aucune extension lower-layer ne sont encore implémentés : la tranche est exclusivement le gate de lecture, audit, brainstorming, external re-audit, sizing et planification imposé par prompts/034-V0_3_15_START_PROMPT.md.

2. Archive stable contrôlée

archive : khadhroony-solana-project-v0.3.14.zip
SHA-256 : 897bd30b0d06c00abc120a7459250b78c11629e84825ac29adbe151d60a668ed
bytes : 8591925
entries : 2046
files : 1851
absolute entries : 0
parent traversal entries : 0
duplicate entries : 0
symlink entries : 0
workspace members : 21
crate manifests : 21
workspace.package.version : 0.3.14
edition : 2024
stable delta : deltas/0.3.14/rel.001.md

Artefacts interdits recherchés :

Cargo.lock : absent
package-lock.json : absent
pnpm-lock.yaml : absent
yarn.lock : absent
rust-toolchain.toml : absent
.env : absent
target/ : absent
node_modules/ : absent
dist/ : absent
.git/ : absent

Résultat archive : PASS.

L'absence de .git empêche de comparer l'objet du tag v0.3.14; aucune équivalence cryptographique au tag n'est revendiquée.

3. Preuve opérateur stable fournie

Le journal opérateur joint à la demande contient notamment l'exécution stable 0.3.14 de :

cargo clean
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-worker-raw-transaction-ingest-lib --all-targets --all-features
cargo test --workspace --all-targets --all-features
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

Les sorties fournies montrent notamment :

General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
Markdown table audit: clean (340 table(s), 886 file(s))
Worker unit tests: 152 passed / 0 failed
Worker cross-layer completeness: 12 passed / 0 failed
Worker dependency boundary: 21 passed / 0 failed
Worker hardening: 38 passed / 0 failed
Worker public API: 22 passed / 0 failed
Worker release completeness: 15 passed / 0 failed

Le journal continue avec le gate workspace stable et ses tests. Cette preuve qualifie l'entrée 0.3.14; elle n'est pas réutilisée comme prétendue exécution post-bump 0.3.15-pre.1.

4. Audits locaux de la base avant modification

Exécuté localement :

python3 scripts/audit_rust_workspace_rules.py

Résultat :

General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean

Exécuté localement :

python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas

Résultat :

Markdown table audit: clean (340 table(s), 886 file(s))

cargo/rustfmt ne sont pas présents dans l'environnement d'assemblage courant. Aucune commande Cargo locale n'est déclarée PASS.

5. Audit des règles

Documents relus :

RULES.md
ROADMAP.md
CHANGELOG.md
docs/000-README.md
docs/rules/RULES_GENERAL.md
docs/rules/RULES_KSP.md
docs/rules/RULES_RUST.md
docs/rules/RULES_DEPENDENCIES.md
docs/rules/RULES_DOCUMENTATION.md
docs/rules/FILE_CONTRACTS.md
docs/rules/VERSION_WORKFLOW.md
docs/rules/PROMPT_STRUCTURE.md

Scan des définitions normatives :

489 définitions
489 IDs uniques
0 doublon

Les deltas publiés antérieurs ne sont pas réécrits. Le gate pre.001 modifie uniquement Cargo.toml et ajoute les trois livrables exigés.

6. Handoff stable et Worker audités

Les documents 0.3.14, README/USAGE Worker et les sources/tests Worker ont été audités.

API publique pertinente confirmée :

RawTransactionIngestRuntimeResources
RawTransactionIngestYellowstoneSource
RawTransactionIngestStandardLogsSource
RawTransactionIngestStandardBlockSource
RawTransactionIngestHeliusTransactionSource
RawTransactionIngestHttpBlockPollingSource
RawTransactionIngestWorker::start_with_runtime_resources
RawTransactionIngestHandle
RawTransactionIngestSnapshotSource

Frontières confirmées :

Worker -X-> Config
Worker -X-> Job Backfill
Worker -X-> ksp-store-postgres-lib
Worker -> ksp-store-lib
Worker -> ksp-onchain-transport-lib

Plusieurs Workers peuvent recevoir le même Arc<Store> ; leurs lifecycle/gaps/TargetCoverage restent indépendants.

7. Requirements exacts des cinq familles Worker

Route V1 Network Required capability Optional capability Config evidence Worker resource Composable aujourdhui ? Missing contract Live-testable ?
yellowstone-hydrated réseau du profil Yellowstone gRPC Block Subscribe + HTTP getBlock même réseau reconciliation HTTP bloc multi-provider gRPC + HTTP role RawTransactionIngestYellowstoneSource oui sur profil complet et secrets résolus aucun contrat essentiel provider-gated selon profil
standard-logs-hydrated réseau du profil WS Solana standard logsSubscribe + HTTP getTransaction scan HTTP repair si rôle compatible WS + HTTP role RawTransactionIngestStandardLogsSource non prouvable strictement avant capability WS explicite Transport + Config + enforcement Worker minimaux oui, notamment Devnet keyless après extension
standard-block-direct réseau du profil WS Solana standard blockSubscribe explicitement déclaré aucune hydration obligatoire WS capability Block RawTransactionIngestStandardBlockSource non prouvable aujourdhui Transport + Config + enforcement Worker minimaux seulement endpoint prouvé compatible
helius-transaction-hydrated réseau du profil WS Helius transactionSubscribe + HTTP getTransaction scan HTTP repair si rôle compatible Helius WS + HTTP role RawTransactionIngestHeliusTransactionSource non dans les profils committed actuels Config data/composite + capability WS explicite provider-gated Helius
http-block-polling réseau du profil HTTP getSlot + getBlocksWithLimit + getBlock getTransaction pour known-reference hydration HTTP role RawTransactionIngestHttpBlockPollingSource oui lorsque le rôle résolu annonce les trois méthodes aucun contrat essentiel oui sur endpoint public compatible

Qualification importante : Composable aujourd'hui ? signifie « prouvable à partir de la Config stable telle qu'elle est structurée », pas « le provider distant répond actuellement ».

8. Audit Config

ResolvedTransportConfig fournit des settings typed HTTP/WS/gRPC et ResolvedStoreConfig fournit les settings Store sans exiger une lecture JSON frontend.

HTTP : rôles et request_kinds sont déjà projetables côté Rust.

WS : le protocol kind est projetable, mais WsEndpointSettings ne porte pas de liste de WsSubscriptionKind. C'est le gap déterminant de pre.001.

Profils Transport sources audités (ils ne constituent pas les profils applicatifs du Desk) :

Profil/config Réseau Surfaces Secret requis Committed ? Observation
devnet_public devnet HTTP + WS Solana standard non oui pas de capability WS fine aujourdhui
orbitflare_devnet devnet HTTP + WS standard + Yellowstone gRPC token gRPC secret conditionnel profil complet seulement si secret résolu
mainnet_public mainnet HTTP + WS Solana standard non oui pas de capability WS fine aujourdhui
mainnet_backfill_pool mainnet plusieurs HTTP + WS standard selon endpoint/config oui si profil résolu priorités/rôles restent Transport-owned
publicnode_mainnet mainnet HTTP + WS standard + Yellowstone gRPC x-token secret conditionnel source Yellowstone same-network
testnet_public testnet HTTP + WS Solana standard non oui socle keyless Testnet
publicnode_testnet testnet HTTP + WS standard + Yellowstone gRPC x-token secret conditionnel source Yellowstone same-network
helius_devnet devnet HTTP public + WS Helius API key secret conditionnel source Helius same-network
helius_mainnet mainnet HTTP public + WS Helius API key secret conditionnel source Helius same-network

Ces profils sont des sources Transport. Le composite applicatif Raw Ingest les agrège désormais sous devnet, mainnet et testnet; un provider n'est jamais une identité réseau.

9. Audit Store

Chemin validé :

ResolvedStoreConfig -> StoreSettings -> ksp-store-lib::Store::open

Aucun besoin de nouvelle API Store n'est identifié pour inventaire, Start/Stop ou partage du Store entre Workers du même réseau.

10. Audit gabarit Desk

Les cinq Desk KSP existantes ont été utilisées comme références de structure et non comme code à copier pendant pre.001.

Socle confirmé :

splash KSP commun
fonts/style KSP
backend Tauri Rust propriétaire des ressources
frontend sans accès direct réseau/Store/secrets
@fltsci/tauri-plugin-tracing
@fortawesome/fontawesome-free
@tauri-apps/api
bootstrap
resize-observer-polyfill
simplebar

Aucun upgrade npm opportuniste n'est retenu.

11. External re-audit pre.001

Sources primaires/courantes consultées pour qualifier les capabilities :

Solana JSON-RPC WebSocket blockSubscribe
Solana JSON-RPC WebSocket logsSubscribe
Yellowstone gRPC upstream Subscribe/proto/changelog
Helius WebSocket standard + transactionSubscribe + unsupported methods
PublicNode Solana RPC/WS/Yellowstone offre actuelle
OrbitFlare Yellowstone gRPC offre actuelle

Constats utilisés :

blockSubscribe est unstable et dépend de l'activation côté validator/provider
logsSubscribe est standard
Helius distingue les méthodes Solana standard de son extension transactionSubscribe
Helius indique blockSubscribe non supporté
Yellowstone expose les filtres Subscribe et le replay/from_slot lower-layer

Aucune de ces informations Web n'est convertie directement en route sélectionnable : Config locale reste l'autorité de composabilité.

12. Gap classification

Sujet Classification Owner Décision
Catalogue de cinq routes et IDs applicatifs adaptation app seulement Desk catalogue borné, pas de nouvelle crate
Projection HTTP request_kinds / rôles / réseau aucun gap Config + Transport déjà exploitable côté Rust
Projection gRPC Yellowstone / metadata sûre aucun gap Config + Transport secrets restent Rust-only
Capability WS exacte par subscription extension Transport minimale Transport ajouter une déclaration typed réutilisant WsSubscriptionKind
Mapping schema/config des capabilities WS extension Config minimale Config Config reste propriétaire de la déclaration
Refus Worker si capability WS requise absente extension Worker minimale Worker enforcement avant I/O, pas de logique provider dans Desk
Composite dédié Raw Ingest Desk extension Config minimale Config profils app dédiés et cohérents Store/Transport
Store open / network / partage Arc<Store> aucun gap Store + app ksp-store-lib suffit
Lifecycle / stop / snapshots Worker aucun gap Worker + app handles publics existants
Backfill / historical repair hors scope Job Backfill aucune orchestration automatique
Taille release pas de split de release plan forecast étendu jusquà pre.018

Résultat : 0.3.15 reste une release cohérente. Aucun split vers 0.3.16 n'est nécessaire avant implémentation, mais le forecast interne est élargi.

13. Décisions UX / architecture validées au gate

catalogue de routes V1 app-owned
cinq route IDs bornés
réseau dérivé d'un composite/profil app cohérent Store+Transport
changement de profil interdit pendant Workers actifs
inventaire Config sans I/O réseau
Start revalide toute la Config
multiple endpoints satisfaisant une route : sélection Transport selon rôles/priorités
1 Worker par route
fault d'une route n'arrête pas les autres
Store partagé seulement si réseau strictement identique
fermeture app : stop/join de tous les Workers détenus
frontend sans endpoints/secrets/handles/source keys/payloads bruts

États UI retenus :

État UI Sémantique Action principale
Unavailable requirements incomplets ou incohérents non
Configured requirements Config complètes, aucune preuve runtime oui
Starting revalidation + reconstruction + ouverture Store + start Worker non
Running Worker actif, snapshots reçus Stop
Stopping stop demandé, attente terminale bornée non
Stopped Worker terminal propre, handle retiré Start après nouvelle revalidation
Faulted échec Start/runtime avec code sûr Start après correction/revalidation

14. IPC et tracing planifiés

DTO candidat Sens Contenu autorisé Exclusions
RawIngestDeskOptionsDto backend -> frontend profils logiques sûrs, réseaux, commitments supportés, génération inventaire URL, secret, Store URI
RawIngestRouteDto backend -> frontend route_id, family, label, network, state, selectable, reason code endpoint name/URL/token/source key
RawIngestRouteStartRequestDto frontend -> backend profile_id, route_id, inventory_generation, commitment endpoint/credential/client
RawIngestRouteStopRequestDto frontend -> backend route_id Worker handle
RawIngestRouteRuntimeDto backend -> frontend route_id, lifecycle/health/activity, compteurs source-neutral payload brut, erreur provider arbitraire
CommandErrorDto backend -> frontend code stable + contexte whiteliste texte remote arbitraire
FrontendLogPayloadDto frontend -> backend target/level/action IDs bornés valeurs config/route sensibles
Interaction Trace autorisée Interdit
Navigation/tab control_id + action aucun profil, endpoint ou secret
Sélection profil/réseau action + résultat code + counts pas de valeur de secret ni URL
Sélection route route family/route_id sûr + booléen pas de source key
Start/Stop route_id sûr + lifecycle outcome code pas de payload Worker
Refresh/resync snapshots action + sequence/count pas de transactions
IPC success/failure command id + stable code + durée éventuelle pas derreur remote brute

Ces tables décrivent le contrat de sécurité à implémenter ; elles ne revendiquent aucune commande Tauri existante en pre.001.

15. Sizing / forecast souple

Le forecast durable est porté par la section 20 du plan 036 sous forme hiérarchique éditable, et non par un tableau. La validation conserve ici la même séquence afin que le statut de chaque tranche puisse être complété sans réécrire la structure documentaire.

pre.001 — cadrage

Statut : réalisé. Audit, route model, external re-audit, sizing et plan/validation, sans code fonctionnel Desk.

pre.001-fix.001 — forme du forecast

Statut : réalisé ; documentaire uniquement. Le tableau de forecast initial est remplacé par la convention KSP ### pre.NNN / #### pre.NNN-fix.MMM. Aucun contenu fonctionnel du sizing n'est changé.

pre.002 — Transport WS capability

Statut : réalisé après pre.002-fix.001. Capability WS typed par subscription + tests, sans app. Le gate opérateur du fix passe cargo check, Clippy -D warnings et la suite Transport complète.

pre.003 — Config capability/composite

Statut : réalisé et gate opérateur PASS. Schema/mapping capabilities WS + composite/profils Raw Ingest Desk. Les profils standard committed déclarent la surface stable sans Block, les profils Helius déclarent explicitement HeliusTransaction sans Block/Vote, et les profils du composite Raw Ingest Desk couplent Transport+Store sur le même réseau. Le gate opérateur passe cargo check, Clippy -D warnings, cargo test -p ksp-config-lib --all-targets --all-features (130 tests unitaires PASS) et cargo test --workspace --all-targets --all-features.

pre.004 — Worker enforcement

Statut : réalisé et gate opérateur PASS. Les trois constructeurs de routes WS (Standard Logs, Standard Block, Helius Transaction) exigent la capability Transport exacte via supports_subscription(...) et refusent l'état undeclared/capability absente avant toute I/O. Le protocole reste validé avant la capability afin de conserver des diagnostics stables. Yellowstone et HTTP Block Polling ne sont pas concernés. Des canaris unitaires prouvent le refus undeclared/mauvaise capability et l'acceptation de la capability exacte ; les canaris hardening/release-completeness prouvent l'ordre avant connect, l'absence de logique provider et l'absence de croissance de surface publique. Le gate opérateur passe les audits, cargo check, Clippy strict, 153/153 tests unitaires Worker, les suites Worker d'intégration et le workspace all-targets/all-features complet.

pre.005 — scaffold Desk

Statut : réalisé et gate opérateur PASS après deux fixes. ksp-app-raw-transaction-ingest-desk existe comme crate Tauri mixte lib/bin, ports 1440/1441, splash/assets/fonts communs, Config/Logging et tracing frontend. pre.005-fix.001 retire un import Rust inutilisé détecté par Clippy. pre.005-fix.002 restaure le template clair commun des Desk KSP, corrige le débordement de #routeFoundation et le retour explicite du canari release-completeness. Le gate final passe audits, cargo check, Clippy strict, tests Desk, cargo test --workspace --all-targets --all-features et cargo tauri dev; le parcours visuel confirme le shell corrigé. Aucun script npm applicatif n'est lancé directement : Tauri déclenche ses hooks conformément à KSP-APP-034.

pre.006 — inventaire composable

Statut : gate opérateur de pre.006 en échec fonctionnel, puis corrections pre.006-fix.001, pre.006-fix.002 et pre.006-fix.003. Le premier inventaire classait encore les capabilities par profil Transport fournisseur : Yellowstone + HTTP hydration Mainnet n'était donc Configured que sous publicnode_mainnet. Le fix remplace cette projection par trois profils réseau (devnet, mainnet, testnet) qui agrègent les sources Transport same-network. PublicNode, Helius et OrbitFlare restent des sources de capabilities et ne deviennent jamais des identités réseau. Un secret optionnel absent n'invalide que la route dépendante. Le canari qui dépendait du Config utilisateur de la machine est remplacé par un moteur Config committé et un environnement injecté. Aucun I/O runtime n'est ajouté.

pre.006-fix.001 — correction classification réseau

Attendus : mainnet expose Yellowstone dès que la source PublicNode Mainnet est effectivement résolue, quelle que soit la source HTTP/WS standard retenue dans le même profil réseau ; mainnet reste utilisable pour Standard Logs et HTTP Block Polling lorsque les secrets Helius/PublicNode sont absents. Le même principe s'applique à Devnet et Testnet.

pre.006-fix.002 — Standard Block réellement composable

Le gate de pre.006-fix.001 révèle deux points : les canaris Desk ne compilent pas car ils appellent ConfigEnvironment::from_maps, helper pub(crate)/cfg(test) de ksp-config-lib, et standard-block-direct reste systématiquement indisponible parce qu'aucun endpoint standard committed ne déclare Block.

Le fix déclare explicitement Block sur les endpoints solana-public Standard WS committés de Devnet, Mainnet et Testnet, sans toucher Helius LaserStream. L'inventaire réseau-centrique projette donc standard-block-direct en Configured sur les trois réseaux. Cette classification reste une preuve Config de composabilité, pas une preuve live permanente : blockSubscribe reste unstable et validator/provider-gated, donc pre.007/Start devra reconstruire et revalider la ressource exacte avant exécution.

Les canaris Desk n'utilisent plus l'API crate-private de Config. Ils valident les routes standard à partir de ConfigEnvironment::load() et acceptent que les routes optionnelles Helius/Yellowstone soient soit Configured, soit missing_required_secret selon l'environnement opérateur.

pre.006-fix.003 — Helius Testnet non applicable

Le parcours visuel de pre.006-fix.002 montre toutes les routes composables en vert sauf helius-transaction-hydrated sur Testnet. Ce rouge était trompeur : le composite Testnet ne déclare aucune source transport.helius, et Helius Enhanced WebSocket transactionSubscribe n'est pas une capability Testnet configurée. Le fix ne falsifie donc pas la capability ; il retire cette route provider-specific de l'inventaire Testnet. Devnet/Mainnet continuent à la projeter et à la qualifier selon leur source Helius.

Le catalogue global reste exactement cinq IDs V1, mais l'inventaire d'un profil réseau contient uniquement les routes applicables à ses sources déclarées. Le canari frontend obsolète logical profile selected est également réaligné sur le tracing réseau-centrique logical network selected.

pre.007 — ressources Start

Statut : réalisé et gate opérateur PASS après pre.007-fix.001 et pre.007-fix.002. Le préflight backend validate_route_start est generation-bound et revalide la sélection contre un inventaire Config reconstruit avant toute ressource. La requête contient désormais profile_id + route_id + inventory_generation + commitment : profile_id est nécessaire depuis que les profils applicatifs sont réseau-centriques et qu'un même route ID existe sur plusieurs réseaux.

La preuve Start-time exige ensuite :

Store Config résolu et réseau identique
Transport de base résolu et same-network
source provider-specific résolue seulement pour Helius/Yellowstone
HTTP role unique satisfaisant l'ensemble des méthodes requises
WS/gRPC endpoint unique correspondant au protocole/capability/réseau
constructeur public Worker de la famille exacte accepté

Les cinq constructeurs source Worker sont effectivement réutilisés. HttpTransportPool::new et YellowstoneGrpcChannel::prepare restent sans requête réseau ; la source reconstruite est détruite immédiatement après validation. Sont encore interdits dans cette tranche : Store::open, connexion WS/gRPC active, RawTransactionIngestRuntimeResources, RawTransactionIngestWorker::start_with_runtime_resources, handle Worker, Start/Stop réel et état Running.

Attendus de sécurité : la réponse de préflight ne contient que profil logique, réseau, route, génération et commitment ; les erreurs lower-layer sont remappées derrière des codes/messages app bornés. Aucun endpoint, URL, credential, metadata secrète, URI Store, source key ou payload RAW ne traverse IPC.

pre.007-fix.001 corrige deux erreurs syntaxiques de l'assemblage initial (double visibilité pub(crate) et littéraux de canari mal échappés). pre.007-fix.002 rend le canari de reconstruction hermétique en l'adossant explicitement aux racines Config committées du workspace au lieu des racines par défaut de la machine. Le gate opérateur final de 0.3.15-pre.7.fix.2 passe audits Rust/Markdown, cargo check --workspace, Clippy workspace strict, les 34 tests unitaires Raw Ingest Desk, ses suites d'intégration et le workspace --all-targets --all-features; cargo tauri dev compile et lance ensuite l'application.

pre.008 — runtime mono-route

Implémenté dans l'assemblage de tranche : Start réutilise la revalidation pre.007, réserve un slot runtime unique, ouvre le Store exclusivement via ksp-store-lib, exige StoreHealthState::Ready, construit un WorkerId backend-owned puis appelle RawTransactionIngestWorker::start_with_runtime_resources. Le handle est installé avant l'accusé IPC.

Stop demande l'arrêt coopératif au handle et attend son état terminal. Un monitor backend indépendant attend également le terminal, récupère de manière bornée l'unique Arc<Store>, appelle explicitement Store::close() puis libère le slot. Les échecs avant installation du handle rollbackent le slot et ferment le Store déjà ouvert.

Surface IPC sûre ajoutée : start_route et stop_route, avec RawIngestRouteRuntimeDto limité à profile_id, network, route_id, inventory_generation, commitment et state. Aucune URL, credential, URI Store, source key, WorkerId, handle ou payload RAW n'est projeté.

Le frontend fournit un contrôle minimal Start/Stop mono-route et le choix confirmed/finalized. Il ne prétend pas encore fournir le monitoring continu : une terminaison spontanée est nettoyée backend-side mais sa projection temps réel demeure pre.010. Multi-route, Store partagé entre Workers et isolation inter-route demeurent pre.009.

pre.008-fix.004 — preuve live Mainnet et correction PublicNode

Le parcours live pre.008-fix.003 prouve que le channel Yellowstone PublicNode Mainnet s'ouvre et produit des transactions, mais que l'hydration était construite depuis le Transport de base mainnet_public. Les traces montraient donc le gRPC publicnode_solana_mainnet_yellowstone suivi de requêtes getTransaction via solana_mainnet_public, puis rate-limit/cooldown et overflow de queue.

Le fix corrige la composition : prepare_yellowstone prend désormais son role/pool HTTP dans le même ResolvedTransportConfig optionnel qui fournit le gRPC. Le profil publicnode_mainnet expose publicnode_solana_mainnet_rpc sur https://solana-rpc.publicnode.com, provider publicnode, sans recopier la limite locale 5 req/s du profil Solana-public. La concurrence locale reste bornée à 8 et le cooldown Transport sur 429 reste actif.

Non-claim : ce changement réduit une erreur de composition mais ne garantit pas qu'une hydration HTTP unitaire puisse suivre le firehose Mainnet. pre.009 choisit finalement Yellowstone Block -> getBlock multi-provider comme optimisation sûre ; la projection protobuf -> RAW directe reste non revendiquée.

pre.009 — optimisation Mainnet et runtime multi-route

La tranche matérialise un Worker indépendant par route, Stop ciblé, Store partagé uniquement sur identité réseau exacte et fermeture Store par la dernière route. Les faults restent route-local et aucune TargetCoverage/gap/frontier n'est partagée entre Workers.

La route Yellowstone bascule sur un filtre Block sans transactions embarquées puis reconcile le slot par getBlock Full/Base64. Le Worker distingue explicitement son ancien mode transaction-hydration du mode block-reconciliation ; ce dernier n'entre plus dans la registry d'hydration getTransaction. La récupération bloc est interruptible par Stop et retente de façon bornée lorsque le bloc n'est pas encore visible ou qu'un endpoint du pool échoue.

Le profil publicnode_mainnet contient deux endpoints HTTP de même rôle : PublicNode et Solana public repair. La validation doit prouver que Config conserve leurs limites séparées et que la route exige désormais getBlock, pas getTransaction. Le direct protobuf -> RAW n'est pas revendiqué.

pre.010 — monitoring

Implémenté dans l'assemblage de tranche. Le nouveau module route_monitoring projette les snapshots Worker existants vers un DTO latest-value source-neutral comprenant lifecycle, health, activity, admission/persistence, backpressure, hydration pending, processing frontier, continuité, source counts, reconnect/replay, gaps et repair. Le Worker expose uniquement les cinq getters de continuité qui manquaient à la façade publique de son snapshot ; aucun type ni comportement d'acquisition n'est ajouté.

Le Tauri backend démarre un monitor de snapshot indépendant pour chaque Worker lancé et émet ksp-raw-ingest-route-status vers la fenêtre principale. La commande get_route_monitoring permet une resynchronisation complète sans dépendre de la livraison de tous les événements intermédiaires. Le runtime conserve le dernier snapshot terminal sûr de chaque route jusqu'à un nouveau Start de la même identité ou au changement de session réseau.

Sécurité attendue : aucune URL, credential, provider physique, source key, WorkerId, handle, URI Store, signature ou payload RAW dans le DTO. Les u64 traversant IPC sont encodés en texte décimal ; les cardinalités bornées sont projetées en u32. Le monitor d'observabilité ne possède ni Store ni lifecycle et ne peut pas arrêter/redémarrer un Worker.

La consommation UI détaillée de ces snapshots et le tracing TypeScript associé restent pre.011.

pre.011 — frontend

Frontend fonctionnel complet + tracing TypeScript sûr.

pre.011-fix.001 — réactivité du monitoring frontend

Le gate statique de pre.011 est propre, mais l'essai Mainnet révèle une saturation de l'interface dès qu'une route Yellowstone productive est active : le Worker continue à hydrater des blocs, alors qu'aucune seconde action Start/Stop n'atteint le tracing frontend après le démarrage. La cause est applicative : monitor_route_status relayait chaque changement de séquence Worker et applyRouteMonitoring reconstruisait toute la grille de routes pour chaque snapshot. Sur un flux transactionnel soutenu, les boutons Start/Stop étaient donc remplacés continuellement dans le DOM et pouvaient perdre leur cible entre les événements pointeur/click.

Le fix conserve le contrat latest-value sans polling métier, mais borne le pont backend à une émission maximale toutes les 500 ms par route. Le snapshot terminal reste projeté ; entre deux émissions, la source watch conserve naturellement la dernière valeur. Côté TypeScript, les snapshots steady-state patchent uniquement les champs monitoring de la carte existante et le détail sélectionné. Une reconstruction complète des cartes n'est effectuée que lorsqu'aucune carte patchable n'existe ou lorsque le lifecycle change, ce qui maintient les boutons Start/Stop stables pendant l'activité normale.

Canari ajouté :

pre_011_fix_001_monitoring_bridge_is_bounded_and_steady_updates_preserve_route_controls

Ce correctif ne modifie ni Config, ni Transport, ni Store, ni Worker, ni Common RAW, ni les stratégies d'acquisition.

pre.012 — hardening

Races, shutdown app, sécurité IPC, hardening hostile/stale inventory.

pre.013 — Yellowstone Block hydration backpressure

Découplage borné réception gRPC / getBlock / admission pour la route Yellowstone Block, conformément à VER-LIFECYCLE-010.

pre.014 — convergence inter-provider + completeness

Diagnostic puis compatibilité étroite des logMessages explicitement tronqués, avec canaris cross-layer et sans expansion de frontières publiques.

pre.015 — Yellowstone sustained gRPC backpressure

La saturation locale de la queue d'updates Yellowstone doit appliquer une backpressure asynchrone bornée au stream gRPC au lieu de produire un terminal grpc_backpressure_overflow.

pre.016 — gate technique/live

Gate technique/live accessible + frontend/Tauri + build final de validation technique.

pre.017 — réconciliation documentaire

Plan/validation/README/USAGE uniquement selon les surfaces réellement affectées.

pre.018 — préparation publication

Prompt suivant + CHANGELOG + ROADMAP + mécanique minimale de publication.

rel.001 — stable

Publication mécanique uniquement, aucun rattrapage fonctionnel.

Le forecast initial du prompt n'est pas recopié : trois tranches lower-layer précèdent volontairement le scaffold UI.

16. Modifications pre.001

Ajouts :

docs/plans/036-V0_3_15_RAW_TRANSACTION_INGEST_DESK_PLAN.md
docs/validation/032-V0_3_15_RAW_TRANSACTION_INGEST_DESK.md
deltas/0.3.15/pre.001.md

Modification :

Cargo.toml
header 593 -> 594
workspace.package.version 0.3.14 -> 0.3.15-pre.1

Aucun fichier de production Rust, Config, frontend ou Tauri n'est modifié par le gate.

17. Validations post-modification

Exécuté localement après le bump et lajout des trois livrables :

python3 scripts/audit_rust_workspace_rules.py

Résultat :

General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean

Exécuté localement :

python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas

Résultat :

Markdown table audit: clean (354 table(s), 889 file(s))

Contrôles complémentaires :

rule definitions : 489
unique rule IDs : 489
duplicates : 0
stable -> worktree : 3 additions / 1 modification / 0 deletion
modified stable file : Cargo.toml seulement

Non exécuté localement :

cargo fmt --all -- --check : NON EXÉCUTÉ — cargo absent du sandbox
cargo check --workspace : NON EXÉCUTÉ — cargo absent du sandbox
cargo clippy --workspace --all-targets --all-features -- -D warnings : NON EXÉCUTÉ — cargo absent du sandbox
cargo test : NON EXÉCUTÉ — cargo absent du sandbox

Aucun de ces gates Cargo nest déclaré PASS. Ils restent dans le gate opérateur après application du delta.

pre.009-fix.001 — shutdown Yellowstone et diagnostic de source

Le gate live pre.009 a confirmé le multi-route et le chemin Yellowstone Block -> getBlock, mais un Stop Yellowstone pouvait entrer en collision avec la deadline de fermeture gRPC de 5 s. Le défaut Worker de shutdown_drain_timeout passe à 10 s ; un timeout de session.close() après Stop déjà demandé est accepté comme fermeture coopérative uniquement pour onchain_transport.timeout, les autres fautes restant terminales. Les sources terminales tracent désormais uniquement leur famille logique et les codes d'erreur sûrs, afin de qualifier séparément le Faulted encore observé sur http-block-polling Mainnet sans le masquer.

pre.009-fix.002 — résultat du gate opérateur fix.001

Le gate opérateur fourni pour 0.3.15-pre.009-fix.001 confirme les points suivants :

cargo fmt --all / --check              exécutés par l'opérateur
audit Rust workspace                   PASS
audit Markdown                         PASS (318 tables / 209 fichiers)
cargo check --workspace                PASS
Clippy workspace strict -D warnings    PASS
tests Raw Transaction Ingest Desk      PASS
tests Worker                            FAIL : 156 PASS / 1 FAIL
tests workspace                         FAIL : canari logging en plus du test Worker
cargo tauri dev                         lancé et application opérationnelle

Les deux échecs statiques sont déterministes :

settings::pre_003_defaults_are_exact_and_preserve_typed_identity
    settings.shutdown_drain_timeout() = 10 s
    assertion restée à 5 s

workspace_logging::behavioral_crates_own_explicit_tracing_targets
    target littéral ajouté dans runtime_resources.rs
    règle workspace : cible possédée par src/constants.rs

Le live Mainnet ne valide pas encore le shutdown de fix.001 :

yellowstone-hydrated
    acquisition getBlock productive
    Stop demandé
    ~5 s plus tard : runtime_invalid, sans transport_domain/code
    terminal : Faulted

http-block-polling
    getSlot/getBlocks/getBlock productifs pendant ~48 s
    Stop demandé
    runtime_invalid quasi immédiat, sans transport_domain/code
    terminal : Faulted

standard-logs-hydrated
    fault avant Stop : source_failed / onchain_transport.timeout
    le log Transport montre un cooldown rate-limit du RPC public

standard-block-direct
    fault avant Stop : source_failed / onchain_transport.rpc_application_error

helius-transaction-hydrated
    fault avant Stop : source_failed / onchain_transport.rpc_application_error

Conclusion : le passage du drain par défaut à 10 s reste conservé mais n'est pas une preuve de correction Yellowstone. fix.002 répare les deux gates statiques et expose dans la trace source le contexte interne statique condition des erreurs runtime_invalid. Le prochain live doit fournir cette condition exacte pour Yellowstone et HTTP polling ; aucune conversion générique runtime_invalid -> Stopped n'est autorisée.

pre.009-fix.003 — résultat du gate opérateur fix.002

Le gate opérateur de 0.3.15-pre.009-fix.002 confirme :

audit Rust workspace                   PASS
audit Markdown                         PASS (318 tables / 210 fichiers)
cargo check --workspace                PASS
Clippy workspace strict -D warnings    PASS
tests unitaires Worker                 PASS : 158 / 158
workspace_logging                      PASS
tests Worker complets                  FAIL : 1 canari release_completeness
tests workspace                        FAIL : même canari Worker
cargo tauri dev                        lancé et application opérationnelle

Léchec statique restant est exact :

pre_010_production_module_inventory_is_exact
    réel    : admission.rs, constants.rs, continuity.rs, ...
    attendu : admission.rs,               continuity.rs, ...

constants.rs est une production source volontaire de fix.002; le canari doit donc linclure.

Le live apporte surtout la qualification attendue :

Mainnet / yellowstone-hydrated
    route productive via Yellowstone Block + HTTP getBlock
    Stop demandé
    ~5 s plus tard : worker_raw_transaction_ingest.runtime_invalid
    condition=source.admission_closed
    transport_domain=none
    transport_code=none

Mainnet / http-block-polling
    route productive via getSlot/getBlocks/getBlock
    Stop demandé
    ~10 ms plus tard : worker_raw_transaction_ingest.runtime_invalid
    condition=source.admission_closed
    transport_domain=none
    transport_code=none

Devnet / yellowstone-hydrated
    fault avant Stop lors de SubscribeOpen
    source_failed / onchain_transport.grpc_status
    OrbitFlare refuse la permission

La cause Mainnet est une race interne commune : le superviseur Worker ferme le receiver dadmission au début du drain alors que run_live_sources na pas nécessairement encore relayé le Stop à sa source interne. Un dernier send() voit alors ladmission fermée alors que son stop_receiver local vaut encore false, ce qui fabrique source.admission_closed.

Validation attendue après fix.003 : le drain coopératif ne ferme plus ladmission au début ; il consomme la file jusquà destruction naturelle des senders après propagation du Stop. Le hard-close reste dans le chemin shutdown_drain_timeout. Un test unitaire force cette fenêtre de propagation et exige Stopped sans source failure.

pre.010-fix.001 — faux positif du canari de sécurité monitoring

Le gate opérateur de 0.3.15-pre.010 confirme que la tranche compile et que son comportement runtime Mainnet attendu est opérationnel, mais détecte un unique échec déterministe dans le canari Desktop Security :

pre_010_monitoring_projection_is_source_neutral_payload_free_and_javascript_exact
    FAIL : monitoring projection exposes forbidden marker raw_transaction

Le marqueur est trop large : route_monitoring.rs doit nécessairement référencer ksp_worker_raw_transaction_ingest_lib et les chemins d'export TypeScript propres à ksp-app-raw-transaction-ingest-desk. Ces identifiants contiennent la chaîne raw_transaction sans exposer aucune donnée RAW.

Le gate opérateur confirme en parallèle :

audit Rust workspace                   PASS
audit Markdown                         PASS (318 tables / 212 fichiers)
cargo check --workspace                PASS
Clippy workspace strict -D warnings    PASS
tests Worker                           PASS : 160 unitaires + suites d'intégration
Raw Transaction Ingest Desk            1 seul échec : canari desktop_security ci-dessus
workspace all-targets/all-features     même échec déterministe
cargo tauri dev                         lancé et application opérationnelle

Le live Mainnet exécuté pendant ce gate confirme aussi que yellowstone-hydrated et http-block-polling peuvent fonctionner simultanément sur le Store partagé, puis terminer toutes deux en Stopped après Stop ciblé. Aucun fault source.admission_closed n'est réapparu.

Le correctif ne modifie pas la projection runtime. Il sépare le canari en deux niveaux : les marqueurs physiques/secrets (endpoint_url, connection_uri, api_key, source_key, worker_id, x-token) restent interdits dans tout route_monitoring.rs, tandis que les marqueurs RAW (raw_transaction, signature, payload, canonical_bytes, wire_bytes, transaction_bytes) sont interdits dans la seule surface déclarative de RawIngestRouteMonitoringDto. Le test vérifie explicitement les bornes de cette déclaration. Il reste donc strict sur l'IPC sans confondre les noms normaux des crates/bindings avec des champs réellement projetés.

pre.011 — frontend fonctionnel et tracing TypeScript

Base : 0.3.15-pre.010-fix.001, validée par le gate opérateur du 15 septembre 2026. Le correctif de canari desktop_security est confirmé propre ; les audits Rust/Markdown, cargo check --workspace, Clippy strict, les suites Raw Transaction Ingest Desk/Worker et le workspace all-targets/all-features sont passés avant le lancement Tauri. Les smokes opérateur Mainnet montrent également Yellowstone et HTTP Block Polling productifs puis terminalement Stopped après Stop ciblé.

Périmètre pre.011 : frontend uniquement au-dessus du contrat latest-value pre.010.

écoute Tauri ksp-raw-ingest-route-status
resynchronisation get_route_monitoring
Map frontend latest-value par profile_id + route_id
réconciliation des active/last runtimes depuis le snapshot Worker projeté
résumé route : lifecycle / health / activity / persisted / sources / gaps
détail supervision : admission / persistence / outcomes / backpressure / hydration / frontier
sources : active / reconnecting / failed / reconnect / replay / continuity-gap counters
continuité : frontier / open gaps / failed-source reconciliation / future coverage
gaps bornés : range / state / reason / latest repair method
repair totals : replay / redundant coverage / HTTP scan / block fetch / transaction hydration
resync explicite après Start, Stop et action opérateur
Store header dérivé du nombre de Workers actifs, sans exposer de handle Store

Le frontend ne transforme jamais les compteurs, slots ou séquences u64 en number; les chaînes décimales pre.010 sont rendues directement. Les cardinalités u32 restent les seules valeurs numériques de présentation.

Tracing TypeScript ajouté/fermé :

clics génériques sans valeur de contrôle
navigation tabs
sélection profil logique avec counts sûrs
sélection commitment
sélection route de supervision avec route_id/family sûrs
Start/Stop avec route_id/lifecycle
latest-value event avec route_id/sequence/state/terminal
refresh/resync monitoring avec source/counts
IPC via invokeKsp avec command id seulement

Les traces ne sérialisent jamais le DTO monitoring complet et ne journalisent aucun gap slot/range, endpoint, URL, credential, provider secret, source key, WorkerId, signature, transaction bytes ou payload RAW.

Canaris ajoutés :

pre_011_frontend_consumes_latest_value_monitoring_and_renders_full_route_supervision
pre_011_frontend_monitoring_tracing_is_source_neutral_and_does_not_log_raw_material
pre_011_frontend_supervision_closes_planned_ui_without_backend_module_growth

Aucune modification Config, Transport, Store, Worker ou Common RAW n'est introduite par pre.011. Le module Rust de production du Desk reste exactement celui de pre.010; la tranche suivante pre.012 reste dédiée aux races/shutdown/sécurité IPC.

pre.012 — races Start/Stop, shutdown applicatif et sécurité IPC

Base : 0.3.15-pre.011-fix.001, validée par le gate opérateur du 16 septembre 2026. Les audits, cargo check --workspace, Clippy strict, les suites Desk/Worker/workspace et le scénario live multi-route passent ; Yellowstone et HTTP Block Polling restent manipulables simultanément sous charge et le Store partagé ferme seulement après la dernière route.

Périmètre pre.012 : hardening du contrôle Desk uniquement, sans modification des stratégies d'acquisition.

shutdown application
    CloseRequested main window intercepté et empêché temporairement
    admission de nouveaux Start fermée une seule fois
    Stop coopératif demandé à tous les Workers actifs
    réservations Starting marquées stop_requested
    attente bornée routes + Store avant app_handle.exit
    second CloseRequested n'engendre aucun second shutdown

race Start / Stop
    Stop sur Starting => marque stop_requested puis attend le cleanup
    activation ultérieure => request_stop avant acknowledgement
    échec avant activation => rollback retient un terminal logique Stopped
    cleanup Desk borné et compatible avec le drain Worker borné

stale inventory / Start
    génération sous verrou pendant la préparation synchrone
    reconstruction complète Config/Transport à chaque Start
    verrou libéré explicitement avant le premier await
    admission shutdown revérifiée atomiquement pendant reserve

IPC hostile
    StartRequest / StopRequest : deny_unknown_fields
    profile_id : non vide, <= 256 octets, sans trim implicite ni contrôle
    FrontendLogPayload : deny_unknown_fields
    level / target <= 16 octets
    message <= 8192 octets et sans caractères de contrôle
    frontend : message technique borné à 1024 code units avant IPC
    erreurs Tauri : projection domain/code uniquement

Canaris ajoutés/étendus :

pre_012_route_requests_reject_unknown_fields_and_bound_free_form_profile_identity
pre_012_frontend_log_ipc_rejects_unknown_fields_and_unbounded_or_control_messages
pre_012_stop_racing_start_marks_the_reservation_for_cooperative_stop
pre_012_application_shutdown_is_one_shot_and_marks_starting_routes_before_activation
pre_012_window_shutdown_and_start_stop_races_are_bounded_backend_owned
pre_012_hostile_ipc_is_shape_strict_bounded_and_safely_projected
pre_012_race_shutdown_and_ipc_hardening_adds_no_production_module_or_lower_layer_growth

Non-claims : pre.012 ne modifie ni Config, ni Transport, ni Store, ni ksp-worker-raw-transaction-ingest-lib, ni Common RAW. Il n'ajoute aucune orchestration Backfill, aucun scheduler, aucune route et aucune nouvelle donnée frontend. pre.014 reste propriétaire de la fermeture completeness/security cross-layer.

pre.012-fix.001 — future Tauri Start Send

Le gate opérateur de pre.012 a arrêté la tranche sur un défaut de compilation déterministe dans AppState::start_route : le std::sync::MutexGuard<u32> de inventory_generation restait considéré vivant au franchissement du await final, malgré std::mem::drop(generation). Le futur exposé par la commande Tauri start_route ne satisfaisait donc plus la borne Send imposée par tauri::ipc::ResultFutureTag::future. Les audits Rust/Markdown étaient propres et les suites Worker indépendantes restaient vertes, mais cargo check, Clippy, les tests Desk/workspace et cargo tauri dev ne pouvaient pas compiler le Desk.

Le fix ne change aucune sémantique Start/Stop. Lock + revalidation/préparation synchrones sont désormais enfermés dans une portée lexicale let prepared = { ... };. Le MutexGuard est donc détruit structurellement à la fin du bloc avant tout await. Le contrat stale-inventory de pre.012 reste inchangé : la génération demeure verrouillée pendant toute la reconstruction synchrone, mais aucun guard non-Send ne traverse une suspension asynchrone.

Un canari de compilation pre_012_fix_001_start_route_future_is_send_for_tauri_command impose explicitement le type Future<...> + Send au futur retourné par AppState::start_route. Le canari desktop vérifie également la portée lexicale et interdit le retour au simple std::mem::drop(generation) comme preuve de lifetime.

Non-claims : aucune modification de route, Config, Transport, Store, Worker, Common RAW, monitoring, shutdown ou sécurité IPC.

pre.012-fix.002 — fault terminal diagnostiquable sans payload sensible

Le gate opérateur de pre.012-fix.001 compile et teste proprement le Desk, y compris le canari Future + Send. Le live Mainnet révèle toutefois un Worker Yellowstone devenant terminal faulted alors que le Worker HTTP Block Polling continue à produire des getBlock HTTP 200. Aucun WARN source/Transport n'est émis avant le terminal, et le log Desk historique ne conservait que worker_state="faulted"; le fault_domain/fault_code déjà présents dans le snapshot Worker et dans le DTO monitoring étaient donc perdus côté logs opérateur.

Le fix ajoute uniquement une projection de diagnostic terminale source-neutre et bornée dans RouteRuntimeLaunch::monitor. Le log terminal porte désormais l'identité logique sûre de route, worker_state, worker_health, worker_activity, fault_domain, fault_code, les compteurs source_failure_total, store_failure_total, content_conflict_total, les cardinalités source/gap et les booléens de continuité. Aucun endpoint, credential, source key, WorkerId, signature, payload ou octet RAW n'est journalisé.

Ce fix ne change volontairement aucune politique fail-closed : il ne transforme pas un Faulted en succès et ne suppose pas que l'incident observé est un conflit Store ou une panne source. Le prochain live doit permettre de classer exactement la cause avant toute correction comportementale.

pre.012-fix.003 — canari terminal compatible Clippy strict

Le gate opérateur de pre.012-fix.002 confirme que le build normal, les suites Desk/Worker et le live Mainnet sont fonctionnels. Le nouveau diagnostic terminal montre Yellowstone et HTTP Block Polling terminant Stopped, Healthy, sans fault_domain/fault_code ni compteur d'erreur ; l'incident Unhealthy/Faulted précédent ne se reproduit donc pas dans ce run.

Le seul échec du gate est dans cargo clippy --workspace --all-targets --all-features -- -D warnings : le canari pre_012_fix_002_terminal_fault_logging_keeps_safe_route_identity_and_worker_cause_counters utilisait deux assert!(false, ...) dans ses branches de marqueur absent, ce qui déclenche clippy::assertions_on_constants.

Le fix ne modifie aucun code de production. Le canari teste désormais d'abord Option::is_some() puis extrait la valeur avec let-else; la branche impossible retourne seulement après l'assertion dynamique. Aucun panic!, unreachable!, unwrap() ou expect() n'est introduit.

Non-claims : aucune modification du runtime Desk, du monitoring, des routes, de Config, Transport, Store, Worker, Common RAW, acquisition, replay, repair ou politique health/fault.

pre.012-fix.004 — diagnostic sémantique des conflits RAW inter-routes

Le gate opérateur de pre.012-fix.003 compile et teste proprement la tranche puis reproduit deux faults Mainnet distincts sur Yellowstone : un premier terminal content_conflict avec content_conflict_total=13 pendant l'exécution simultanée de Yellowstone + HTTP Block Polling, puis un source_failed / onchain_transport.grpc_backpressure_overflow sur un run ultérieur. Le premier fault ne peut pas être expliqué par le cache de convergence interne d'un Worker : les deux routes Desk sont deux Workers indépendants qui partagent le même Store, et le conflit peut donc être détecté au niveau PostgreSQL lorsqu'une même identité canonique (network, signature) arrive avec un contenu différent.

Le fix ajoute un diagnostic fail-closed au point exact où PostgreSQL possède les deux versions. Avant de renvoyer le même store_api.raw_conflict, le backend journalise uniquement un masque borné et sans valeur métier :

slot_mismatch
block_time_mismatch
format_id_mismatch
format_version_mismatch
content_hash_mismatch
payload_bytes_mismatch
payload_diagnostic_available
transaction_mismatch
meta_mismatch
version_mismatch
transaction_index_mismatch
payload_other_mismatch
meta_mismatch_fields

Pour RAW transaction v1, meta_mismatch_fields compare uniquement une liste fixe de champs connus : err, status, fee, preBalances, postBalances, innerInstructions, logMessages, preTokenBalances, postTokenBalances, rewards, loadedAddresses, returnData, computeUnitsConsumed, costUnits, accounts. Toute extension inconnue est réduite au marqueur statique other; aucun nom arbitraire ni valeur JSON distante n'est copié dans les logs.

Une seconde ligne de diagnostic lit au mieux l'observation durable la plus ancienne de la transaction conflictuelle et compare uniquement la provenance logique sûre avec l'observation entrante : provider, protocol, acquisition_method, endpoint_id, commitment. Signature, hash, payload, bytes, source payload hash, capture session et filter id ne sont jamais journalisés. Une impossibilité de lire cette observation de diagnostic ne modifie pas le fault original.

Ce fix n'altère ni l'identité canonique, ni la comparaison d'égalité, ni la politique de conflit, ni le Store schema, ni la stratégie d'acquisition. serde_json est utilisé uniquement dans le backend PostgreSQL pour classifier les composants du payload RAW v1 déjà canonique au moment d'un conflit.

Le prochain live Yellowstone + HTTP Block Polling doit permettre de distinguer immédiatement un désaccord de slot/block_time, de transaction wire, de transaction index, de version ou d'un sous-ensemble précis de meta (par exemple logMessages ou rewards).

pre.013 — découplage borné Yellowstone Block / HTTP getBlock

Le gate de pre.012-fix.004 est propre sur les nouveaux canaris Store et Worker. Le live Mainnet ne reproduit aucun content_conflict, mais reproduit deux fois un terminal Yellowstone source_failed / onchain_transport.grpc_backpressure_overflow. Les WARN Transport apparaissent à 22:06:52.734796 puis 22:08:39.746262, alors que les Stop Yellowstone n'arrivent qu'à 22:07:10.781900 puis 22:08:51.337483. Le Stop ne crée donc pas l'overflow ; il révèle ensuite le terminal déjà produit par Transport.

La cause est le chemin Yellowstone Block historique entièrement séquentiel :

Block update gRPC
  -> await HTTP getBlock
  -> await admission de toutes les transactions du bloc
  -> seulement ensuite next_update gRPC

Cette source était en outre exclue de uses_hydration(), donc ne recevait aucun quota pending/in-flight du supervisor multi-source. Une queue Transport plus grande ne ferait que retarder le même défaut.

pre.013 corrige le débit sans modifier Transport : Yellowstone Block est désormais une source hydratante pour le partitionnement des budgets existants. Un coordinateur privé de slots borne les pending ; jusqu'au quota in-flight, chaque tâche possédée exécute getBlock puis l'admission complète du bloc. La boucle source reste disponible pour session.next_update() tant qu'une place pending existe. Le slot n'est settled qu'après admission complète. Stop/abort joint toutes les tâches et purge les pending sans faux settlement.

La politique reste fail-closed : aucune hausse de YellowstoneGrpcSessionSettings.update_channel_capacity, aucune queue non bornée, aucun silent drop et aucun masquage de grpc_backpressure_overflow. Si le débit aval reste durablement inférieur à la chaîne malgré la concurrence bornée, une saturation terminale reste correcte et observable.

Canaris ajoutés :

v0_3_15_pre_013_yellowstone_block_hydration_coordinator_is_bounded_before_http_work
v0_3_15_pre_013_yellowstone_block_hydration_is_bounded_concurrent_and_stop_preemptible
v0_3_15_pre_013_yellowstone_block_backpressure_fix_adds_no_public_surface

Aucun type du coordinateur n'est exporté publiquement.

Validation live requise : Yellowstone Mainnet seul pendant une durée supérieure aux ~80 s de reproduction, puis Yellowstone + HTTP Block Polling en parallèle. Le run doit rester Healthy, sans grpc_backpressure_overflow, et les Stop ciblés doivent finir Stopped. Le diagnostic pre.012-fix.004 reste actif si un content_conflict réapparaît.

pre.014 — diagnostic de convergence inter-provider au niveau bloc / logMessages

Le gate opérateur de pre.013 est propre : audits Rust/Markdown, cargo check --workspace, Clippy strict, Worker (161/161 unitaires + suites externes), Desk (50/50 unitaires + suites externes) et workspace passent. Le live Mainnet confirme également l'objectif de pre.013 : aucune occurrence de grpc_backpressure_overflow ni de Yellowstone subscribe update queue overflowed n'est observée dans le run transmis.

Le live reproduit en revanche un unique terminal Yellowstone content_conflict. Le diagnostic Store de pre.012-fix.004 donne exactement :

slot_mismatch=false
block_time_mismatch=false
format_id_mismatch=false
format_version_mismatch=false
transaction_mismatch=false
meta_mismatch=true
version_mismatch=false
transaction_index_mismatch=false
payload_other_mismatch=false
meta_mismatch_fields="logMessages"

La provenance durable classe les deux acquisitions :

stored   : solana-public / solana_http / block_polling_get_block / solana_mainnet_public / confirmed
incoming : ys.publicnode:http.publicnode / yellowstone_http / block_subscribe_get_block /
           ys.publicnode_solana_mainnet_yellowstone:http.publicnode_solana_mainnet_rpc / confirmed

La transaction canonique elle-même est donc identique ; seule la métadonnée d'exécution meta.logMessages diffère. Le diagnostic existant ne permet toutefois pas encore de savoir si les deux RPC décrivent le même bloc confirmed ou deux forks/vues de bloc différents au même slot.

pre.014 ajoute deux niveaux de diagnostic sans changer la politique de conflit :

Worker / chaque getBlock réussi
    source_kind
    network
    slot
    provider
    endpoint_id
    commitment
    parent_slot
    block_height
    block_identity_fingerprint = SHA-256(domain || blockhash || previousBlockhash || parentSlot || blockHeight)

Store / seulement lors d'un content_conflict
    stored_slot / incoming_slot
    logMessages state : missing | null | array | other
    count de chaque côté
    premier index divergent
    type statique de la première ligne divergente :
        program_invoke | program_success | program_failed | program_log |
        program_data | compute_units | log_truncated | other | non_string | missing
    longueur de la première ligne divergente
    stored_prefix_of_incoming / incoming_prefix_of_stored
    has_truncation_marker de chaque côté

Le blockhash, le previousBlockhash, les signatures, les payloads et le texte réel de logMessages ne sont jamais journalisés. Le fingerprint de bloc est irréversible et sert uniquement à comparer deux acquisitions du même slot.

Interprétation du prochain conflit :

même slot + fingerprints différents
    => vues/forks confirmed différents entre providers au moment des getBlock

même slot + même fingerprint + logMessages différents
    => divergence provider/node sur la représentation ou l'enregistrement des logs du même bloc

incoming_prefix_of_stored=true ou stored_prefix_of_incoming=true
    => divergence compatible avec une liste tronquée/incomplète

*_has_truncation_marker=true
    => marqueur explicite de troncature présent dans une des réponses

Non-claims : pre.014 ne retire pas logMessages du contenu canonique, ne choisit aucun provider comme vérité, ne transforme pas content_conflict en succès, ne modifie pas le schema Store et n'expose aucun nouveau type public Worker/Store.

pre.014-fix.001 — observation logMessages tronquée compatible non terminale

Le live pre.014 ferme le diagnostic du conflit Mainnet observé au slot 447781296. HTTP Block Polling et Yellowstone Block Hydration ont produit la même identité de bloc (parent_slot=447781295, block_height=425822496, même fingerprint), la même transaction, le même index et les mêmes autres métadonnées. La seule divergence est meta.logMessages.

Le canonique déjà stocké contient 176 lignes sans marqueur de troncature. L'acquisition entrante contient 118 lignes et un marqueur exact Log truncated au premier point divergent. Le diagnostic prouve que toutes les lignes antérieures à ce marqueur sont identiques. Le défaut n'est donc ni un fork ni un changement de transaction : c'est une représentation RPC explicitement tronquée du même résultat d'exécution.

fix.001 ajoute une compatibilité volontairement asymétrique et étroite dans le backend PostgreSQL :

canonique déjà complet
+ entrant explicitement tronqué et moins complet au-delà du marqueur
+ mêmes network/signature/slot/block_time/format
+ mêmes transaction/version/transactionIndex
+ mêmes champs meta hors logMessages
+ un seul marqueur exact "Log truncated" côté entrant
+ préfixe avant marqueur identique
+ préfixe exact jusquau marqueur ; le matériel après le marqueur nest pas utilisé pour prouver la complétude
=> AlreadyPresent + observation persistée
=> aucun content_conflict
=> Worker continue

Le canonical RAW n'est jamais remplacé par la version tronquée. Les cas suivants restent des content_conflict en 0.3.15 : préfixe différent avant le marqueur, autre champ meta différent, marqueur non exact ou multiple, canonique déjà tronqué puis version plus complète entrante. Ce dernier cas nécessite une promotion canonique atomique et appartient explicitement à 0.3.16, avec variantes durables et historique de résolution ; il n'est pas simulé par un overwrite opportuniste dans ce fix.

Le correctif ne change aucune API publique Store/Worker, aucun schema SQL et aucun outcome public. RawEntityWriteOutcome::AlreadyPresent reste l'outcome canonique, l'observation entrante est conservée normalement et le Worker ne reçoit plus d'erreur pour le cas compatible démontré.

pre.014-fix.002 — correction du canari PostgreSQL et résultat live prolongé

Le gate opérateur de pre.014-fix.001 confirme les audits statiques et cargo check --workspace, puis échoue pendant Clippy/tests sur le nouveau test PostgreSQL : RawTimestamp::unix_millis() renvoie u64 alors que la ligne de test RawTransactionRow::block_time_unix_millis attend Option<i64>. Le fixture concerné utilise block_time=None, mais Rust doit tout de même typer la closure. fix.002 remplace la projection de test par une conversion sûre i64::try_from(...).ok() ; aucun comportement de production n'est modifié.

Le même run live apporte deux preuves distinctes. D'abord, le comportement fix.001 est confirmé sur plusieurs transactions Mainnet : le backend PostgreSQL accepte les logMessages entrants explicitement tronqués comme observations compatibles sans remplacer le canonique complet et sans produire de content_conflict. Les couples observés incluent 159/144, 176/119, 136/133 et 154/141 lignes stored/incoming.

Ensuite, un grpc_backpressure_overflow réapparaît sur Yellowstone Mainnet lors d'un run prolongé. Le WARN Transport survient à 22:09:54, plus de deux minutes avant le Stop opérateur à 22:12:03; il ne s'agit donc pas d'une race de shutdown. Le correctif pre.013 a supprimé le couplage strictement séquentiel, mais sa file privée de 256 slots pending et ses 8 hydratations concurrentes ne suffisent pas lorsque le débit aval moyen reste durablement inférieur au débit gRPC. Conformément à VER-LIFECYCLE-010, cette anomalie du couloir acquisition Yellowstone ne sera pas mélangée à pre.014-fix.002 : elle ouvre la prochaine prerelease dédiée, avant de rejouer le gate technique/live final.

Non-claims de fix.002 : aucun changement de Transport, Worker runtime, Store production, politique de conflit ou capacité de queue. Le seul changement Rust est la correction de typage du test PostgreSQL.

pre.014-fix.003 — resserrement du canari de non-réécriture canonique

Le gate opérateur de pre.014-fix.002 confirme la correction de typage : les audits statiques sont propres, cargo check --workspace et Clippy strict passent, les 75/75 tests unitaires ksp-store-postgres-lib passent, puis l'unique échec apparaît dans tests/hardening_completeness.rs. Le canari v0_3_15_pre_014_fix_001_truncated_log_compatibility_is_narrow_and_keeps_canonical_content cherchait la chaîne SQL UPDATE ksp_raw_transactions SET payload dans l'intégralité de src/raw_transaction.rs. Or cette chaîne existe légitimement dans la transition de rétention/archive historique du backend et n'appartient pas au chemin CompatibleLessComplete.

fix.003 resserre donc uniquement ce canari sur le corps de compare_existing_transaction(), jusqu'à la frontière du helper raw_transaction_incoming_truncated_log_messages_compatible(). Dans cette portion exacte, le test continue d'exiger le retour ExistingTransactionMatch::ActiveIncomingTruncatedLogs et interdit toute occurrence d'UPDATE ... payload ou de DELETE FROM ksp_raw_transactions. Le comportement production, le schéma SQL, la compatibilité logMessages, les outcomes Store et le Worker restent byte-for-byte inchangés par ce fix.

Non-claim : ce fix ne traite pas le grpc_backpressure_overflow Yellowstone prolongé déjà identifié ; ce défaut ouvre toujours la prochaine prerelease acquisition dédiée après fermeture du gate pre.014.

pre.015 — backpressure gRPC Yellowstone soutenue sans overflow local

Le gate opérateur de pre.014-fix.003 ferme la prerelease pre.014 : audits Rust/Markdown propres, cargo check --workspace, Clippy strict, ksp-store-postgres-lib (75/75 unitaires et 16/16 hardening completeness), Worker (161/161 unitaires et suites externes), Raw Transaction Ingest Desk et cargo test --workspace --all-targets --all-features passent. Le canari de non-réécriture canonique resserré par fix.003 est donc validé.

Le défaut acquisition restant provient du live prolongé précédent et ne relève pas du Store. Le WARN Yellowstone subscribe update queue overflowed apparaît à 22:09:54, tandis que le Stop Yellowstone n'est demandé qu'à 22:12:03. content_conflict_total=0 au terminal. La chaîne de saturation est désormais établie :

Worker Yellowstone Block pending plein (borne 256)
    -> Worker suspend session.next_update()
    -> queue Transport update bornée (borne 256) se remplit
    -> ancien update_tx.try_send(...) retourne Full
    -> grpc_backpressure_overflow local
    -> source_failed / Faulted

pre.015 ne change ni les valeurs 256/8, ni le Store, ni le payload RAW, ni le reconnect budget. L'acteur Subscribe Yellowstone conserve sa queue d'updates bornée mais attend désormais asynchronement sa capacité avec update_tx.send(...).await. Le même tokio::select! donne priorité au shutdown afin qu'un Stop reste préemptif même lorsque le consumer ne draine plus la queue. Pendant l'attente, le stream Tonic n'est plus pollé ; la pression est donc propagée au transport gRPC/HTTP/2 au lieu d'être transformée en erreur locale ou en drop.

Canaris requis :

yellowstone_slow_receiver_applies_bounded_backpressure_without_terminal_overflow
release_v0_3_15_pre_015_yellowstone_update_delivery_backpressures_without_local_overflow

Non-claims : aucune garantie lossless/exactly-once, aucune queue non bornée, aucun agrandissement de capacité, aucune modification WebSocket, aucun changement de la queue de mutations Yellowstone qui reste synchrone/fail-fast, aucune suppression du code public grpc_backpressure_overflow, aucune nouvelle API publique.

Gate opérateur attendu :

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/0.3.15
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
cargo test -p ksp-app-raw-transaction-ingest-desk --all-targets --all-features
cargo test --workspace --all-targets --all-features

Gate live Mainnet : Yellowstone seul pendant au moins 10 minutes, puis Yellowstone + HTTP Block Polling pendant au moins 10 minutes. Dans les deux cas, aucune occurrence de Yellowstone subscribe update queue overflowed ou grpc_backpressure_overflow n'est admise ; la route Yellowstone doit rester active/saine jusqu'au Stop ciblé, puis terminer Stopped. Une interruption réseau distante réellement observée doit rester classée par les codes Transport/reconnect existants et ne doit pas être confondue avec ce gate de saturation locale.

pre.015-fix.001 — Stop Yellowstone : Status post-half-close non terminal

Le gate opérateur de pre.015 est propre sur les audits, cargo check --workspace, Clippy strict, Transport (395/395 unitaires dont le canari de backpressure soutenue), Worker (161/161 unitaires), Raw Transaction Ingest Desk et le workspace. Le live Mainnet ne reproduit plus Yellowstone subscribe update queue overflowed ni grpc_backpressure_overflow dans la trace fournie.

Le défaut restant apparaît uniquement au Stop ciblé Yellowstone. L'opérateur demande le Stop à 20:57:33.659; après le half-close local, PublicNode renvoie environ 119 ms plus tard un Status gRPC Unknown. finish_client_half_close() classait encore ce status comme Failed/grpc_status, puis session.close() remontait cette faute au Worker, qui publiait source_failed et terminait Faulted/Unhealthy. HTTP Block Polling, arrêté ensuite, termine normalement Stopped/Healthy.

Le fix corrige uniquement la sémantique de fermeture déjà engagée : finish_client_half_close() n'est appelé qu'après un shutdown local explicite. Un Status reçu dans cette phase publie désormais Closed avec terminal_error_code=None. Il ne peut donc plus remplacer le Stop coopératif par une faute distante tardive. Un timeout de fermeture reste borné et continue de produire le code timeout; aucune attente infinie n'est introduite.

Cette règle ne s'applique pas aux Status reçus pendant une session active. La branche normale de run_subscribe_actor() conserve le reconnect borné existant et, lorsque la politique ne permet pas de reprise, le code grpc_status reste terminal. Le fix ne masque donc aucune panne distante observée avant le Stop.

Canaris :

yellowstone_explicit_close_accepts_remote_status_after_local_half_close
yellowstone_stream_remote_status_is_safe_and_terminal
release_v0_3_15_pre_015_fix_001_local_close_status_is_closed_without_weakening_active_status_failure

Gate live attendu : Yellowstone Mainnet doit rester Running/Healthy pendant le run, puis un Stop ciblé doit terminer Stopped même si PublicNode répond Unknown après le half-close. Une occurrence de grpc_status avant la demande de Stop reste un échec réel et ne doit pas être reclassée.

pre.016 — ouverture du gate technique/live final

Le retour opérateur de pre.015-fix.001 ferme la responsabilité backpressure/shutdown de pre.015. Après un cargo clean, les audits Rust/Markdown sont propres, cargo check --workspace et Clippy strict passent, et aucune suite testée ne rapporte d'échec. Les canaris récents passent notamment :

ksp-onchain-transport-lib unitaires : 396 passed / 0 failed
ksp-onchain-transport-lib release completeness : 49 passed / 0 failed
ksp-worker-raw-transaction-ingest-lib unitaires : 161 passed / 0 failed
ksp-app-raw-transaction-ingest-desk unitaires : 50 passed / 0 failed

La preuve live finale de la base pre.015-fix.001 est également cohérente :

21:52:59  Yellowstone Mainnet installé
21:53:02  HTTP Block Polling Mainnet installé
22:11:52  Stop Yellowstone demandé
22:11:57  Yellowstone -> Stopped / Healthy / fault none
22:11:59  Stop HTTP Block Polling demandé
22:12:02  HTTP Block Polling -> Stopped / Healthy / fault none

Sur ce run d'environ 19 minutes en parallèle :

worker_state=Faulted : aucune occurrence
grpc_backpressure_overflow : aucune occurrence
Yellowstone subscribe update queue overflowed : aucune occurrence
content_conflict terminal : aucune occurrence

pre.016 ne modifie aucun runtime. Le gate technique final attendu est donc :

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/0.3.15
cargo check --workspace
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test -p ksp-store-postgres-lib --all-targets --all-features
cargo test -p ksp-onchain-transport-lib --all-targets --all-features
cargo test -p ksp-worker-raw-transaction-ingest-lib --all-targets --all-features
cargo test -p ksp-app-raw-transaction-ingest-desk --all-targets --all-features
cargo test --workspace --all-targets --all-features
cargo tree -p ksp-onchain-transport-lib --edges normal
cargo tree -p ksp-onchain-transport-lib -e features
cargo tree -p ksp-worker-raw-transaction-ingest-lib --edges normal
cargo tree -p ksp-worker-raw-transaction-ingest-lib -e features
cargo tree -p ksp-app-raw-transaction-ingest-desk --edges normal
cargo tree -p ksp-app-raw-transaction-ingest-desk -e features
cargo tree --duplicates
(cd crates/ksp-app-raw-transaction-ingest-desk && cargo tauri build)

Aucun nouveau live n'est requis par cette archive tant que ce scope reste strictement mécanique/documentaire : la base immédiatement précédente possède déjà le run long requis et pre.016 ne change aucun fichier consommé par le runtime. Une modification runtime découverte nécessaire pendant le gate invaliderait cette non-claim et devrait être portée par un fix ou une nouvelle prerelease conforme au lifecycle.

pre.016 — clôture du gate opérateur

Le rejeu opérateur de 0.3.15-pre.016 ferme le gate technique final. Les audits annoncent :

General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
Markdown table audit: clean (318 tables, 229 files)

cargo check --workspace et Clippy strict terminent sans erreur. Les suites ciblées confirment notamment Store PostgreSQL 75/75 unitaires, Transport 396/396 unitaires et 49/49 release completeness, Worker 161/161 unitaires avec ses suites cross-layer/dependency/hardening/public/release, et Raw Transaction Ingest Desk 50/50 unitaires avec ses suites desktop/dependency/public/release. Le rejeu cargo test --workspace --all-targets --all-features ne contient aucun test result: FAILED, aucune erreur de compilation Rust et aucun panic dans le log fourni.

Les graphes demandés ont été produits pour Transport, Worker et Raw Transaction Ingest Desk, ainsi que cargo tree --duplicates. Enfin, cargo tauri build termine le profil release et produit les trois bundles Linux :

KSP Raw Transaction Ingest Desk_0.3.15-pre.16_amd64.deb
KSP Raw Transaction Ingest Desk-0.3.15-pre.16-1.x86_64.rpm
KSP Raw Transaction Ingest Desk_0.3.15-pre.16_amd64.AppImage

Aucun nouveau live n'était nécessaire : pre.016 n'a modifié aucun artefact runtime et la preuve longue immédiatement précédente reste applicable.

pre.017 — réconciliation documentaire finale

Cette tranche réconcilie uniquement les documents durables et le numéro de prerelease workspace imposé par VER-ID-009. Elle ne modifie aucun fichier src, tests, unit_tests, frontend, Config, schema, migration, manifest de crate ou dépendance. Elle fixe notamment dans la documentation active :

Yellowstone Block Mainnet -> trigger gRPC + HTTP getBlock Full/Base64
backpressure queue Yellowstone -> attente bornée/asynchrone vers le stream, pas d'overflow local
Stop Yellowstone -> status post-half-close coopératif ; status actif toujours fautif/reconnectable
canonique complet + incoming Log truncated compatible -> observation acceptée, canonique conservé
reverse promotion et variantes/résolutions générales -> 0.3.16
Raw Transaction Ingest Desk -> multi-route same-network, Workers indépendants, Store partagé
0.3.17 -> Backfill multi-route / multi-stratégie
0.3.18 -> Backfill Desk adapté

Non-claims : pre.017 ne transforme pas encore un vrai content_conflict en quarantaine durable, ne promeut pas un canonique tronqué vers une version complète, n'ajoute aucune politique Store retry/Transport reconnect configurable de 0.3.16, et ne modifie ni CHANGELOG.md, ni ROADMAP.md, ni le prompt suivant. Ces trois derniers artefacts appartiennent à pre.018 conformément à VER-LIFECYCLE-003 et VER-LIFECYCLE-006.

Gate documentaire attendu : audits Rust/Markdown, cargo fmt --all -- --check et cargo check --workspace suffisent à confirmer que le bump workspace 0.3.15-pre.17 et les documents restent cohérents avec la candidate déjà techniquement validée. Aucun nouveau live n'est requis tant que le différentiel reste strictement documentaire hors Cargo.toml racine.