50 KiB
Plan 0.3.5 — Interface passive acquisition events
1. Statut de la release
Base canonique auditée :
v0.3.4
workspace.package.version = 0.3.4
Version de travail à la réconciliation documentaire finale :
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 :
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 :
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 :
Pubkey
ProgramAccountMeta
ProgramInstruction
MAX_PROGRAM_INSTRUCTION_ACCOUNTS
MAX_PROGRAM_INSTRUCTION_DATA_LEN
ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED
Modules de production actuels :
error
program_account_meta
program_instruction
Le manifeste Interface ne possède qu'une dependency normale :
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 :
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 :
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 :
slotSubscribesignale le traitement d'un nouveau slot et transporteslot,parentet le root courant ;slotsUpdatesSubscribeest instable et publiefirstShredReceived,completed,createdBank,frozen,dead,optimisticConfirmationetroot, avec metadata conditionnelle ;- Yellowstone expose
Processed,Confirmed,Finalized,FirstShredReceived,Completed,CreatedBanketDead; signatureSubscribeest un abonnement one-shot à une signature, éventuellement précédé dereceivedSignature, et n'est pas un snapshot HTTP ;getSignatureStatusesreste un snapshot queryable avec confirmations/status/confirmationStatus ;voteSubscribepublie 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 :
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 :
ksp-interface-lib
└── ksp-core-lib
Et les frontières restent :
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 :
- le type est passif et ne possède aucun lifecycle/runtime ;
- le fait est provider-neutral ;
- il ne duplique pas un modèle persistant/replayable de
ksp-store-api; - l'intersection sémantique entre producers est précise et documentable ;
- au moins deux producers/converters peuvent produire exactement ce fait, ou plusieurs consumers ont un besoin identique déjà démontré ;
- le type n'est pas une union d'options destinée à masquer des sémantiques différentes ;
- les détails non communs peuvent rester sur les DTOs Transport sans forcer Interface à devenir lossless pour le protocole source ;
- 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 :
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 :
TransactionSignature([u8; 64])
TransactionExecutionOutcome {
Succeeded,
Failed,
}
TransactionExecutionEvent {
slot,
signature,
outcome,
}
Décisions du gate :
TransactionSignatureest 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 ;
logsSubscribefournit directementslot,signatureeterr;err == nullproduitSucceeded, une erreur produitFailed;- Yellowstone
TransactionStatusfournit exactementslot, signature et erreur optionnelle ;is_vote,index, filters etcreated_atrestent Transport ; - Helius
transactionSubscribereste provider-specific : une projection n'est produite que lorsque le DTO/detail mode permet de déterminerslot, signature et outcome sans ambiguïté ; une formenone, 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 :
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 :
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à :
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 :
#[non_exhaustive]
pub enum SlotLifecycleStage {
Processed,
FirstShredReceived,
Completed,
CreatedBank,
Dead,
OptimisticallyConfirmed,
Rooted,
}
pub struct SlotLifecycleEvent {
slot: u64,
stage: SlotLifecycleStage,
}
Surface publique visée :
SlotLifecycleStage
SlotLifecycleEvent::new(slot, stage)
SlotLifecycleEvent::slot()
SlotLifecycleEvent::stage()
Décisions de représentation :
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 ; Debugest sûr par construction ;- aucun error code n'est nécessaire si tout
u64de slot et toute variante publique construite sont valides ; - aucune dépendance nouvelle n'est requise ;
- aucun trait universel
Event,EventSource,SubscriberouHandler; - aucune conversion
From<TransportType>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 :
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 :
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 :
ksp-interface-lib
└── ksp-core-lib
Aucune nouvelle dependency normale ou feature n'est planifiée.
Les consumers restent :
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 ;
slotest 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 :
TransactionSignatureconserve exactement 64 octets et masque ses bytes enDebug;SucceededetFailedsont distincts ;TransactionExecutionEventconserveu64::MAX, signature et outcome sans narrowing ;Debugd'event ne rend pas la signature.
Tests d'intégration :
- les trois nouveaux symboles sont disponibles depuis le crate-root ;
TransactionExecutionOutcomeimpose 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 :
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/ YellowstoneTransactionStatus/ HeliustransactionSubscribe: PASS sous projection conservative ; - anti-duplication Store : PASS, conversion explicite Interface <-> Store à la composition ;
- consumer concret : PASS, futur worker RAW live ;
- implémenter
TransactionSignature,TransactionExecutionOutcomeetTransactionExecutionEventseulement ; - 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 treeInterface 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.006propre ; - README/USAGE Interface durables et version-neutral ;
- rôle Interface explicité dans les documents d'architecture réellement devenus incomplets ;
TransactionLogEventtransféré dansdocs/IDEAS.mdcomme idée différée ;- aucun CHANGELOG/ROADMAP/prompt suivant dans cette tranche.
0.3.5-pre.008 — préparation de publication
Lane minimale :
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
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
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
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 :
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.