Files
khadhroony-solana-project/docs/plans/026-V0_3_5_INTERFACE_ACQUISITION_EVENTS_PLAN.md
2026-08-31 09:45:27 +02:00

43 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 ouverte par cette tranche :

workspace.package.version = 0.3.5-pre.1
label = 0.3.5-pre.001

0.3.5-pre.001 est un gate de lecture, inventaire, convergence sémantique, threat model, sizing et planification. Il ne modifie aucun contrat public Rust, aucun DTO Transport, aucun modèle Store, aucun runtime et aucune persistence.

Le résultat du gate est volontairement étroit : une seule famille satisfait actuellement les critères d'admission Interface, le lifecycle de slot sur une intersection minimale de statuts réellement commune entre les surfaces Solana WebSocket et Yellowstone. Toutes les autres familles restent Transport, Store ou reportées.

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 :

  • 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 :

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 :

  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 + Yellowstone Slot Processed, FirstShredReceived, Completed, CreatedBank, Dead parent/root/timestamps/diagnostics ; Frozen/Optimistic/Root vs Confirmed/Finalized oui, pour l'occurrence slot + stage futur worker RAW live Interface ADMIS
root/finality rootSubscribe + slotsUpdates Root + Yellowstone Finalized aucune root et finalized ne sont pas déclarés équivalents par une norme commune non aucun besoin exact démontré Transport REPORTÉ
logs realtime logsSubscribe + transaction meta logs + Yellowstone tx signature/logs seulement au prix de perdre la nature de la source event logs vs matière d'une transaction complète non pour un fait unique hydratation possible, non requise Transport REPORTÉ
transaction status getSignatureStatuses + signatureSubscribe + Yellowstone TransactionStatus aucune snapshot vs transition commitment vs execution update non 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 uniquement aucune multi-source mono-producer et protocole-specific non aucun Transport REJET Interface
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 uniquement aucune multi-source indépendante filtres/options/provider behavior non Transport consumer Transport REJET Interface

7.1 Lifecycle de slot — ADMIS sous intersection stricte

Fait commun Solana standard Yellowstone Décision Interface Metadata volontairement non commune
slot traité slotSubscribe Processed ADMIS parent/root courant côté WS ; filters/created_at/parent côté Yellowstone
premier shred reçu FirstShredReceived FirstShredReceived ADMIS timestamp WS ; created_at/filters côté Yellowstone
ingestion slot complétée Completed Completed ADMIS timestamp WS ; created_at/filters côté Yellowstone
bank créé CreatedBank CreatedBank ADMIS parent requis WS, optionnel Yellowstone ; il reste dans le DTO Transport riche
slot mort Dead Dead ADMIS diagnostics source-specific et horodatages restent Transport
frozen Frozen aucun statut exact REJETÉ pas d'intersection
optimistic confirmation OptimisticConfirmation Confirmed REJETÉ aucune équivalence normative exacte retenue
root rootSubscribe / slotsUpdates Root Finalized REJETÉ aucune équivalence normative exacte retenue
confirmed aucun événement exact retenu Confirmed REJETÉ pas de producer standard exact
finalized aucun événement exact retenu Finalized REJETÉ root n'est pas promu artificiellement en finalized

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 et dead_error restent également source-owned ; un consumer qui en a besoin doit conserver le DTO Transport ou définir un contrat supérieur spécifique.

7.2 Logs realtime — REPORTÉ

Le payload standard logsSubscribe est compact et event-only, mais le gate ne démontre pas une deuxième surface sémantiquement indépendante qui produise exactement le même fait :

  • Helius standard réutilise le wire standard Solana et n'ajoute donc pas un nouveau contrat ;
  • Yellowstone expose des logs dans la transaction/meta complète, pas une notification logsSubscribe équivalente ;
  • la signature + logs peut déclencher une hydratation, mais ce besoin pourra être porté directement par le DTO Transport tant qu'un consumer multi-transport concret n'exige pas une projection commune.

Aucun RawLog, TransactionLogEvent ou équivalent public n'est donc créé en 0.3.5 sauf réouverture explicite du gate avec un nouveau producer/consumer.

7.3 Transaction status — REPORTÉ

Les trois familles restent distinctes :

HTTP getSignatureStatuses
    = snapshot interrogé, avec confirmations/status/confirmationStatus

WS signatureSubscribe
    = transition one-shot vers le commitment demandé, éventuellement précédée de ReceivedSignature

Yellowstone transaction_status
    = update execution/status avec signature, slot, is_vote, index et error

Une struct avec de nombreux Option perdrait les distinctions entre snapshot, observation de réception, transition de commitment et update d'exécution. La décision de 0.3.1 est donc confirmée.

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 — REJET Interface

Entry n'a qu'un producer/protocole démontré et ne possède aucun consumer transversal proche. Il reste strictement Transport.

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,
}

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 ;
  • 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<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.

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.

Cette API ne devient pas un prérequis de 0.3.6 : le premier backfill historique reste fondé sur Transport + Store et peut ne jamais consommer SlotLifecycleEvent.

10. Anti-duplication Store

Contrat Store comparé Chevauchement avec SlotLifecycleEvent Pourquoi il n'y a pas de seconde vérité
RawTransaction slot possible seulement aucun payload/signature/format/hash ; l'event ne peut reconstruire une transaction
RawTransactionObservation slot indirect possible aucune provenance acquisition, observation key ou timestamp durable
RawAccountState slot possible 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 dans l'event, précisément pour éviter la fusion de clocks différentes
RawContentHash aucun aucune identité de contenu durable
RawObservationKey aucun aucune identité d'observation persistante
notification after commit aucun SlotLifecycle précède/observe le réseau ; le wake-up Store référence une donnée déjà commitée

Le gate doit conserver les canaris conceptuels suivants :

SlotLifecycleEvent -X-> RawTransaction
SlotLifecycleEvent -X-> RawAccountState
SlotLifecycleEvent -X-> RawAcquisitionProvenance
SlotLifecycleEvent -X-> persisted-data notification

Interface -X-> ksp-store-api
Interface -X-> ksp-store-lib
Store API -X-> Interface

Aucun champ du modèle admis ne doit pouvoir servir de substitut à une référence 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 si utile
    -> 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 une seule famille étroite ; aucun Event générique
Option soup sémantiques différentes fusionnées par champs optionnels slot + stage seulement ; status/log/vote non convergents reportés
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 root normalisé artificiellement en Finalized aucun stage Root/Finalized tant qu'une équivalence normative exacte n'est pas démontrée
optimistic/confirmed confusion OptimisticConfirmation fusionné à Confirmed deux états exclus de l'intersection
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é
unbounded payload logs/JSON/bytes entrent dans Interface aucun payload variable dans la famille admise
public enum breakage ajout futur casse les matches externes SlotLifecycleStage non-exhaustive
logging/runtime creep crate passive acquiert tracing/Tokio/channel aucune dependency/runtime/logging ajoutée
Helius semantic promotion extension provider devient norme KSP Enhanced transactionSubscribe reste Transport
Yellowstone protocol promotion Entry/filters/created_at deviennent Interface types Yellowstone restent Transport

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 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
YellowstoneSlotStatus équivalents -> mêmes stages

Et devra explicitement refuser de convertir les variantes hors intersection en un faux stage commun.

14. Prévision souple des prereleases

La release est dimensionnée pour rester dans une seule session et ne possède qu'une famille fonctionnelle retenue.

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.002SlotLifecycleEvent

  • 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 — canaris externes + complétude API

  • external consumer ;
  • inventaire exact des modules/exports ;
  • canaris négatifs contre Transport/Store/runtime/serde/logging ;
  • non-exhaustive et stabilité de la surface.

Cette tranche ne crée aucune deuxième famille d'event sauf réouverture documentée du gate avec preuve nouvelle. Le défaut est de conserver logs/status/vote/root reportés.

0.3.5-pre.004 — 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.005 — gate technique final

  • gates Rust/workspace complets ;
  • cargo tree Interface normal/features/duplicates ;
  • aucun smoke live requis : le contrat est passif et ne possède aucun réseau.

0.3.5-pre.006 — réconciliation documentaire finale

  • plan + validation ;
  • README/USAGE Interface durables et version-neutral ;
  • architecture durable uniquement si le rôle Interface doit être explicité ;
  • aucun CHANGELOG/ROADMAP/prompt suivant dans cette tranche.

0.3.5-pre.007 — 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.007.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é
VoteEvent partagé
Root == Finalized
OptimisticConfirmation == Confirmed
Helius Enhanced DTO dans Interface
Yellowstone Entry 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
aucune variante hors intersection n'est normalisée artificiellement
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.