# Plan `0.3.5` — Interface passive acquisition events ## 1. Statut de la release Base canonique auditée : ```text v0.3.4 workspace.package.version = 0.3.4 ``` Version de travail à la réconciliation documentaire finale : ```text workspace.package.version = 0.3.5-pre.7 label = 0.3.5-pre.007 ``` La release est fonctionnellement fermée. `pre.001` a ouvert l'audit ; son fix a établi l'intersection exacte de sept stages slot et rouvert le candidat transaction execution. `pre.002` a matérialisé `SlotLifecycleEvent`, `pre.003` a admis `TransactionExecutionEvent`, `pre.004`/`pre.005` ont fermé les canaris de complétude et le hardening, puis `pre.006` a validé le gate workspace et le graphe Cargo final. `pre.007` ne modifie aucun contrat Rust. Elle réconcilie les documents durables avec la surface réellement acquise et transfère l'idée `TransactionLogEvent` vers `docs/IDEAS.md` au lieu de laisser un TODO caché dans le plan de release. ## 2. Mission La release doit étendre `ksp-interface-lib` seulement lorsqu'un consumer de composition peut recevoir un fait passif KSP-owned sans dépendre du protocole producteur et sans créer un second modèle RAW. Le contrat cible reste : ```text producer Transport -> DTO Transport riche et lossless -> conversion explicite à la composition -> fait Interface passif minimal, seulement si la sémantique commune est prouvée ``` Le type Interface ne remplace jamais le DTO Transport. Les informations source-specific qui ne font pas partie du fait commun restent disponibles sur le DTO Transport et ne sont pas copiées dans Interface. ## 3. Sources relues ### 3.1 Sources internes Le gate a relu les familles prescrites par le prompt : ```text RULES.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 docs/architecture/000-README.md docs/architecture/001-PROJECT_OBJECTIVES.md docs/architecture/002-LAYERS_AND_DEPENDENCIES.md docs/architecture/003-COMPONENT_CONTRACTS.md docs/architecture/004-COMPONENT_INVENTORY.md docs/architecture/005-DEPENDENCY_GRAPH.md docs/architecture/006-WIRE_AND_PROGRAM.md docs/architecture/008-DATA_MATERIALIZATION_AND_STORE.md docs/architecture/009-ACQUISITION_WORKERS_AND_JOBS.md docs/architecture/010-APPS_SERVICES_SCENARIOS_AND_CONTROL.md docs/plans/020-V0_2_13_INTERFACE_PLAN.md docs/validation/016-V0_2_13_INTERFACE.md docs/plans/022-V0_3_1_STORE_RAW_PLAN.md docs/validation/018-V0_3_1_STORE_RAW.md docs/plans/023-V0_3_2_STORE_POSTGRES_FOUNDATION_PLAN.md docs/plans/024-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION_PLAN.md docs/plans/025-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT_PLAN.md docs/validation/019-V0_3_2_STORE_POSTGRES_FOUNDATION.md docs/validation/020-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION.md docs/validation/021-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT.md ``` Les sources Rust relues couvrent `ksp-interface-lib`, `ksp-store-api`, la façade/backend Store et les modules HTTP/WS/gRPC/Helius event-like de `ksp-onchain-transport-lib` demandés par le prompt. ### 3.2 Surface Interface/Store stable constatée Inventaire crate-root actuel de `ksp-interface-lib` : ```text Pubkey ProgramAccountMeta ProgramInstruction MAX_PROGRAM_INSTRUCTION_ACCOUNTS MAX_PROGRAM_INSTRUCTION_DATA_LEN ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED ``` Modules de production actuels : ```text error program_account_meta program_instruction ``` Le manifeste Interface ne possède qu'une dependency normale : ```text ksp-core-lib ``` Le consumer runtime/public actuel identifié est `ksp-program-api`, qui réutilise `ProgramAccountMeta` et `ProgramInstruction`. Aucun consumer actuel n'impose encore un event d'acquisition. La surface Store stable expose exactement dix capabilities RAW : ```text RawTransactionRead RawTransactionWrite RawTransactionObservationRead RawTransactionObservationWrite RawTransactionRetentionRead RawTransactionRetentionWrite RawAccountStateRead RawAccountStateWrite RawAccountObservationRead RawAccountObservationWrite ``` ### 3.3 Sources externes courantes auditées le 2026-08-31 L'audit externe a porté sur les documents normatifs/courants suivants : ```text Solana RPC WebSocket: - slotSubscribe - slotsUpdatesSubscribe - rootSubscribe / index WebSocket - signatureSubscribe - logsSubscribe - voteSubscribe - accountSubscribe - programSubscribe Solana RPC HTTP: - getSignatureStatuses - getTransaction - getBlock Yellowstone gRPC upstream et version KSP : - KSP stable déclare `yellowstone-grpc-proto = ^12.6` - geyser.proto / SlotStatus / SubscribeUpdateSlot - transaction_status - account / transaction / block / block_meta / entry update families - upstream courant comparé pour vérifier que l'intersection SlotStatus retenue n'est pas une hypothèse obsolète Helius: - LaserStream-powered standard WebSockets - Enhanced WebSocket transactionSubscribe ``` Les conclusions externes qui structurent le plan sont : - `slotSubscribe` signale le traitement d'un nouveau slot et transporte `slot`, `parent` et le root courant ; - `slotsUpdatesSubscribe` est instable et publie `firstShredReceived`, `completed`, `createdBank`, `frozen`, `dead`, `optimisticConfirmation` et `root`, avec metadata conditionnelle ; - Yellowstone expose `Processed`, `Confirmed`, `Finalized`, `FirstShredReceived`, `Completed`, `CreatedBank` et `Dead` ; - `signatureSubscribe` est un abonnement one-shot à une signature, éventuellement précédé de `receivedSignature`, et n'est pas un snapshot HTTP ; - `getSignatureStatuses` reste un snapshot queryable avec confirmations/status/confirmationStatus ; - `voteSubscribe` publie des votes gossip pré-consensus, sans garantie qu'ils entrent dans le ledger ; - Helius conserve les méthodes standard Solana et possède en plus des extensions Enhanced WebSocket, notamment `transactionSubscribe`, qui restent provider-specific. ## 4. Invariants d'ownership Les règles directement structurantes restent : ```text KSP-TRANSPORT-002..004 KSP-STORE-001..002 KSP-NOTIFY-001..006 KSP-PROC-007..008 KSP-REL-005..016 DEP-KSP-* DEP-LOG-* DEP-STORE-* DEP-WORKER-* DEP-JOB-* ``` Le graphe cible reste : ```text ksp-interface-lib └── ksp-core-lib ``` Et les frontières restent : ```text Interface -X-> Transport Interface -X-> Store API / Store Interface -X-> Program API / Program Lib Interface -X-> Config / Wallet Transport -X-> Store API Store API -X-> Transport composition supérieure = owner des conversions ``` `ksp-program-api` reste un consumer de la surface Program-facing existante de `ksp-interface-lib`. Il n'a aucune raison de re-exporter les futurs événements d'acquisition. ## 5. Critères d'admission d'une famille Interface Une famille n'est admise que si toutes les conditions suivantes sont vraies : 1. le type est passif et ne possède aucun lifecycle/runtime ; 2. le fait est provider-neutral ; 3. il ne duplique pas un modèle persistant/replayable de `ksp-store-api` ; 4. l'intersection sémantique entre producers est précise et documentable ; 5. au moins deux producers/converters peuvent produire exactement ce fait, ou plusieurs consumers ont un besoin identique déjà démontré ; 6. le type n'est pas une union d'options destinée à masquer des sémantiques différentes ; 7. les détails non communs peuvent rester sur les DTOs Transport sans forcer Interface à devenir lossless pour le protocole source ; 8. un usage de composition proche est identifiable. Une égalité de noms ne suffit pas. Une proximité de commitment ne suffit pas. Une possibilité théorique de conversion ne suffit pas. ## 6. Inventaire producer -> nature de fait | Producer / surface | Nature du fait | Champs structurants obligatoires | Optionalité utile | Ordre / lifecycle | Timestamp semantics | Error semantics | Metadata session/provider | Volume potentiel | Persistent/replayable ? | |----------------------------------------|-------------------------------------|-------------------------------------------------------|-----------------------------------|---------------------------------------------------|-------------------------------------------|----------------------------------------|----------------------------------------|-----------------------|-----------------------------------------| | Solana WS `slotSubscribe` | progression de slot | slot, parent, root | aucune | event répété à chaque slot traité | aucun | aucune | subscription id hors DTO métier | minime | non | | Solana WS `rootSubscribe` | progression de root | root slot | aucune | event répété lors du changement de root | aucun | aucune | subscription id | minime | non | | Solana WS `slotsUpdatesSubscribe` | lifecycle interne slot | slot, type, timestamp | parent/stats/error selon variante | multi-event par slot ; méthode instable | Unix update timestamp en ms | texte seulement pour `dead` | subscription id | faible à modéré | non | | Solana WS `logsSubscribe` | logs transaction realtime | signature, err, logs + context slot | err nullable | flux continu selon filtre/commitment | aucun provider timestamp | opaque RPC transaction error | filtre + commitment + subscription id | logs non bornés wire | non par défaut | | Solana WS `signatureSubscribe` | réception/terminal signature | signature en request ; context slot + result en event | received précoce | one-shot terminal ; reçu optionnel avant terminal | aucun | transaction error au terminal | commitment/request/subscription | minime | non | | Solana HTTP `getSignatureStatuses` | snapshot de status | signatures request ; slot/status/confirmationStatus | nulls/confirmations | requête ponctuelle, pas une transition | aucun | transaction error dans snapshot | search history request policy | faible | queryable côté node, pas event KSP | | Solana WS `voteSubscribe` | vote gossip pré-consensus | votePubkey, slots, hash, signature | timestamp | flux gossip ; aucune garantie ledger | timestamp vote optionnel | aucune erreur transaction canonique | subscription id | faible | non | | Solana WS `accountSubscribe` | changement d'état account | context slot + account payload | selon encoding/dataSlice | flux d'état selon commitment | aucun | payload account, pas error event | commitment/encoding/subscription | potentiellement élevé | peut alimenter `RawAccountState` | | Solana WS `programSubscribe` | changement account d'un programme | context + pubkey + account payload | selon encoding/filters | flux d'état filtré | aucun | payload account | program/filter/commitment/subscription | potentiellement élevé | peut alimenter `RawAccountState` | | Solana WS `blockSubscribe` | bloc/transactions realtime | slot/context + block ou erreur | block nullable | flux de blocs selon config | blockTime dans payload si présent | RPC/block error | filter/config/subscription | très élevé | conteneur d'acquisition | | Yellowstone `Slot` | lifecycle/commitment slot | slot, status | parent/dead_error/created_at | plusieurs statuts possibles par slot | `created_at` = création serveur update | dead diagnostic optionnel | filter echo + server metadata | faible | non | | Yellowstone `TransactionStatus` | update execution/status transaction | slot, signature, is_vote, index | err/created_at | flux transaction status | server `created_at` | erreur transaction encodée | filter echo | faible | non par lui-même | | Yellowstone `Account` | état account + provenance Geyser | account bytes/info + slot + is_startup | txn signature/created_at | flux state/replay startup | server `created_at` | decode/transport error hors payload | filters + write/version metadata | potentiellement élevé | peut alimenter `RawAccountState` | | Yellowstone `Transaction` | transaction complète + meta | slot + transaction info/meta | created_at | flux transaction | server `created_at` | transaction meta error | filters + transaction index | très élevé | peut alimenter `RawTransaction` | | Yellowstone `Block` / `BlockMeta` | bloc / metadata de bloc | slot + block/meta fields | champs block spécifiques | flux block/meta | server `created_at` | payload/meta-specific | filters | très élevé | conteneur d'acquisition | | Yellowstone `Entry` | entry Geyser | slot/index/entry-specific fields | created_at | flux protocol-specific | server `created_at` | protocol-specific | filters | modéré | non retenu | | Helius standard WebSocket | mêmes faits standard Solana | mêmes champs standard | mêmes optionalités | même wire sémantique, runtime provider différent | mêmes semantics Solana | mêmes semantics Solana | Helius session/runtime | selon méthode | suit la famille standard | | Helius Enhanced `transactionSubscribe` | transaction provider-filtered | slot/signature ou transaction selon details | options/detail modes | extension provider continue | provider payload semantics | provider/RPC semantics | filtres Helius + options + session | faible à très élevé | acquisition possible, Transport-owned | | Store notification after commit | wake-up de donnée durable | future référence durable compacte | à définir avec publisher/consumer | après commit ; jamais source de vérité | non requis par le contrat normatif actuel | code safe éventuel, pas payload source | mécanisme de diffusion indépendant | minime | référence une persistence déjà commitée | ### 6.1 Consumers actuels et imminents | Consumer | Besoin actuel / prévu | Dépendance à Interface event | Décision `pre.001` | |-------------------------------------------|-----------------------------------------------------------|------------------------------|---------------------------------------------------------------------------| | `ksp-program-api` | ProgramInstruction / ProgramAccountMeta | non | préserver la surface Program existante ; aucun re-export acquisition | | `0.3.6` premier backfill RAW | range historique -> Transport -> RAW ingestion -> Store | non | ne pas forcer SlotLifecycle dans le backfill | | futur worker RAW live | subscriptions/fetch live multi-transport -> RAW ingestion | oui, candidat concret | consumer proche justifiant une projection slot lifecycle provider-neutral | | futurs processors CORE/DECODE/SPECIALIZED | backlog Store + wake-up post-commit | non pour event réseau | consommer Store/backlog ; ne pas détourner les events réseau | | apps de contrôle/inspection futures | observabilité et commandes de composants | non démontré | aucun type Interface ajouté uniquement pour une UI future | ## 7. Matrice de convergence sémantique | Nature candidate | Sources comparées | Intersection exacte retenue | Différences non représentables sans perte | Projection commune lossless pour le fait ciblé ? | Consumer concret | Owner | Décision | |-------------------------------|-----------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------|-------------------------------------------------------|-------------------------------|--------------------------------|------------------| | lifecycle slot | slotSubscribe + slotsUpdates + rootSubscribe + Yellowstone Slot | Processed, FirstShredReceived, Completed, CreatedBank, Dead, OptimisticallyConfirmed, Rooted | Frozen ; parent/timestamps/diagnostics ; noms wire Confirmed/Finalized côté Yellowstone | oui, pour l'occurrence `slot + stage` | futur worker RAW live | Interface | ADMIS | | transaction execution | logsSubscribe + Yellowstone TransactionStatus + Helius transactionSubscribe | `slot + TransactionSignature + Succeeded/Failed` | metadata/provider/detail modes ; commitment/delivery ; erreur source | oui pour le fait d'exécution observée | futur worker RAW live | Interface | ADMIS `pre.003` | | logs realtime | logsSubscribe + transaction/meta Yellowstone/Helius | logs associés à une transaction | volume/bornes, richesse de source, disponibilité selon détail | non admis dans cette release sans consumer/bornes | hydratation possible | Interface si futur gate | IDÉE DIFFÉRÉE | | signature commitment/snapshot | getSignatureStatuses + signatureSubscribe | identité de signature seulement | snapshot HTTP vs transition one-shot commitment | non comme event d'exécution | aucun contrat unique démontré | Transport | REPORTÉ | | vote | voteSubscribe + Yellowstone transaction `is_vote` | qualificatif « vote » seulement | gossip pré-consensus vs transaction exécutée | non | aucun besoin commun démontré | Transport | REPORTÉ | | account/program | WS account/program + Yellowstone Account | état account complet possible | persistance/replay déjà possédés par RawAccountState/Observation | non comme event distinct | RAW ingestion | Store/Transport | REJET Interface | | transaction complète | HTTP/WS/Helius/Yellowstone transaction | transaction complète selon source | format/commitment/provider metadata ; RawTransaction déjà canonique Store | non comme second type | RAW ingestion | Store/Transport | REJET Interface | | block | getBlock/blockSubscribe/Yellowstone Block | slot + block container | payloads/options/reconstruction ; pas de consumer passif minimal prouvé | non | extraction RAW transaction | Transport | REJET Interface | | Yellowstone Entry | Yellowstone aujourd'hui | aucune multi-source démontrée | mono-producer et protocole-specific aujourd'hui | non aujourd'hui ; futur fait transversal -> Interface | aucun actuel | Transport DTO | REPORTÉ | | persisted-data available | Store after commit | référence durable compacte conceptuelle | event réseau sans relation ; owner déjà normé par KSP-NOTIFY | oui mais hors owner Interface | processors futurs | Store API | REJET Interface | | Helius Enhanced transaction | Helius transactionSubscribe | peut produire `TransactionExecutionEvent` lorsque signature/slot/outcome sont déterminables | filtres/options/provider behavior et DTO riche | DTO non ; projection sémantique admise via converter | futur worker RAW live | Transport DTO / Interface fact | PRODUCTEUR ADMIS | ### 7.1 Lifecycle de slot — ADMIS sous intersection stricte | Fait commun | Solana standard | Yellowstone | Stage KSP | Décision Interface | Metadata volontairement non commune | |--------------------------|-----------------------------------------|----------------------|---------------------------|--------------------|--------------------------------------------------------------------------------| | slot traité | `slotSubscribe` | `Processed` | `Processed` | ADMIS | parent/root courant côté WS ; filters/created_at/parent côté Yellowstone | | premier shred reçu | `FirstShredReceived` | `FirstShredReceived` | `FirstShredReceived` | ADMIS | timestamp WS ; created_at/filters côté Yellowstone | | ingestion slot complétée | `Completed` | `Completed` | `Completed` | ADMIS | timestamp WS ; created_at/filters côté Yellowstone | | bank créé | `CreatedBank` | `CreatedBank` | `CreatedBank` | ADMIS | parent requis WS, optionnel Yellowstone ; il reste dans le DTO Transport riche | | slot mort | `Dead` | `Dead` | `Dead` | ADMIS | diagnostics source-specific et horodatages restent Transport | | confirmation optimiste | `OptimisticConfirmation` | `Confirmed` | `OptimisticallyConfirmed` | ADMIS | le nom KSP conserve la sémantique Solana ; delivery reste Transport | | root atteint | `rootSubscribe` / `slotsUpdates` `Root` | `Finalized` | `Rooted` | ADMIS | le nom KSP évite de généraliser `Finalized` au-delà du mapping audité | | frozen | `Frozen` | aucun statut exact | — | REJETÉ | pas d'intersection | Le fait commun est volontairement plus petit que chaque DTO producteur. La conversion **n'efface pas** le DTO Transport : elle ajoute une projection passive `slot + stage` pour les consumers qui n'ont besoin que de l'occurrence de l'étape commune. Le timestamp n'entre pas dans le contrat commun : `slotsUpdatesSubscribe.timestamp` décrit l'horodatage Unix de l'update côté validator alors que `YellowstoneUpdateTimestamp` est une metadata de création serveur. Les réunir sous un seul champ produirait une fausse équivalence. Le `parent`, le root courant exposé comme metadata de certaines notifications, `dead_error` et les détails de delivery restent source-owned ; un consumer qui en a besoin doit conserver le DTO Transport ou définir un contrat supérieur spécifique. Les mappings `OptimisticConfirmation -> OptimisticallyConfirmed <- Yellowstone Confirmed` et `Root -> Rooted <- Yellowstone Finalized` sont des normalisations sémantiques documentées, pas des renommages universels des commitments Solana. ### 7.2 Transaction execution — ADMIS `pre.003` `pre.003` confirme que l'**exécution observée d'une transaction** est distincte des snapshots/commitments de signature et possède une intersection multi-producer compacte : ```text Solana logsSubscribe -> context slot + signature base58 + err Yellowstone TransactionStatus -> slot + signature [u8; 64] + err + metadata Yellowstone Helius transactionSubscribe -> slot + signature + résultat d'exécution lorsque le detail mode expose l'erreur ``` Le contrat admis est : ```text TransactionSignature([u8; 64]) TransactionExecutionOutcome { Succeeded, Failed, } TransactionExecutionEvent { slot, signature, outcome, } ``` Décisions du gate : - `TransactionSignature` est une primitive passive Interface-owned de **64 octets déjà décodés** ; Interface n'ajoute ni base58, ni codec, ni dépendance Solana supplémentaire ; - cette primitive représente l'identité transactionnelle observée, pas une capability de signature Wallet ; - `logsSubscribe` fournit directement `slot`, `signature` et `err`; `err == null` produit `Succeeded`, une erreur produit `Failed` ; - Yellowstone `TransactionStatus` fournit exactement `slot`, signature et erreur optionnelle ; `is_vote`, `index`, filters et `created_at` restent Transport ; - Helius `transactionSubscribe` reste provider-specific : une projection n'est produite que lorsque le DTO/detail mode permet de déterminer `slot`, signature et outcome sans ambiguïté ; une forme `none`, inconnue ou sans information d'erreur ne fabrique aucun outcome ; - commitment, transaction index, logs, memo, block time, confirmation status, provider timestamps et erreurs opaques restent hors Interface ; - le futur worker RAW live peut utiliser ce fait comme signal d'acquisition/hydratation provider-neutral sans le confondre avec une donnée RAW persistée. `TransactionSignature` ne remplace pas `ksp-store-api::RawTransactionSignature`. Les deux contrats ont des owners distincts : Interface possède l'identité compacte d'un **event-only fact**, Store possède l'identité d'une **référence RAW persistante**. La conversion explicite entre les deux appartient à la composition conformément à `DEP-KSP-003`; aucune dépendance Interface -> Store n'est introduite. Les DTOs `logsSubscribe`, Yellowstone `TransactionStatus` et Helius `transactionSubscribe` restent intégralement Transport-owned. Interface ne contient aucun converter depuis ces types. ### 7.3 Logs realtime et signature commitment — DIFFÉRENCIÉS Les logs ne sont plus utilisés comme argument pour rejeter l'exécution commune : `logsSubscribe` peut produire un `TransactionExecutionEvent` sans que les lignes de logs entrent dans le contrat partagé. Une éventuelle famille distincte reste une **idée différée** : ```text TransactionLogEvent { slot, signature, logs, } ``` Elle n'est pas admise en `0.3.5` tant que les bornes, la disponibilité multi-producer et un consumer concret ne sont pas démontrés. Aucun `RawLog` n'est créé. Les surfaces de commitment/snapshot restent séparées de l'exécution : ```text HTTP getSignatureStatuses = snapshot interrogé, avec confirmations/status/confirmationStatus WS signatureSubscribe = transition one-shot vers le commitment demandé, éventuellement précédée de ReceivedSignature ``` Aucun `TransactionCommitmentEvent` n'est admis. Une future famille de commitment devra être auditée indépendamment et ne pourra pas être fusionnée avec `TransactionExecutionEvent` par une struct à nombreux `Option`. ### 7.4 Vote — REPORTÉ `voteSubscribe` est explicitement pré-consensus et ne garantit pas l'entrée du vote dans le ledger. Le booléen `is_vote` de la famille transaction Yellowstone qualifie une transaction et n'est pas une notification gossip équivalente. Aucun `VoteEvent` partagé n'est admis. ### 7.5 Account/program — REJET Interface Les updates account/program peuvent contenir l'état complet nécessaire à l'acquisition RAW. `ksp-store-api` possède déjà : ```text RawAccountState RawAccountObservation ``` Créer un second account event structurel dans Interface serait soit : - un doublon persistant déguisé ; - un wrapper des DTOs Transport ; - un type incomplet qui perd les conditions d'admission RAW. La conversion Transport -> Store API reste à la composition. ### 7.6 Transaction/block — REJET Interface `RawTransaction` et `RawTransactionObservation` sont déjà Store-owned. Les blocs servent de conteneurs d'acquisition ou de payloads Transport riches. Aucun `RawBlock`, `TransactionEvent` ou `BlockEvent` transversal n'est ajouté. ### 7.7 Yellowstone Entry — Transport aujourd'hui, ownership sémantique réservé `SubscribeUpdateEntry` reste un DTO Yellowstone strictement Transport-owned : un seul producer/protocole est démontré aujourd'hui et aucun consumer transversal proche ne justifie un type partagé. Cette décision ne réserve toutefois pas le **fait** à Transport : si une future seconde source permet de reconstruire un même `LedgerEntryEvent` provider-neutral, ce nouveau fait passif devra être audité pour ownership Interface, sans déplacer le DTO Yellowstone lui-même. ### 7.8 Notification after commit — REJET Interface Les règles `KSP-NOTIFY-001..006` sont explicites : le format canonique du signal « persisted data available » appartient à `ksp-store-api`, est publié après commit et ne constitue jamais le backlog. `0.3.5` ne crée aucun équivalent Interface. ## 8. API sketch admise La seule API planifiée pour `pre.002` est conceptuellement : ```rust #[non_exhaustive] pub enum SlotLifecycleStage { Processed, FirstShredReceived, Completed, CreatedBank, Dead, OptimisticallyConfirmed, Rooted, } pub struct SlotLifecycleEvent { slot: u64, stage: SlotLifecycleStage, } ``` Surface publique visée : ```text SlotLifecycleStage SlotLifecycleEvent::new(slot, stage) SlotLifecycleEvent::slot() SlotLifecycleEvent::stage() ``` Décisions de représentation : ```text SlotLifecycleStage -> #[non_exhaustive] + Clone + Copy + Debug + Eq + PartialEq SlotLifecycleEvent -> champs privés + Clone + Copy + Debug + Eq + PartialEq Hash -> absent tant qu'un consumer réel ne l'exige pas validation -> aucune borne variable ; slot u64 conservé exactement error code -> aucun ; aucune admission faillible n'est nécessaire ``` Contraintes : - pas de serde imposé ; - pas de `String`, JSON, bytes, timestamp ou diagnostic ; - pas de network id dupliquant `RawNetworkId` ; la composition est déjà liée à son contexte réseau ; - `Debug` est sûr par construction ; - aucun error code n'est nécessaire si tout `u64` de slot et toute variante publique construite sont valides ; - aucune dépendance nouvelle n'est requise ; - aucun trait universel `Event`, `EventSource`, `Subscriber` ou `Handler` ; - aucune conversion `From` dans Interface, car cela créerait une dépendance interdite ; - les converters appartiennent au futur consumer/composition ou, si un usage concret le justifie plus tard, à une crate de composition dédiée. Le nom final peut être ajusté pendant `pre.002` seulement si les canaris montrent une ambiguïté réelle. Le sens `slot + stage commun` ne doit pas être élargi. `OptimisticallyConfirmed` et `Rooted` sont volontairement préférés à `Confirmed` et `Finalized` afin de conserver la sémantique KSP du fait normalisé sans transformer les labels Yellowstone en terminologie universelle. ## 9. Consumer proche et usage concret L'architecture prévoit un futur worker RAW live : ```text subscriptions/fetch live -> ksp-onchain-transport-lib -> RAW ingestion -> D1 RAW ``` Ce worker est le consumer proche qui peut recevoir des événements de progression/lifecycle de slot venant soit des WebSockets standard, soit de Yellowstone sans connaître les DTOs des deux protocoles lorsqu'il n'a besoin que de déclencher/ordonner une réaction de composition propre au lifecycle. Le même worker peut consommer `TransactionExecutionEvent` comme signal compact qu'une signature a été exécutée dans un slot avec succès ou échec, puis décider dans sa propre policy/composition s'il doit hydrater/persister un `RawTransaction`. Le fait Interface ne contient ni payload ni provenance et n'est jamais utilisé comme preuve durable de présence dans Store. Ces APIs ne deviennent pas un prérequis de `0.3.6` : le premier backfill historique reste fondé sur Transport + Store et peut ne jamais consommer les events Interface. ## 10. Anti-duplication Store | Contrat Store comparé | Chevauchement Interface admis | Pourquoi il n'y a pas de seconde vérité | |-----------------------------|------------------------------------------------|------------------------------------------------------------------------------------------------------| | `RawTransaction` | slot + signature possibles via execution event | aucun payload/format/hash/block_time ; l'event ne peut reconstruire ni remplacer une transaction RAW | | `RawTransactionReference` | signature sémantiquement apparentée | la référence Store inclut `RawNetworkId` et possède l'identité durable ; Interface reste contextuel | | `RawTransactionSignature` | même primitive protocolaire 64 octets | wrapper Store et primitive Interface restent owner-specific ; conversion explicite à la composition | | `RawTransactionObservation` | référence transaction possible indirectement | aucune provenance acquisition, observation key ou timestamp durable | | `RawAccountState` | slot possible via lifecycle seulement | aucune pubkey/state hash/lamports/owner/data ; aucun état account | | `RawAccountObservation` | slot indirect possible | aucune référence account/provenance Geyser | | `RawAcquisitionProvenance` | aucun | pas d'origin/provider code/timestamps ; les metadata source restent Transport/composition | | `RawTimestamp` | aucun | aucun timestamp commun inventé | | `RawContentHash` | aucun | aucune identité de contenu durable | | `RawObservationKey` | aucun | aucune identité d'observation persistante | | notification after commit | aucun | event réseau observe l'acquisition ; wake-up Store référence une donnée déjà commitée | Le gate conserve les canaris conceptuels suivants : ```text SlotLifecycleEvent -X-> RawTransaction TransactionExecutionEvent -X-> RawTransaction TransactionExecutionEvent -X-> RawTransactionObservation TransactionSignature -X-> RawTransactionSignature implicit conversion Interface -X-> ksp-store-api Interface -X-> ksp-store-lib Store API -X-> Interface ``` Une couche de composition peut convertir explicitement `TransactionSignature::as_bytes()` vers `RawTransactionSignature::new(...)`, mais aucun `From` cross-domain n'est possédé par Interface ou Store API. Aucun champ d'un event admis ne constitue à lui seul une preuve RAW durable. ## 11. Graphe cible Après implémentation de la release : ```text ksp-interface-lib └── ksp-core-lib ``` Aucune nouvelle dependency normale ou feature n'est planifiée. Les consumers restent : ```text ksp-program-api -> ksp-interface-lib # ProgramInstruction / ProgramAccountMeta seulement future acquisition composition / RAW live worker -> ksp-onchain-transport-lib -> ksp-interface-lib # SlotLifecycleEvent / TransactionExecutionEvent -> ksp-store-lib / ksp-store-api selon ownership ``` ## 12. Threat model | Risque | Menace | Garde planifiée | |--------------------------------|----------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------| | mega enum | Interface devient catalogue de tous les transports | familles admises séparées et étroites ; aucun `Event` générique | | Option soup | sémantiques différentes fusionnées par champs optionnels | familles séparées : SlotLifecycle / TransactionExecution ; logs/commitment restent distincts | | persistence creep | event-only devient second Store | aucun RAW/payload/cursor/retention ; `KSP-NOTIFY-*` reste Store API | | transport wrapper | copie des DTOs WS/gRPC/provider | aucune metadata protocol/provider dans le type Interface | | faux timestamp commun | horodatages WS et Yellowstone confondus | aucun timestamp partagé | | root/finality confusion | label Yellowstone `Finalized` promu comme vérité universelle | stage KSP `Rooted` ; mapping source explicite `Root <-> Yellowstone Finalized` sans renommer le fait KSP | | optimistic/confirmed confusion | label Yellowstone `Confirmed` promu comme commitment générique | stage KSP `OptimisticallyConfirmed` ; mapping source explicite avec l'optimistic confirmation Solana | | dead diagnostic leak | texte provider/validator traverse une API partagée durable | stage `Dead` sans diagnostic ; détail reste Transport | | cross-network confusion | event sans réseau mélangé entre sessions | conversion autorisée seulement dans une composition déjà liée à un contexte réseau ; pas de network id dupliqué | | signature/payload leak | signature ou payload brut fuit via Debug | signature fixe 64 octets, Debug redacted ; aucun payload variable | | public enum breakage | ajout futur casse les matches externes | `SlotLifecycleStage` et `TransactionExecutionOutcome` non-exhaustive | | logging/runtime creep | crate passive acquiert tracing/Tokio/channel | aucune dependency/runtime/logging ajoutée | | Helius semantic promotion | DTO/provider extension devient norme KSP | `transactionSubscribe` reste Transport ; seule une projection sémantique commune peut devenir Interface | | Yellowstone protocol promotion | DTO `Entry`/filters/created_at deviennent Interface | types Yellowstone restent Transport ; un futur fait transversal est réaudité séparément | ## 13. Stratégie de tests ### 13.1 `pre.002` — modèle admis Tests unitaires Interface : - chaque stage public est distinct ; - `slot` est conservé sans narrowing ; - le constructeur/getters sont passifs et déterministes ; - le type n'emporte aucune metadata de transport. Tests d'intégration : - crate-root public API ; - external consumer ; - dependency boundary Core-only ; - exact production module/export inventory. ### 13.2 `pre.003` — transaction execution admis Tests unitaires Interface : - `TransactionSignature` conserve exactement 64 octets et masque ses bytes en `Debug` ; - `Succeeded` et `Failed` sont distincts ; - `TransactionExecutionEvent` conserve `u64::MAX`, signature et outcome sans narrowing ; - `Debug` d'event ne rend pas la signature. Tests d'intégration : - les trois nouveaux symboles sont disponibles depuis le crate-root ; - `TransactionExecutionOutcome` impose un wildcard downstream via `#[non_exhaustive]` ; - l'inventaire crate-root passe de 8 à 11 reexports ; - l'inventaire de production ajoute uniquement `transaction_execution`. ### 13.3 Convergence sans dépendance Transport `ksp-interface-lib` ne doit pas ajouter `ksp-onchain-transport-lib` même en dépendance normale pour « tester » les converters. Les preuves d'équivalence sémantique restent dans le plan/validation de release et les canaris de frontière Interface. Si un converter concret est introduit plus tard dans une crate de composition, sa propre crate devra tester les mappings exacts : ```text SolanaSlotNotification -> Processed SolanaSlotUpdate::FirstShredReceived -> FirstShredReceived SolanaSlotUpdate::Completed -> Completed SolanaSlotUpdate::CreatedBank -> CreatedBank SolanaSlotUpdate::Dead -> Dead SolanaSlotUpdate::OptimisticConfirmation -> OptimisticallyConfirmed SolanaRoot / SolanaSlotUpdate::Root -> Rooted YellowstoneSlotStatus::Confirmed -> OptimisticallyConfirmed YellowstoneSlotStatus::Finalized -> Rooted YellowstoneSlotStatus autres équivalents -> mêmes stages ``` Et devra explicitement refuser `Frozen` ou toute variante future sans mapping audité au lieu d'inventer un faux stage commun. ### 13.4 Traçabilité des candidats différés `TransactionExecutionEvent` est admis par `pre.003` et n'est donc plus un TODO. `TransactionLogEvent` reste explicitement une idée différée. Si cette idée reste hors scope à la réconciliation documentaire finale, elle doit être reportée dans la surface TODO/IDEAS durable appropriée au lieu d'être perdue avec le plan de release. ## 14. Prévision souple des prereleases La release reste dimensionnée pour une seule session. `SlotLifecycleEvent` est matérialisé en `pre.002` et `TransactionExecutionEvent` devient la seconde famille admise en `pre.003`; aucune troisième famille n'est ouverte sans nouveau gate. ### `0.3.5-pre.001` — audit + plan - lecture des règles/architectures/surfaces ; - audit externe actuel ; - matrice producer/fact et convergence ; - ownership et anti-duplication Store ; - threat model ; - API sketch ; - sizing et plan de release. ### `0.3.5-pre.002` — `SlotLifecycleEvent` - implémenter la famille minimale admise ; - crate-root exports ; - tests unitaires et public API ciblés ; - préserver le graphe Core-only. ### `0.3.5-pre.003` — `TransactionExecutionEvent` - gate signature/dependency : PASS avec `TransactionSignature([u8; 64])` passive, sans codec ni dépendance supplémentaire ; - mappings Solana `logsSubscribe` / Yellowstone `TransactionStatus` / Helius `transactionSubscribe` : PASS sous projection conservative ; - anti-duplication Store : PASS, conversion explicite Interface <-> Store à la composition ; - consumer concret : PASS, futur worker RAW live ; - implémenter `TransactionSignature`, `TransactionExecutionOutcome` et `TransactionExecutionEvent` seulement ; - ne pas introduire logs, commitment, metadata provider ou converters Transport dans Interface. ### `0.3.5-pre.004` — canaris externes + complétude API - external consumer pour toutes les familles effectivement admises ; - inventaire exact des modules/exports ; - canaris négatifs contre Transport/Store/runtime/serde/logging ; - non-exhaustive et stabilité de la surface. ### `0.3.5-pre.005` — hardening final - adversarial/API hardening ; - absence de payload/source metadata ; - absence de second RAW ; - validation du graphe et des dépendances ; - aucun élargissement fonctionnel opportuniste. ### `0.3.5-pre.006` — gate technique final - gates Rust/workspace complets ; - `cargo tree` Interface normal/features/duplicates ; - aucun smoke live requis : les contrats Interface restent passifs et ne possèdent aucun réseau. ### `0.3.5-pre.007` — réconciliation documentaire finale - plan + validation réconciliés avec le gate `pre.006` propre ; - README/USAGE Interface durables et version-neutral ; - rôle Interface explicité dans les documents d'architecture réellement devenus incomplets ; - `TransactionLogEvent` transféré dans `docs/IDEAS.md` comme idée différée ; - aucun CHANGELOG/ROADMAP/prompt suivant dans cette tranche. ### `0.3.5-pre.008` — préparation de publication Lane minimale : ```text Cargo.toml CHANGELOG.md ROADMAP.md prompts/025-V0_3_6_START_PROMPT.md deltas/0.3.5/pre.008.md ``` Aucun code, README/USAGE, plan ou validation ne doit être rouvert ici. ### `0.3.5-rel.001` — stable - mécanique de publication uniquement ; - version Cargo finale `0.3.5` ; - aucun rattrapage fonctionnel/documentaire. ## 15. Gates opérateur ### 15.1 Après chaque overlay fonctionnel ```text cargo fmt --all 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.5 cargo check --workspace cargo clippy --workspace --all-targets cargo test -p ksp-interface-lib ``` Ajouter `cargo test -p ksp-program-api` lorsqu'une modification publique Interface pourrait affecter son consumer actuel. ### 15.2 Gate technique final ```text cargo fmt --all 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.5 cargo check --workspace cargo clippy --workspace --all-targets cargo test -p ksp-interface-lib cargo test --workspace cargo tree -p ksp-interface-lib --edges normal cargo tree -p ksp-interface-lib -e features cargo tree --duplicates ``` Aucun live test n'est justifié par `0.3.5` : un type passif ne doit pas acquérir de dépendance réseau pour prouver son existence. ## 16. Hors périmètre ```text grand enum Event event bus / channel / broadcaster scheduler / worker / job backfill 0.3.6 persistence ou migration Store nouvelle capability Store RawLog / RawBlock TransactionStatusObservation fusionné TransactionCommitmentEvent fusionné avec execution TransactionLogEvent sans nouveau gate de bornes/consumer VoteEvent partagé label KSP `Finalized` utilisé comme alias universel de root label KSP `Confirmed` utilisé comme alias universel d'optimistic confirmation Helius Enhanced DTO dans Interface Yellowstone Entry DTO dans Interface serde/wincode/borsh ajouté sans protocole réel logging/runtime dans Interface conversion Transport -> Interface possédée par Interface ``` ## 17. Critère de clôture de `0.3.5` La release peut fermer si et seulement si : ```text une famille minimale SlotLifecycle est publique et bornée intersection = Processed + FirstShredReceived + Completed + CreatedBank + Dead + OptimisticallyConfirmed + Rooted Frozen et toute variante future sans mapping audité ne sont pas normalisées artificiellement TransactionExecutionEvent est admis en pre.003 sous la forme minimale slot + TransactionSignature + outcome aucun payload/diagnostic/timestamp/provider metadata n'entre dans Interface Interface reste Core-only Store RAW 10/10 reste inchangé KSP-NOTIFY ownership reste Store API aucun event bus/runtime/persistence n'est ajouté external consumer et crate-root canaries passent README/USAGE finaux restent durables et version-neutral ``` Si l'implémentation montre qu'un de ces invariants ne peut pas être préservé, la famille SlotLifecycle est retirée plutôt qu'élargie artificiellement.