13 Commits

Author SHA1 Message Date
3de0488cab v0.3.5-rel.001 2026-08-31 14:28:12 +02:00
2460162243 v0.3.5-pre.008-fix.001 2026-08-31 14:27:15 +02:00
1b9104d64b v0.3.5-pre.008 2026-08-31 14:01:23 +02:00
80be36bfdd v0.3.5-pre.007 2026-08-31 13:07:56 +02:00
e8d2382ac3 v0.3.5-pre.006 2026-08-31 11:43:40 +02:00
749e53edc2 v0.3.5-pre.005-fix.001 2026-08-31 11:23:40 +02:00
7377a6f6cd v0.3.5-pre.005 2026-08-31 11:17:09 +02:00
45601ce386 v0.3.5-pre.004 2026-08-31 11:14:44 +02:00
7d490eb5d8 v0.3.5-pre.003 2026-08-31 11:01:45 +02:00
ca018095bf v0.3.5-pre.002-fix.001 2026-08-31 10:44:51 +02:00
0dd722ffca v0.3.5-pre.002 2026-08-31 10:36:10 +02:00
75e8030b07 v0.3.5-pre.001-fix.001 2026-08-31 10:21:47 +02:00
c24e4c48f8 v0.3.5-pre.001 2026-08-31 09:45:27 +02:00
39 changed files with 5714 additions and 144 deletions

View File

@@ -1,8 +1,20 @@
<!-- file: CHANGELOG.md --> <!-- file: CHANGELOG.md -->
<!-- version: 23 --> <!-- version: 25 -->
# Changelog KSP # Changelog KSP
## 0.3.5 — Interface acquisition events partagés — 2026-08-31
`0.3.5` étend `ksp-interface-lib` avec deux familles passives d'acquisition réellement partagées, sans transformer Interface en runtime, en event bus ou en seconde couche RAW. `SlotLifecycleEvent` expose un `slot` et un `SlotLifecycleStage` non exhaustif limité à `Processed`, `FirstShredReceived`, `Completed`, `CreatedBank`, `Dead`, `OptimisticallyConfirmed` et `Rooted`. La normalisation conserve la sémantique commune : les notifications Solana `optimisticConfirmation` et Yellowstone `Confirmed` convergent vers `OptimisticallyConfirmed`, tandis que Solana `root` et Yellowstone `Finalized` convergent vers `Rooted`; les différences d'ordre, de complétude, de replay et de transport restent la responsabilité du producteur/Transport.
La seconde famille matérialise le fait minimal d'exécution transactionnelle partagé par plusieurs sources. `TransactionSignature` possède exactement 64 octets et un `Debug` redacted, `TransactionExecutionOutcome` distingue seulement `Succeeded` et `Failed`, et `TransactionExecutionEvent` transporte uniquement `slot + signature + outcome`. Les logs, erreurs provider détaillées, commitments, indexes, timestamps, filtres et payloads complets restent Transport-owned. Les snapshots HTTP `getSignatureStatuses`, les transitions one-shot `signatureSubscribe`, un éventuel `TransactionLogEvent`, les votes et les entrées Yellowstone ne sont pas fusionnés artificiellement dans ce contrat.
La frontière d'ownership reste stricte : les DTOs wire/provider demeurent dans `ksp-onchain-transport-lib`, les événements passifs provider-neutral sont Interface-owned, et les modèles persistants/replayables `RawTransaction` / `RawAccountState` restent `ksp-store-api`. `ksp-interface-lib` conserve exactement `ksp-core-lib` comme seule dépendance normale, sans feature propre, dev/build dependency, serde, codec, logging ou runtime. Les canaris publics, external consumer, inventaires exacts et hardening vérifient également qu'aucun second RAW, metadata source ou payload hostile n'entre dans la surface Interface.
Les gates de clôture passent audits Rust/Markdown, `cargo check --workspace`, Clippy all-targets, tests ciblés Interface/Program API, `cargo test --workspace` et graphes Cargo. `cargo tree -p ksp-interface-lib --edges normal` confirme le chemin `ksp-interface-lib -> ksp-core-lib -> solana-pubkey -> solana-address`; le graphe features ne montre aucune feature propre Interface et les doublons éventuels restent ceux du workspace global. La documentation durable a été réconciliée et `TransactionLogEvent` est conservé comme idée différée soumise à un nouveau gate consumer/bornes.
`prompts/025-V0_3_6_START_PROMPT.md` ouvre `0.3.6` sur le développement parallèle de `ksp-job-api` et `ksp-job-backfill-lib`. La release doit reprendre fonctionnellement le backfill historique kbot3 sans en copier le code : audit obligatoire de l'archive historique, `RawTransaction` par adresse via `getSignaturesForAddress` + `getTransaction`, directions/anchors et déduplication, hydratation/persistence via les façades KSP, frontier/checkpoint/reprise, cancellation/concurrency bornées, idempotence et distinctions missing/conflit. `ksp-job-api` doit en parallèle stabiliser lifecycle/progress/outcome et un contrat de notifications/listeners borné pour qu'une couche supérieure puisse visualiser l'état sans parser les logs. Le ROADMAP enchaîne ensuite l'app de backfill/inspection `0.3.7`, `ksp-worker-api` `0.3.8`, `ksp-worker-live-transactions-retriever-lib` `0.3.9` puis une application de monitoring/visualisation Jobs + Workers `0.3.10`, avec reprise du même pattern d'observabilité côté Worker sans fusionner les sémantiques Job/Worker/Store.
## 0.3.4 — Store/PostgreSQL RawAccountState + complétude RAW — 2026-08-31 ## 0.3.4 — Store/PostgreSQL RawAccountState + complétude RAW — 2026-08-31
`0.3.4` complète la seconde vertical slice RAW physique sur le couple `ksp-store-lib` / `ksp-store-postgres-lib` et ferme la conformance PostgreSQL des **10 capabilities** backend-agnostic de `ksp-store-api` : les six capabilities `RawTransaction*` acquises en `0.3.3` restent intactes et les quatre capabilities `RawAccountStateRead`, `RawAccountStateWrite`, `RawAccountObservationRead` et `RawAccountObservationWrite` sont désormais implémentées par `PostgresBackend` puis dispatchées par la façade `Store`. La séparation reste stricte : les consommateurs ordinaires passent par `ksp-store-lib`, le backend PostgreSQL conserve SQL/driver/pool/TLS/migrations privés, et la façade reste compilable/testable sans backend via `--no-default-features`. `0.3.4` complète la seconde vertical slice RAW physique sur le couple `ksp-store-lib` / `ksp-store-postgres-lib` et ferme la conformance PostgreSQL des **10 capabilities** backend-agnostic de `ksp-store-api` : les six capabilities `RawTransaction*` acquises en `0.3.3` restent intactes et les quatre capabilities `RawAccountStateRead`, `RawAccountStateWrite`, `RawAccountObservationRead` et `RawAccountObservationWrite` sont désormais implémentées par `PostgresBackend` puis dispatchées par la façade `Store`. La séparation reste stricte : les consommateurs ordinaires passent par `ksp-store-lib`, le backend PostgreSQL conserve SQL/driver/pool/TLS/migrations privés, et la façade reste compilable/testable sans backend via `--no-default-features`.

View File

@@ -1,12 +1,12 @@
# file: Cargo.toml # file: Cargo.toml
# version: 382 # version: 392
[workspace] [workspace]
resolver = "3" resolver = "3"
members = ["crates/ksp-app-config-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-interface-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-program-api", "crates/ksp-store-api", "crates/ksp-store-lib", "crates/ksp-store-postgres-lib", "crates/ksp-wallet-lib"] members = ["crates/ksp-app-config-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-interface-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-program-api", "crates/ksp-store-api", "crates/ksp-store-lib", "crates/ksp-store-postgres-lib", "crates/ksp-wallet-lib"]
[workspace.package] [workspace.package]
version = "0.3.4" version = "0.3.5"
edition = "2024" edition = "2024"
license = "MIT" license = "MIT"
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"

View File

@@ -1,5 +1,5 @@
<!-- file: ROADMAP.md --> <!-- file: ROADMAP.md -->
<!-- version: 98 --> <!-- version: 100 -->
# Roadmap KSP # Roadmap KSP
@@ -97,18 +97,21 @@ RAW -> STRUCTURAL -> DECODED -> DOMAIN
- [X] `0.3.2``ksp-store-lib` + `ksp-store-postgres-lib` stables comme fondation runtime/backend PostgreSQL : feature `postgres` par défaut, `Store` lié à un unique `RawNetworkId`, Config `std.store` avec targets/bases `devnet`/`mainnet`/`testnet`, pool Deadpool borné, `tokio-postgres`, TLS Rustls `Disabled`/`VerifyFull`, moteur de migrations privé `V000` + SHA-256/advisory lock, health/readiness portable et close borné. Gate complet + PostgreSQL réel major 17 verts ; aucune table/capability `RawTransaction`/`RawAccountState` métier n'est encore ajoutée. - [X] `0.3.2``ksp-store-lib` + `ksp-store-postgres-lib` stables comme fondation runtime/backend PostgreSQL : feature `postgres` par défaut, `Store` lié à un unique `RawNetworkId`, Config `std.store` avec targets/bases `devnet`/`mainnet`/`testnet`, pool Deadpool borné, `tokio-postgres`, TLS Rustls `Disabled`/`VerifyFull`, moteur de migrations privé `V000` + SHA-256/advisory lock, health/readiness portable et close borné. Gate complet + PostgreSQL réel major 17 verts ; aucune table/capability `RawTransaction`/`RawAccountState` métier n'est encore ajoutée.
- [X] `0.3.3` — Vertical slice PostgreSQL `RawTransaction` complète sur `ksp-store-lib` + `ksp-store-postgres-lib` : six capabilities transaction/observation/rétention, V001 physique liée à un réseau, acquisition canonical+observation atomique, idempotence/conflit, get/list keyset cursorisé, archive/purge/tombstone/ForceRehydrate, hardening des erreurs et du schéma, concurrence et rollback validés sur PostgreSQL 17. - [X] `0.3.3` — Vertical slice PostgreSQL `RawTransaction` complète sur `ksp-store-lib` + `ksp-store-postgres-lib` : six capabilities transaction/observation/rétention, V001 physique liée à un réseau, acquisition canonical+observation atomique, idempotence/conflit, get/list keyset cursorisé, archive/purge/tombstone/ForceRehydrate, hardening des erreurs et du schéma, concurrence et rollback validés sur PostgreSQL 17.
- [X] `0.3.4` — Vertical slice PostgreSQL `RawAccountState` complète sur `ksp-store-lib` + `ksp-store-postgres-lib` : quatre capabilities account ajoutées aux six transaction pour une conformance RAW 10/10, V002 additive de 32 ressources au-dessus de V000/V001 immuables, state+observation atomiques, idempotence/conflit exacts, metadata Yellowstone observation-only, get/list keyset `(slot,pubkey,state_hash)` avec cursor KSPA anti-replay, hardening cross-family et live validé sur PostgreSQL 17 sans rétention destructive account. - [X] `0.3.4` — Vertical slice PostgreSQL `RawAccountState` complète sur `ksp-store-lib` + `ksp-store-postgres-lib` : quatre capabilities account ajoutées aux six transaction pour une conformance RAW 10/10, V002 additive de 32 ressources au-dessus de V000/V001 immuables, state+observation atomiques, idempotence/conflit exacts, metadata Yellowstone observation-only, get/list keyset `(slot,pubkey,state_hash)` avec cursor KSPA anti-replay, hardening cross-family et live validé sur PostgreSQL 17 sans rétention destructive account.
- [ ] `0.3.5` Étendre `ksp-interface-lib` uniquement avec les modèles passifs/event-only dont une matrice des surfaces HTTP/WS/Yellowstone/Helius démontre la sémantique réellement partagée et le besoin consumer ; auditer notamment logs, slot/root/slotsUpdates, transaction status et vote, sans dupliquer `RawTransaction`/`RawAccountState`, sans event bus et sans dépendance Interface vers Transport/Store. - [X] `0.3.5``ksp-interface-lib` étendu avec deux familles passives réellement partagées : `SlotLifecycleEvent` (`Processed`, `FirstShredReceived`, `Completed`, `CreatedBank`, `Dead`, `OptimisticallyConfirmed`, `Rooted`) et `TransactionExecutionEvent` (`slot + TransactionSignature[64] + Succeeded/Failed`). Interface reste Core-only, provider-neutral, sans serde/codec/runtime/event bus et sans duplication de `RawTransaction`/`RawAccountState`; les DTOs riches restent Transport-owned et les candidats non convergents restent différés.
- [ ] `0.3.6` — Introduire `ksp-job-api` et un premier job de backfill historique concret consommant `ksp-store-lib`, avec policy/batch-size/progression possédés par le job et non par Store. - [ ] `0.3.6` — Introduire en parallèle `ksp-job-api` et `ksp-job-backfill-lib` : reprendre fonctionnellement le backfill historique kbot3 sur les abstractions KSP actuelles (`ksp-onchain-transport-lib` + `ksp-store-lib`), fermer une première verticale `RawTransaction` historique par adresse avec directions/anchors, déduplication, hydratation, idempotence, frontier/checkpoint/reprise, cancellation et concurrency bornées, et stabiliser un lifecycle/progress/outcome avec notifications/listeners sûrs pour les couches supérieures.
- [ ] `0.3.7` — Introduire une application spécialisée de backfill/inspection RAW. - [ ] `0.3.7` — Introduire une application spécialisée de backfill/inspection RAW consommant `ksp-job-api` : lancement/annulation contrôlés, état et progression live, compteurs, checkpoint/frontière, outcome terminal et inspection RAW sans parser les logs ni connaître les providers/backends physiques.
- [ ] Compléter ensuite la couche RAW avec le worker/service live, son contrôle et les outils dexploitation réellement nécessaires avant de passer à la couche de normalisation générique suivante. - [ ] `0.3.8` — Introduire `ksp-worker-api` comme API générique de lifecycle/health/progression pour services continus, en reprenant le pattern de notifications/listeners stabilisé par Job tout en gardant les sémantiques Worker distinctes des jobs terminables et des wake-ups Store post-commit.
- [ ] `0.3.9` — Introduire `ksp-worker-live-transactions-retriever-lib` pour l'acquisition continue `RawTransaction` via les surfaces live de `ksp-onchain-transport-lib`, persistence par `ksp-store-lib`, reprise/backpressure/idempotence et notifications `ksp-worker-api`, sans decode Program ni dépendance backend/provider directe.
- [ ] `0.3.10` — Introduire une application de monitoring/visualisation Jobs + Workers : vue graphique des lifecycles, health/progression, rates/backpressure/retries sûrs, checkpoints et outcomes ; l'application consomme les APIs publiques Job/Worker et ne devient ni scheduler caché ni source de vérité du backlog.
### TODO/IDEAS — taxonomie N1, processing et rétention ### TODO/IDEAS — taxonomie N1, processing et rétention
- [ ] **TODO** — maintenir la matrice dadmission HTTP/WS/gRPC/provider lors de toute nouvelle famille N1 : plusieurs sources ne convergent vers un même struct que si elles satisfont la même sémantique sans perte. - [ ] **TODO** — maintenir la matrice dadmission HTTP/WS/gRPC/provider lors de toute nouvelle famille N1 : plusieurs sources ne convergent vers un même struct que si elles satisfont la même sémantique sans perte.
- [X] `RawAccountState` + observation — contrat commun stabilisé en `0.3.1` avec bytes complets + slot, provenance séparée et enrichissements source-specific optionnels ; la persistence PostgreSQL physique est complétée en `0.3.4` avec les quatre capabilities account et la conformance RAW 10/10. - [X] `RawAccountState` + observation — contrat commun stabilisé en `0.3.1` avec bytes complets + slot, provenance séparée et enrichissements source-specific optionnels ; la persistence PostgreSQL physique est complétée en `0.3.4` avec les quatre capabilities account et la conformance RAW 10/10.
- [ ] **TODO**`TransactionStatusObservation` : réauditer `signatureSubscribe`, `getSignatureStatuses`, Yellowstone TransactionStatus et extensions provider lorsquun consumer réel apparaît ; ne pas fusionner snapshot, transition et update dans un modèle Option-soup. - [ ] **TODO**statut/commitment transactionnel restant : `0.3.5` couvre uniquement le fait passif dexécution `slot + signature + outcome`; réauditer séparément `signatureSubscribe` et `getSignatureStatuses` lorsquun consumer de commitment/snapshot réel apparaît, sans fusionner snapshot, transition et execution update dans un modèle Option-soup.
- [ ] **TODO** — logs realtime : conserver `logMessages` dans `RawTransaction` jusquà la décomposition STRUCTURAL ; traiter `logsSubscribe` comme event-only candidat et décider son contrat passif dans `ksp-interface-lib`, sans table Store par défaut. Le format canonique dun wake-up « donnée persistée disponible » reste distinct et appartient à `ksp-store-api` conformément à `KSP-NOTIFY-*`, mais ne sera matérialisé quavec un publisher/consumer réel. - [ ] **IDEA** — logs realtime enrichis : `logsSubscribe` alimente déjà la projection minimale `TransactionExecutionEvent`, mais un éventuel `TransactionLogEvent` portant les lignes de log reste différé dans `docs/IDEAS.md` jusquà démonstration dun consumer et de bornes explicites. `logMessages` reste dans `RawTransaction` jusquà STRUCTURAL ; le wake-up post-commit reste distinct et Store API-owned conformément à `KSP-NOTIFY-*`.
- [ ] **TODO** slot/root/slotsUpdates et vote : ne créer un modèle passif commun que si un consumer realtime réel et une sémantique cross-ledger/provider justifient le contrat ; aucune persistence Store par défaut. - [X] slot/root/slotsUpdates `0.3.5` stabilise `SlotLifecycleEvent` pour lintersection réellement partagée, sans persistence Store par défaut et sans promettre lordre/complétude du flux.
- [ ] **TODO** — vote realtime : reste hors Interface tant quaucun consumer transversal et aucune sémantique provider-neutral suffisamment précise ne justifient un contrat partagé.
- [ ] **IDEA**`RawBlock` : ne rouvrir que si une information block-level non reconstructible devient nécessaire ; `getBlock` doit dabord être traité comme source de `RawTransaction`, pas comme invitation à recopier le ledger en blocs. - [ ] **IDEA**`RawBlock` : ne rouvrir que si une information block-level non reconstructible devient nécessaire ; `getBlock` doit dabord être traité comme source de `RawTransaction`, pas comme invitation à recopier le ledger en blocs.
- [ ] **REJET ACTUEL** — Yellowstone `Entry` : trop bas niveau et aucune destination replay/decomposition/event métier justifiant un modèle KSP nest identifiée. - [ ] **REJET ACTUEL** — Yellowstone `Entry` : trop bas niveau et aucune destination replay/decomposition/event métier justifiant un modèle KSP nest identifiée.
- [ ] **TODO** — processing ledger : reprendre lidée kbot2/kbot3 `stage + processor identity/version + input identity/hash + terminal status`, sans faire dun `processed: bool` la preuve durable unique ; prévoir force replay/version upgrades lorsque les processors seront ouverts. - [ ] **TODO** — processing ledger : reprendre lidée kbot2/kbot3 `stage + processor identity/version + input identity/hash + terminal status`, sans faire dun `processed: bool` la preuve durable unique ; prévoir force replay/version upgrades lorsque les processors seront ouverts.

View File

@@ -1,9 +1,11 @@
<!-- file: crates/ksp-interface-lib/README.md --> <!-- file: crates/ksp-interface-lib/README.md -->
<!-- version: 2 --> <!-- version: 3 -->
# ksp-interface-lib # ksp-interface-lib
`ksp-interface-lib` est la façade wire officielle KSP destinée aux contrats passifs partagés par les implémentations Program Solana officielles ou externes. La crate expose uniquement des structures de représentation/admission ; elle ne possède ni transport, ni exécution, ni persistence, ni comportement métier Program. `ksp-interface-lib` possède les contrats passifs KSP qui doivent être partagés entre plusieurs composants sans imposer leur runtime d'origine. Sa surface couvre actuellement deux familles distinctes : les contrats wire Program génériques et un petit ensemble de faits d'acquisition provider-neutral dont la sémantique commune a été démontrée.
La crate reste une façade de représentation. Elle ne possède ni transport réseau, ni event bus, ni worker/job, ni persistence, ni exécution, ni comportement métier Program.
## Ownership ## Ownership
@@ -15,9 +17,9 @@ Error / ErrorCode / Result
Program IDs fondamentaux Program IDs fondamentaux
``` ```
`Pubkey` est réexporté depuis le crate-root Interface afin qu'un consumer wire n'introduise aucun wrapper d'identité parallèle. Les Program IDs restent possédés et répertoriés par Core. `Pubkey` est réexporté depuis le crate-root Interface afin qu'un consumer n'introduise aucun wrapper d'identité parallèle. Les Program IDs restent possédés et répertoriés par Core.
La dependency direction candidate `0.2.13` reste strictement : Le graphe normal reste strictement :
```text ```text
ksp-interface-lib ksp-interface-lib
@@ -25,9 +27,11 @@ ksp-interface-lib
└── solana-pubkey └── solana-pubkey
``` ```
## Surface publique `0.2.13` La crate ne possède aucune feature Cargo, aucune `dev-dependency` et aucune `build-dependency` runtime propre.
La façade crate-root expose exactement : ## Surface publique
La façade crate-root expose :
```text ```text
Pubkey Pubkey
@@ -36,10 +40,17 @@ MAX_PROGRAM_INSTRUCTION_ACCOUNTS
ProgramInstruction ProgramInstruction
MAX_PROGRAM_INSTRUCTION_DATA_LEN MAX_PROGRAM_INSTRUCTION_DATA_LEN
ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED
SlotLifecycleStage
SlotLifecycleEvent
TransactionSignature
TransactionExecutionOutcome
TransactionExecutionEvent
``` ```
Aucun module interne n'est public. Aucun module interne n'est public.
## Contrats Program passifs
### `ProgramAccountMeta` ### `ProgramAccountMeta`
`ProgramAccountMeta` représente un compte ordonné d'une instruction avec : `ProgramAccountMeta` représente un compte ordonné d'une instruction avec :
@@ -68,16 +79,14 @@ data: Vec<u8>
Les cas vides sont valides et une `program_id` inconnue du registry KSP reste admissible. Les cas vides sont valides et une `program_id` inconnue du registry KSP reste admissible.
## Bornes d'admission ### Bornes d'admission
Interface applique deux limites locales :
| Limite | Valeur | | Limite | Valeur |
|------------------------------------|----------| |------------------------------------|----------|
| `MAX_PROGRAM_INSTRUCTION_ACCOUNTS` | `255` | | `MAX_PROGRAM_INSTRUCTION_ACCOUNTS` | `255` |
| `MAX_PROGRAM_INSTRUCTION_DATA_LEN` | `10_240` | | `MAX_PROGRAM_INSTRUCTION_DATA_LEN` | `10_240` |
Ces valeurs sont des **bornes d'admission Interface**. Elles ne constituent pas une garantie qu'une instruction donnée tient dans toutes les contraintes d'une transaction Solana top-level. La limite CPI de comptes uniques n'est notamment pas transformée en règle artificielle sur la liste d'account metas. Ces valeurs sont des **bornes d'admission Interface**. Elles ne constituent pas une garantie qu'une instruction donnée respecte à elle seule toutes les contraintes d'une transaction Solana complète.
Un dépassement utilise le code commun : Un dépassement utilise le code commun :
@@ -89,21 +98,74 @@ Le contexte d'erreur est limité aux métadonnées sûres `field`, `actual_len`
Le `Debug` de `ProgramInstruction` est volontairement borné : il affiche `program_id`, `account_count` et `data_len`, jamais les accounts complets ni les octets du payload. Le `Debug` de `ProgramInstruction` est volontairement borné : il affiche `program_id`, `account_count` et `data_len`, jamais les accounts complets ni les octets du payload.
## Faits passifs d'acquisition
Les événements Interface sont des **projections provider-neutral supplémentaires**. Ils ne remplacent jamais les DTOs riches de `ksp-onchain-transport-lib` et ne deviennent jamais la source de vérité durable d'un backlog.
La conversion depuis un DTO HTTP/WS/gRPC/provider appartient à la composition ou au consumer qui connaît les deux contrats. `ksp-interface-lib` ne dépend donc pas de Transport.
### `SlotLifecycleEvent`
`SlotLifecycleEvent` conserve exactement :
```text
slot: u64
stage: SlotLifecycleStage
```
Les stages actuellement représentés sont :
```text
Processed
FirstShredReceived
Completed
CreatedBank
Dead
OptimisticallyConfirmed
Rooted
```
`SlotLifecycleStage` est `#[non_exhaustive]` afin qu'un consumer externe traite explicitement l'évolution future de l'enum.
Parent, timestamp, diagnostics de slot mort, source/provider, filter/session metadata et autres détails Transport ne sont pas copiés dans ce contrat minimal.
### `TransactionExecutionEvent`
`TransactionExecutionEvent` conserve exactement :
```text
slot: u64
signature: TransactionSignature
outcome: TransactionExecutionOutcome
```
`TransactionSignature` contient exactement les 64 bytes canoniques déjà décodés d'une signature Solana. Son `Debug` ne rend pas les bytes.
`TransactionExecutionOutcome` distingue uniquement :
```text
Succeeded
Failed
```
L'enum est `#[non_exhaustive]`. Le contrat ne contient aucun log, payload RAW, erreur provider, commitment, provenance, network id, source metadata ni détail d'exécution arbitraire. Une source ambiguë ou insuffisante doit rester dans son owner Transport au lieu de forcer un événement Interface.
## Codecs et runtime ## Codecs et runtime
La foundation `0.2.13` n'ajoute aucun codec par réflexe : La crate n'ajoute aucun codec ou runtime par réflexe :
```text ```text
serde / serde_json absents serde / serde_json absents
borsh absent borsh absent tant qu'aucun wire réel ne le requiert
wincode absent wincode absent tant qu'aucun wire réel ne le requiert
bincode absent bincode interdit pour les codecs wire KSP
solana-instruction absent transport/runtime réseau absent
logging runtime absent
``` ```
Des codecs/layouts/discriminants spécifiques pourront être ajoutés ultérieurement uniquement lorsqu'un vertical Program réel en démontre le besoin et que leur ownership wire appartient bien à Interface. Des codecs/layouts/discriminants spécifiques peuvent être ajoutés uniquement lorsqu'un protocole réel en démontre le besoin et que leur ownership wire appartient bien à Interface.
La crate ne produit aucun événement runtime. Elle ne dépend donc pas de `ksp-logging-lib` et ne possède ni `constants.rs` ni `TRACING_TARGET`. Si un futur comportement Interface exige réellement du logging, le runtime devra passer par la façade Logging KSP plutôt que par une dépendance directe à Tracing. Les événements passifs ne constituent pas un comportement runtime. La crate ne dépend donc pas de `ksp-logging-lib` et ne possède ni `constants.rs` ni `TRACING_TARGET`.
## Frontières ## Frontières
@@ -112,20 +174,20 @@ La crate ne produit aucun événement runtime. Elle ne dépend donc pas de `ksp-
```text ```text
RPC / WebSocket / gRPC RPC / WebSocket / gRPC
provider DTOs Transport provider DTOs Transport
wallet / signature sessions / reconnect / backpressure
Config / environnement Config / environnement
persistence / Store persistence / Store / cursor / retention
notifications post-commit Store
worker / job / scheduler / event bus
Program decoding / recognition / proofs Program decoding / recognition / proofs
execution policy / signers execution policy / signers
transaction replay / CPI path / runtime logs RawTransaction / RawAccountState
lifecycle réseau
``` ```
La foundation Program API est reportée à `0.2.14`. Les wires génériques d'acquisition/CORE restent reportés à `0.3.2+`. Les données persistantes/replayables restent dans `ksp-store-api`. Les notifications post-commit de données persistées restent un contrat Store API lorsqu'un publisher/consumer réel les justifie. Les faits réseau riches restent Transport-owned.
## Références ## Références
- [Usage public](USAGE.md) - [Usage public](USAGE.md)
- [Plan `0.2.13`](../../docs/plans/020-V0_2_13_INTERFACE_PLAN.md)
- [Validation `0.2.13`](../../docs/validation/016-V0_2_13_INTERFACE.md)
- [Architecture Wire + Program](../../docs/architecture/006-WIRE_AND_PROGRAM.md) - [Architecture Wire + Program](../../docs/architecture/006-WIRE_AND_PROGRAM.md)
- [Architecture Acquisition/Workers/Jobs](../../docs/architecture/009-ACQUISITION_WORKERS_AND_JOBS.md)

View File

@@ -1,9 +1,9 @@
<!-- file: crates/ksp-interface-lib/USAGE.md --> <!-- file: crates/ksp-interface-lib/USAGE.md -->
<!-- version: 2 --> <!-- version: 3 -->
# Usage de ksp-interface-lib # Usage de ksp-interface-lib
Cette page décrit la façade publique matérialisée par `0.2.13`. Les modules internes ne font pas partie du contrat consommable : utiliser uniquement les exports du crate-root. Cette page décrit l'utilisation durable de la façade publique. Les modules internes ne font pas partie du contrat consommable : utiliser uniquement les exports du crate-root.
## Construire des account metas ## Construire des account metas
@@ -52,9 +52,9 @@ match result {
L'ordre et les doublons des accounts sont conservés. Les octets `data` restent opaques : `ProgramInstruction` ne les sérialise, désérialise ni interprète. L'ordre et les doublons des accounts sont conservés. Les octets `data` restent opaques : `ProgramInstruction` ne les sérialise, désérialise ni interprète.
Les `Vec` fournis à `try_new` sont consommés par la structure après validation des bornes ; aucun clone ou reformatage interne n'est requis par le contrat actuel. Les `Vec` fournis à `try_new` sont consommés par la structure après validation des bornes ; aucun clone ou reformatage interne n'est requis par le contrat.
## Bornes ## Respecter les bornes Program
Les limites publiques sont : Les limites publiques sont :
@@ -63,9 +63,9 @@ assert_eq!(ksp_interface_lib::MAX_PROGRAM_INSTRUCTION_ACCOUNTS, 255);
assert_eq!(ksp_interface_lib::MAX_PROGRAM_INSTRUCTION_DATA_LEN, 10_240); assert_eq!(ksp_interface_lib::MAX_PROGRAM_INSTRUCTION_DATA_LEN, 10_240);
``` ```
`255` account metas et `10_240` bytes de data sont admis. `256` account metas ou `10_241` bytes sont refusés par `try_new` avant création d'un `ProgramInstruction` valide. `255` account metas et `10_240` bytes de data sont admis. `256` account metas ou `10_241` bytes sont refusés par `ProgramInstruction::try_new`.
Ces limites sont des bornes locales Interface et ne promettent pas qu'une instruction admise respecte à elle seule toutes les contraintes de taille/account-set d'une transaction Solana complète. Ces limites sont locales à Interface et ne promettent pas qu'une instruction admise respecte à elle seule toutes les contraintes d'une transaction Solana complète.
## Observer une erreur de limite ## Observer une erreur de limite
@@ -92,31 +92,108 @@ maximum_len
Le contenu du payload et les account metas arbitraires ne sont pas projetés dans le diagnostic. Le contenu du payload et les account metas arbitraires ne sont pas projetés dans le diagnostic.
## Debug borné ## Construire un événement de lifecycle de slot
Le `Debug` de `ProgramInstruction` contient uniquement : Un producer/composer qui a déjà établi la correspondance sémantique avec son DTO Transport peut construire le fait passif partagé :
```text ```rust
program_id let event = ksp_interface_lib::SlotLifecycleEvent::new(
account_count 42,
data_len ksp_interface_lib::SlotLifecycleStage::Processed,
);
assert_eq!(event.slot(), 42);
assert_eq!(event.stage(), ksp_interface_lib::SlotLifecycleStage::Processed);
``` ```
Il ne faut donc pas attendre de ce rendu une sérialisation wire ou un dump du payload. Les stages actuellement disponibles sont :
```text
Processed
FirstShredReceived
Completed
CreatedBank
Dead
OptimisticallyConfirmed
Rooted
```
Un consumer externe doit traiter `SlotLifecycleStage` comme une enum évolutive `#[non_exhaustive]` et prévoir un fallback dans ses `match`.
Le type ne contient volontairement ni timestamp, ni parent, ni diagnostic, ni source/provider, ni identifiant de subscription.
## Construire un événement d'exécution de transaction
Une signature doit d'abord être disponible sous sa forme canonique de 64 bytes :
```rust
let signature = ksp_interface_lib::TransactionSignature::new([7_u8; 64]);
let event = ksp_interface_lib::TransactionExecutionEvent::new(
123,
signature,
ksp_interface_lib::TransactionExecutionOutcome::Succeeded,
);
assert_eq!(event.slot(), 123);
assert_eq!(event.signature().as_bytes(), &[7_u8; 64]);
assert_eq!(
event.outcome(),
ksp_interface_lib::TransactionExecutionOutcome::Succeeded,
);
```
`TransactionExecutionOutcome` distingue uniquement `Succeeded` et `Failed` et reste `#[non_exhaustive]`.
Le `Debug` de `TransactionSignature` et de `TransactionExecutionEvent` n'affiche pas les bytes de signature.
## Convertir depuis Transport
Ne pas ajouter `ksp-onchain-transport-lib` comme dépendance de `ksp-interface-lib` pour fournir des `From<TransportDto>`.
La conversion appartient au composant qui connaît les deux côtés :
```text
Transport DTO riche
|
| conversion explicite dans composition/consumer
v
Interface event passif minimal
```
Ne construire un événement Interface que si la source fournit suffisamment d'information pour le fait commun exact. Si l'état est ambigu, conserver le DTO dans son owner Transport ou déclencher une hydratation adaptée ; ne pas inventer de valeur par défaut.
## Ne pas utiliser les événements comme stockage durable
`SlotLifecycleEvent` et `TransactionExecutionEvent` sont des faits passifs, pas des modèles RAW replayables.
Ils ne remplacent pas :
```text
RawTransaction
RawTransactionObservation
RawAccountState
RawAccountObservation
Store backlog / cursor / retention
```
Les consumers qui ont besoin de reprise après crash ou de replay doivent s'appuyer sur `ksp-store-lib`/`ksp-store-api` selon leur responsabilité, pas sur un event Interface en mémoire.
## Dépendances à ne pas ajouter côté consumer ## Dépendances à ne pas ajouter côté consumer
Un consumer de la façade Interface n'a pas besoin d'ajouter un SDK Program Solana uniquement pour reconstruire `ProgramInstruction`. La crate utilise le `Pubkey` canonique partagé avec Core et conserve son propre contrat passif. Un consumer de la façade Interface n'a pas besoin d'ajouter un SDK Program Solana uniquement pour reconstruire `ProgramInstruction`, ni un runtime Transport pour manipuler les événements passifs déjà normalisés.
La foundation ne fournit volontairement pas : La crate ne fournit volontairement pas :
```text ```text
serde générique serde générique
Borsh / Wincode générique Borsh / Wincode générique
solana-instruction interop automatique solana-instruction interop automatique
transport réseau transport réseau
converters provider automatiques
Program decoder/preparer Program decoder/preparer
signing/execution signing/execution
persistence/event bus
``` ```
Ces surfaces doivent être introduites dans leur owner respectif lorsqu'un cas réel le justifie, pas comme dépendances implicites d'un consumer Interface. Ces surfaces doivent être introduites dans leur owner respectif lorsqu'un cas réel le justifie.

View File

@@ -1,20 +1,22 @@
// file: crates/ksp-interface-lib/src/lib.rs // file: crates/ksp-interface-lib/src/lib.rs
// version: 3 // version: 5
#![warn(missing_docs)] #![warn(missing_docs)]
#![deny(unreachable_pub)] #![deny(unreachable_pub)]
#![forbid(unsafe_code)] #![forbid(unsafe_code)]
//! Passive wire contracts shared by KSP Program implementations. //! Passive contracts shared across KSP component boundaries.
//! //!
//! The foundation reuses the canonical Solana [`Pubkey`] owned by //! The crate reuses canonical Solana primitives owned by `ksp-core-lib` and
//! `ksp-core-lib` and exposes only bounded, passive Program-facing structures. //! exposes only bounded, passive Program-facing and provider-neutral
//! Runtime, transport, persistence and Program behavior remain outside this //! acquisition structures. Runtime, transport, persistence and Program
//! crate. //! behavior remain outside this crate.
mod error; mod error;
mod program_account_meta; mod program_account_meta;
mod program_instruction; mod program_instruction;
mod slot_lifecycle;
mod transaction_execution;
/// Error code used when an Interface-owned Program instruction admission limit is exceeded. /// Error code used when an Interface-owned Program instruction admission limit is exceeded.
pub use self::error::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED; pub use self::error::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED;
@@ -26,5 +28,15 @@ pub use self::program_account_meta::ProgramAccountMeta;
pub use self::program_instruction::MAX_PROGRAM_INSTRUCTION_DATA_LEN; pub use self::program_instruction::MAX_PROGRAM_INSTRUCTION_DATA_LEN;
/// Passive, bounded Program instruction wire contract. /// Passive, bounded Program instruction wire contract.
pub use self::program_instruction::ProgramInstruction; pub use self::program_instruction::ProgramInstruction;
/// Passive provider-neutral occurrence of one slot lifecycle stage.
pub use self::slot_lifecycle::SlotLifecycleEvent;
/// Provider-neutral stage in the lifecycle of an observed Solana slot.
pub use self::slot_lifecycle::SlotLifecycleStage;
/// Passive provider-neutral observation of one transaction execution result.
pub use self::transaction_execution::TransactionExecutionEvent;
/// Provider-neutral outcome of one observed transaction execution.
pub use self::transaction_execution::TransactionExecutionOutcome;
/// Canonical 64-byte Solana transaction signature used by passive Interface events.
pub use self::transaction_execution::TransactionSignature;
/// Canonical Solana account address primitive owned by `ksp-core-lib`. /// Canonical Solana account address primitive owned by `ksp-core-lib`.
pub use ksp_core_lib::Pubkey; pub use ksp_core_lib::Pubkey;

View File

@@ -0,0 +1,53 @@
// file: crates/ksp-interface-lib/src/slot_lifecycle.rs
// version: 1
/// Provider-neutral stage in the lifecycle of an observed Solana slot.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum SlotLifecycleStage {
/// The slot has been processed.
Processed,
/// The first shred for the slot has been received.
FirstShredReceived,
/// Slot ingestion has completed.
Completed,
/// A bank has been created for the slot.
CreatedBank,
/// The slot has been marked dead.
Dead,
/// The slot has reached optimistic confirmation.
OptimisticallyConfirmed,
/// The slot has become rooted.
Rooted,
}
/// Passive provider-neutral occurrence of one slot lifecycle stage.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SlotLifecycleEvent {
slot: u64,
stage: SlotLifecycleStage,
}
impl SlotLifecycleEvent {
/// Creates a lifecycle event for `slot` and `stage`.
#[must_use]
pub const fn new(slot: u64, stage: SlotLifecycleStage) -> Self {
return Self { slot, stage };
}
/// Returns the observed slot exactly as supplied.
#[must_use]
pub const fn slot(&self) -> u64 {
return self.slot;
}
/// Returns the provider-neutral lifecycle stage.
#[must_use]
pub const fn stage(&self) -> SlotLifecycleStage {
return self.stage;
}
}
#[cfg(test)]
#[path = "../unit_tests/slot_lifecycle.rs"]
mod tests;

View File

@@ -0,0 +1,85 @@
// file: crates/ksp-interface-lib/src/transaction_execution.rs
// version: 1
/// Canonical 64-byte Solana transaction signature used by passive Interface events.
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct TransactionSignature([u8; 64]);
impl TransactionSignature {
/// Creates one transaction signature from already-decoded canonical bytes.
#[must_use]
pub const fn new(bytes: [u8; 64]) -> Self {
return Self(bytes);
}
/// Returns the exact canonical signature bytes.
#[must_use]
pub const fn as_bytes(&self) -> &[u8; 64] {
return &self.0;
}
}
impl std::fmt::Debug for TransactionSignature {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter.write_str("TransactionSignature(..)");
}
}
/// Provider-neutral outcome of one observed transaction execution.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum TransactionExecutionOutcome {
/// The observed transaction execution completed without a transaction error.
Succeeded,
/// The observed transaction execution completed with a transaction error.
Failed,
}
/// Passive provider-neutral observation of one transaction execution result.
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct TransactionExecutionEvent {
slot: u64,
signature: TransactionSignature,
outcome: TransactionExecutionOutcome,
}
impl TransactionExecutionEvent {
/// Creates one transaction execution event from its common provider-neutral fields.
#[must_use]
pub const fn new(slot: u64, signature: TransactionSignature, outcome: TransactionExecutionOutcome) -> Self {
return Self { slot, signature, outcome };
}
/// Returns the containing slot exactly as observed by the producer.
#[must_use]
pub const fn slot(&self) -> u64 {
return self.slot;
}
/// Returns the canonical transaction signature.
#[must_use]
pub const fn signature(&self) -> TransactionSignature {
return self.signature;
}
/// Returns the normalized execution outcome.
#[must_use]
pub const fn outcome(&self) -> TransactionExecutionOutcome {
return self.outcome;
}
}
impl std::fmt::Debug for TransactionExecutionEvent {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter
.debug_struct("TransactionExecutionEvent")
.field("slot", &self.slot)
.field("signature", &"<omitted>")
.field("outcome", &self.outcome)
.finish();
}
}
#[cfg(test)]
#[path = "../unit_tests/transaction_execution.rs"]
mod tests;

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-interface-lib/tests/dependency_boundary.rs // file: crates/ksp-interface-lib/tests/dependency_boundary.rs
// version: 5 // version: 8
//! Dependency and passive-surface canaries for the Interface foundation. //! Dependency and passive-surface canaries for the Interface foundation.
@@ -91,6 +91,8 @@ fn pre_005_all_production_sources_preserve_the_dependency_firewall() {
include_str!("../src/lib.rs"), include_str!("../src/lib.rs"),
include_str!("../src/program_account_meta.rs"), include_str!("../src/program_account_meta.rs"),
include_str!("../src/program_instruction.rs"), include_str!("../src/program_instruction.rs"),
include_str!("../src/slot_lifecycle.rs"),
include_str!("../src/transaction_execution.rs"),
]; ];
for source in production_sources { for source in production_sources {
for forbidden in [ for forbidden in [
@@ -129,3 +131,52 @@ fn pre_005_instruction_source_has_no_narrowing_cast_or_hidden_codec_entry_point(
} }
return; return;
} }
#[test]
fn v0_3_5_pre_004_acquisition_sources_remain_provider_neutral_and_runtime_free() {
let acquisition_sources = [include_str!("../src/slot_lifecycle.rs"), include_str!("../src/transaction_execution.rs")];
for source in acquisition_sources {
for forbidden in [
"borsh::",
"bincode::",
"ksp_config_lib::",
"ksp_logging_lib::",
"ksp_onchain_transport_lib::",
"ksp_store_api::",
"ksp_store_lib::",
"reqwest::",
"serde::",
"serde_json::",
"solana_instruction::",
"tauri::",
"tokio::",
"tonic::",
concat!("tracing", "::"),
"wincode::",
] {
assert!(!source.contains(forbidden), "forbidden acquisition dependency path detected: {forbidden}");
}
}
return;
}
#[test]
fn v0_3_5_pre_005_manifest_keeps_exact_core_only_graph_without_features_or_hidden_dependency_sections() {
let manifest = include_str!("../Cargo.toml");
assert!(!manifest.contains("[features]"));
assert!(!manifest.contains("[dev-dependencies]"));
assert!(!manifest.contains("[build-dependencies]"));
assert_eq!(manifest.matches("[dependencies]").count(), 1);
let dependencies_tail = manifest.split("[dependencies]").nth(1);
assert!(dependencies_tail.is_some(), "Interface dependencies section must exist");
let dependencies_tail = match dependencies_tail {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let dependencies = match dependencies_tail.split("[lints]").next() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
assert_eq!(manifest_dependency_names(dependencies), std::vec!["ksp-core-lib"]);
return;
}

View File

@@ -1,12 +1,22 @@
// file: crates/ksp-interface-lib/tests/external_consumer.rs // file: crates/ksp-interface-lib/tests/external_consumer.rs
// version: 1 // version: 2
//! Downstream-style consumer canary for the public Interface facade. //! Downstream-style consumer canaries for the public Interface facade.
fn consume_instruction(instruction: ksp_interface_lib::ProgramInstruction) -> (ksp_interface_lib::Pubkey, usize, usize) { fn consume_instruction(instruction: ksp_interface_lib::ProgramInstruction) -> (ksp_interface_lib::Pubkey, usize, usize) {
return (*instruction.program_id(), instruction.accounts().len(), instruction.data().len()); return (*instruction.program_id(), instruction.accounts().len(), instruction.data().len());
} }
fn consume_slot_lifecycle(event: ksp_interface_lib::SlotLifecycleEvent) -> (u64, ksp_interface_lib::SlotLifecycleStage) {
return (event.slot(), event.stage());
}
fn consume_transaction_execution(
event: ksp_interface_lib::TransactionExecutionEvent,
) -> (u64, ksp_interface_lib::TransactionSignature, ksp_interface_lib::TransactionExecutionOutcome) {
return (event.slot(), event.signature(), event.outcome());
}
#[test] #[test]
fn pre_005_external_consumer_uses_only_the_crate_root_facade() { fn pre_005_external_consumer_uses_only_the_crate_root_facade() {
let program_id = ksp_interface_lib::Pubkey::new_from_array([0xA1_u8; 32]); let program_id = ksp_interface_lib::Pubkey::new_from_array([0xA1_u8; 32]);
@@ -25,3 +35,13 @@ fn pre_005_external_consumer_uses_only_the_crate_root_facade() {
assert_eq!(ksp_interface_lib::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED.domain(), "interface"); assert_eq!(ksp_interface_lib::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED.domain(), "interface");
return; return;
} }
#[test]
fn v0_3_5_pre_004_external_consumer_uses_only_crate_root_for_both_acquisition_families() {
let lifecycle = ksp_interface_lib::SlotLifecycleEvent::new(u64::MAX, ksp_interface_lib::SlotLifecycleStage::OptimisticallyConfirmed);
assert_eq!(consume_slot_lifecycle(lifecycle), (u64::MAX, ksp_interface_lib::SlotLifecycleStage::OptimisticallyConfirmed));
let signature = ksp_interface_lib::TransactionSignature::new([0xA3_u8; 64]);
let execution = ksp_interface_lib::TransactionExecutionEvent::new(u64::MAX - 1, signature, ksp_interface_lib::TransactionExecutionOutcome::Failed);
assert_eq!(consume_transaction_execution(execution), (u64::MAX - 1, signature, ksp_interface_lib::TransactionExecutionOutcome::Failed));
return;
}

View File

@@ -1,75 +1,75 @@
// file: crates/ksp-interface-lib/tests/release_completeness.rs // file: crates/ksp-interface-lib/tests/release_completeness.rs
// version: 1 // version: 5
//! Release-level completeness canaries for the `0.2.13` Interface foundation. //! Release-completeness canaries for the public `ksp-interface-lib` surface.
#[test] fn crate_root_source() -> std::result::Result<std::string::String, std::io::Error> {
fn pre_005_exact_crate_root_export_inventory_is_stable() { let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src/lib.rs");
let crate_root = include_str!("../src/lib.rs"); return std::fs::read_to_string(path);
let mut actual = std::vec::Vec::new();
for line in crate_root.lines() {
let trimmed = line.trim();
if trimmed.starts_with("pub use ") {
actual.push(trimmed);
}
}
actual.sort_unstable();
let mut expected = std::vec![
"pub use self::error::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED;",
"pub use self::program_account_meta::MAX_PROGRAM_INSTRUCTION_ACCOUNTS;",
"pub use self::program_account_meta::ProgramAccountMeta;",
"pub use self::program_instruction::MAX_PROGRAM_INSTRUCTION_DATA_LEN;",
"pub use self::program_instruction::ProgramInstruction;",
"pub use ksp_core_lib::Pubkey;",
];
expected.sort_unstable();
assert_eq!(actual, expected);
assert!(!crate_root.contains("pub mod "));
return;
} }
#[test] #[test]
fn pre_005_production_module_inventory_contains_no_second_wire_domain() -> std::io::Result<()> { fn v0_3_5_pre_004_foundation_keeps_exactly_two_passive_acquisition_families() {
let source_root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
let entries = match std::fs::read_dir(source_root) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let mut names = std::vec::Vec::new();
for entry in entries {
let entry = match entry {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let file_type = match entry.file_type() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if !file_type.is_file() {
continue;
}
let name = match entry.file_name().into_string() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => continue,
};
if name.ends_with(".rs") {
names.push(name);
}
}
names.sort_unstable();
assert_eq!(names, std::vec!["error.rs", "lib.rs", "program_account_meta.rs", "program_instruction.rs"]);
return std::result::Result::Ok(());
}
#[test]
fn pre_005_foundation_has_one_error_code_and_two_bounded_passive_types() {
assert_eq!(ksp_interface_lib::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED.domain(), "interface"); assert_eq!(ksp_interface_lib::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED.domain(), "interface");
assert_eq!(ksp_interface_lib::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED.code(), "program_instruction_limit_exceeded"); assert_eq!(ksp_interface_lib::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED.code(), "program_instruction_limit_exceeded");
assert_eq!(ksp_interface_lib::MAX_PROGRAM_INSTRUCTION_ACCOUNTS, 255); assert_eq!(ksp_interface_lib::MAX_PROGRAM_INSTRUCTION_ACCOUNTS, 255);
assert_eq!(ksp_interface_lib::MAX_PROGRAM_INSTRUCTION_DATA_LEN, 10_240); assert_eq!(ksp_interface_lib::MAX_PROGRAM_INSTRUCTION_DATA_LEN, 10_240);
let program_id = ksp_interface_lib::Pubkey::new_from_array([0xB1_u8; 32]); let lifecycle = ksp_interface_lib::SlotLifecycleEvent::new(u64::MAX, ksp_interface_lib::SlotLifecycleStage::Rooted);
let account = ksp_interface_lib::ProgramAccountMeta::readonly(ksp_interface_lib::Pubkey::new_from_array([0xB2_u8; 32]), false); assert_eq!(lifecycle.slot(), u64::MAX);
let instruction = ksp_interface_lib::ProgramInstruction::try_new(program_id, std::vec![account], std::vec![0xB3_u8]); assert_eq!(lifecycle.stage(), ksp_interface_lib::SlotLifecycleStage::Rooted);
assert!(instruction.is_ok()); let signature = ksp_interface_lib::TransactionSignature::new([0xB4_u8; 64]);
let execution = ksp_interface_lib::TransactionExecutionEvent::new(u64::MAX, signature, ksp_interface_lib::TransactionExecutionOutcome::Succeeded);
assert_eq!(execution.slot(), u64::MAX);
assert_eq!(execution.signature(), signature);
assert_eq!(execution.outcome(), ksp_interface_lib::TransactionExecutionOutcome::Succeeded);
return;
}
#[test]
fn v0_3_5_pre_004_exact_crate_root_export_inventory_is_stable() -> std::result::Result<(), std::io::Error> {
let source = match crate_root_source() {
Ok(source) => source,
Err(error) => return Err(error),
};
let public_use_count = source.lines().filter(|line| return line.starts_with("pub use ")).count();
assert_eq!(public_use_count, 11);
assert!(source.contains("pub use self::error::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED;"));
assert!(source.contains("pub use self::program_account_meta::MAX_PROGRAM_INSTRUCTION_ACCOUNTS;"));
assert!(source.contains("pub use self::program_account_meta::ProgramAccountMeta;"));
assert!(source.contains("pub use self::program_instruction::MAX_PROGRAM_INSTRUCTION_DATA_LEN;"));
assert!(source.contains("pub use self::program_instruction::ProgramInstruction;"));
assert!(source.contains("pub use self::slot_lifecycle::SlotLifecycleEvent;"));
assert!(source.contains("pub use self::slot_lifecycle::SlotLifecycleStage;"));
assert!(source.contains("pub use self::transaction_execution::TransactionExecutionEvent;"));
assert!(source.contains("pub use self::transaction_execution::TransactionExecutionOutcome;"));
assert!(source.contains("pub use self::transaction_execution::TransactionSignature;"));
assert!(source.contains("pub use ksp_core_lib::Pubkey;"));
return Ok(());
}
#[test]
fn v0_3_5_pre_004_production_module_inventory_is_exact() -> std::result::Result<(), std::io::Error> {
let source = match crate_root_source() {
Ok(source) => source,
Err(error) => return Err(error),
};
let modules = source
.lines()
.filter_map(|line| return line.strip_prefix("mod ").and_then(|module| return module.strip_suffix(';')))
.collect::<std::vec::Vec<_>>();
assert_eq!(modules, std::vec!["error", "program_account_meta", "program_instruction", "slot_lifecycle", "transaction_execution"]);
assert!(!source.contains("serde"));
assert!(!source.contains("tracing"));
assert!(!source.contains("ksp_onchain_transport"));
assert!(!source.contains("ksp_store"));
return Ok(());
}
#[test]
fn v0_3_5_pre_004_evolutive_acquisition_enums_remain_non_exhaustive() {
let lifecycle_source = include_str!("../src/slot_lifecycle.rs");
let execution_source = include_str!("../src/transaction_execution.rs");
assert!(lifecycle_source.contains("#[non_exhaustive]\npub enum SlotLifecycleStage"));
assert!(execution_source.contains("#[non_exhaustive]\npub enum TransactionExecutionOutcome"));
return; return;
} }

View File

@@ -0,0 +1,100 @@
// file: crates/ksp-interface-lib/tests/security_hardening.rs
// version: 1
//! Adversarial and ownership-hardening canaries for passive Interface acquisition events.
const HOSTILE_MARKER: &str = "INTERFACE-ACQUISITION-SECRET-CANARY";
#[test]
fn v0_3_5_pre_005_signature_and_event_debug_never_render_signature_bytes() {
let mut bytes = [0x41_u8; 64];
bytes[..HOSTILE_MARKER.len()].copy_from_slice(HOSTILE_MARKER.as_bytes());
let signature = ksp_interface_lib::TransactionSignature::new(bytes);
assert_eq!(signature.as_bytes(), &bytes);
let signature_debug = std::format!("{signature:?}");
assert_eq!(signature_debug, "TransactionSignature(..)");
assert!(!signature_debug.contains(HOSTILE_MARKER));
let event = ksp_interface_lib::TransactionExecutionEvent::new(u64::MAX, signature, ksp_interface_lib::TransactionExecutionOutcome::Failed);
let event_debug = std::format!("{event:?}");
assert!(event_debug.contains("TransactionExecutionEvent"));
assert!(event_debug.contains("18446744073709551615"));
assert!(event_debug.contains("Failed"));
assert!(!event_debug.contains(HOSTILE_MARKER));
return;
}
#[test]
fn v0_3_5_pre_005_acquisition_structs_keep_exact_minimal_fields_without_payload_or_source_metadata() {
let lifecycle_source = include_str!("../src/slot_lifecycle.rs");
let execution_source = include_str!("../src/transaction_execution.rs");
assert!(lifecycle_source.contains("pub struct SlotLifecycleEvent {\n slot: u64,\n stage: SlotLifecycleStage,\n}"));
assert!(execution_source.contains("pub struct TransactionSignature([u8; 64]);"));
assert!(execution_source.contains(
"pub struct TransactionExecutionEvent {\n slot: u64,\n signature: TransactionSignature,\n outcome: TransactionExecutionOutcome,\n}"
));
for source in [lifecycle_source, execution_source] {
for forbidden in [
"std::vec::Vec<",
"std::string::String",
"Box<",
"serde_json::Value",
"timestamp:",
"provider_id:",
"endpoint:",
"filter:",
"network:",
"payload:",
] {
assert!(!source.contains(forbidden), "forbidden acquisition field/surface detected: {forbidden}");
}
}
return;
}
#[test]
fn v0_3_5_pre_005_interface_exports_no_second_raw_or_deferred_event_family() {
let crate_root = include_str!("../src/lib.rs");
for forbidden in [
"RawTransaction",
"RawAccount",
"RawPayload",
"RawPageCursor",
"RawRetention",
"RawObservation",
"TransactionLogEvent",
"TransactionCommitmentEvent",
"LedgerEntryEvent",
"VoteEvent",
"BlockEvent",
] {
assert!(!crate_root.contains(forbidden), "forbidden Interface export detected: {forbidden}");
}
return;
}
#[test]
fn v0_3_5_pre_005_program_instruction_foundation_remains_bounded_and_unchanged() {
assert_eq!(ksp_interface_lib::MAX_PROGRAM_INSTRUCTION_ACCOUNTS, 255);
assert_eq!(ksp_interface_lib::MAX_PROGRAM_INSTRUCTION_DATA_LEN, 10_240);
let program_id = ksp_interface_lib::Pubkey::new_from_array([0xD1_u8; 32]);
let account = ksp_interface_lib::ProgramAccountMeta::readonly(ksp_interface_lib::Pubkey::new_from_array([0xD2_u8; 32]), false);
let admitted = ksp_interface_lib::ProgramInstruction::try_new(
program_id,
std::vec![account; ksp_interface_lib::MAX_PROGRAM_INSTRUCTION_ACCOUNTS],
std::vec![0x5A_u8; ksp_interface_lib::MAX_PROGRAM_INSTRUCTION_DATA_LEN],
);
assert!(admitted.is_ok());
let admitted = match admitted {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert_eq!(admitted.accounts().len(), ksp_interface_lib::MAX_PROGRAM_INSTRUCTION_ACCOUNTS);
assert_eq!(admitted.data().len(), ksp_interface_lib::MAX_PROGRAM_INSTRUCTION_DATA_LEN);
let too_many_accounts =
ksp_interface_lib::ProgramInstruction::try_new(program_id, std::vec![account; ksp_interface_lib::MAX_PROGRAM_INSTRUCTION_ACCOUNTS + 1], std::vec![]);
assert!(too_many_accounts.is_err());
let oversized_data =
ksp_interface_lib::ProgramInstruction::try_new(program_id, std::vec![], std::vec![0x5A_u8; ksp_interface_lib::MAX_PROGRAM_INSTRUCTION_DATA_LEN + 1]);
assert!(oversized_data.is_err());
return;
}

View File

@@ -0,0 +1,29 @@
// file: crates/ksp-interface-lib/tests/slot_lifecycle_public_api.rs
// version: 2
//! Public API canaries for the shared slot lifecycle event contract.
#[test]
fn public_v0_3_5_pre_002_slot_lifecycle_contract_is_available_from_crate_root() {
let event = ksp_interface_lib::SlotLifecycleEvent::new(u64::MAX, ksp_interface_lib::SlotLifecycleStage::Rooted);
assert_eq!(event.slot(), u64::MAX);
assert_eq!(event.stage(), ksp_interface_lib::SlotLifecycleStage::Rooted);
}
#[test]
fn public_v0_3_5_pre_002_slot_lifecycle_stage_remains_downstream_evolvable() {
fn stage_label(stage: ksp_interface_lib::SlotLifecycleStage) -> &'static str {
return match stage {
ksp_interface_lib::SlotLifecycleStage::Processed => "processed",
ksp_interface_lib::SlotLifecycleStage::FirstShredReceived => "first_shred_received",
ksp_interface_lib::SlotLifecycleStage::Completed => "completed",
ksp_interface_lib::SlotLifecycleStage::CreatedBank => "created_bank",
ksp_interface_lib::SlotLifecycleStage::Dead => "dead",
ksp_interface_lib::SlotLifecycleStage::OptimisticallyConfirmed => "optimistically_confirmed",
ksp_interface_lib::SlotLifecycleStage::Rooted => "rooted",
_ => "future",
};
}
assert_eq!(stage_label(ksp_interface_lib::SlotLifecycleStage::Processed), "processed");
assert_eq!(stage_label(ksp_interface_lib::SlotLifecycleStage::Rooted), "rooted");
}

View File

@@ -0,0 +1,28 @@
// file: crates/ksp-interface-lib/tests/transaction_execution_public_api.rs
// version: 1
//! Public API canaries for the provider-neutral transaction execution contract.
#[test]
fn public_v0_3_5_pre_003_transaction_execution_contract_is_available_from_crate_root() {
let signature = ksp_interface_lib::TransactionSignature::new([0xC7_u8; 64]);
let event = ksp_interface_lib::TransactionExecutionEvent::new(u64::MAX, signature, ksp_interface_lib::TransactionExecutionOutcome::Succeeded);
assert_eq!(signature.as_bytes(), &[0xC7_u8; 64]);
assert_eq!(event.slot(), u64::MAX);
assert_eq!(event.signature(), signature);
assert_eq!(event.outcome(), ksp_interface_lib::TransactionExecutionOutcome::Succeeded);
}
fn outcome_label(outcome: ksp_interface_lib::TransactionExecutionOutcome) -> &'static str {
return match outcome {
ksp_interface_lib::TransactionExecutionOutcome::Succeeded => "succeeded",
ksp_interface_lib::TransactionExecutionOutcome::Failed => "failed",
_ => "future",
};
}
#[test]
fn public_v0_3_5_pre_003_transaction_execution_outcome_remains_downstream_evolvable() {
assert_eq!(outcome_label(ksp_interface_lib::TransactionExecutionOutcome::Succeeded), "succeeded");
assert_eq!(outcome_label(ksp_interface_lib::TransactionExecutionOutcome::Failed), "failed");
}

View File

@@ -0,0 +1,45 @@
// file: crates/ksp-interface-lib/unit_tests/slot_lifecycle.rs
// version: 2
#[test]
fn slot_lifecycle_stages_are_distinct_copy_and_complete_for_the_admitted_family() {
fn assert_copy<T: Copy>() {}
assert_copy::<crate::SlotLifecycleStage>();
let stages = [
crate::SlotLifecycleStage::Processed,
crate::SlotLifecycleStage::FirstShredReceived,
crate::SlotLifecycleStage::Completed,
crate::SlotLifecycleStage::CreatedBank,
crate::SlotLifecycleStage::Dead,
crate::SlotLifecycleStage::OptimisticallyConfirmed,
crate::SlotLifecycleStage::Rooted,
];
for (index, stage) in stages.iter().enumerate() {
for other in stages.iter().skip(index + 1) {
assert_ne!(stage, other);
}
}
}
#[test]
fn slot_lifecycle_event_preserves_full_u64_slot_and_stage() {
fn assert_copy<T: Copy>() {}
assert_copy::<crate::SlotLifecycleEvent>();
let event = crate::SlotLifecycleEvent::new(u64::MAX, crate::SlotLifecycleStage::Rooted);
assert_eq!(event.slot(), u64::MAX);
assert_eq!(event.stage(), crate::SlotLifecycleStage::Rooted);
let copied = event;
assert_eq!(event, copied);
}
#[test]
fn slot_lifecycle_debug_is_bounded_and_contains_only_shared_fields() {
let event = crate::SlotLifecycleEvent::new(42, crate::SlotLifecycleStage::OptimisticallyConfirmed);
let debug = std::format!("{event:?}");
assert!(debug.len() <= 128);
assert!(debug.contains("slot: 42"));
assert!(debug.contains("OptimisticallyConfirmed"));
assert!(!debug.contains("provider"));
assert!(!debug.contains("yellowstone"));
assert!(!debug.contains("websocket"));
}

View File

@@ -0,0 +1,28 @@
// file: crates/ksp-interface-lib/unit_tests/transaction_execution.rs
// version: 1
#[test]
fn transaction_signature_preserves_exact_bytes_and_redacts_debug() {
let signature = crate::TransactionSignature::new([0xA5_u8; 64]);
assert_eq!(signature.as_bytes(), &[0xA5_u8; 64]);
assert_eq!(std::format!("{signature:?}"), "TransactionSignature(..)");
}
#[test]
fn transaction_execution_outcomes_are_distinct_and_copy() {
let succeeded = crate::TransactionExecutionOutcome::Succeeded;
let failed = crate::TransactionExecutionOutcome::Failed;
assert_ne!(succeeded, failed);
let copied = succeeded;
assert_eq!(copied, crate::TransactionExecutionOutcome::Succeeded);
}
#[test]
fn transaction_execution_event_preserves_full_slot_signature_and_outcome() {
let signature = crate::TransactionSignature::new([0xB6_u8; 64]);
let event = crate::TransactionExecutionEvent::new(u64::MAX, signature, crate::TransactionExecutionOutcome::Failed);
assert_eq!(event.slot(), u64::MAX);
assert_eq!(event.signature(), signature);
assert_eq!(event.outcome(), crate::TransactionExecutionOutcome::Failed);
assert_eq!(std::format!("{event:?}"), "TransactionExecutionEvent { slot: 18446744073709551615, signature: \"<omitted>\", outcome: Failed }");
}

View File

@@ -0,0 +1,191 @@
<!-- file: deltas/0.3.5/pre.001-fix.001.md -->
<!-- version: 1 -->
# Delta `0.3.5-pre.001-fix.001` — correction du gate Interface acquisition events
## Base requise
```text
0.3.5-pre.001
workspace.package.version = 0.3.5-pre.1
```
Ce fix est strictement documentaire. Conformément au workflow KSP, il ne modifie pas `workspace.package.version` et ne touche aucun fichier Cargo/Rust/runtime/config.
## Motif du fix
Le brainstorming post-gate a identifié deux décisions trop conservatrices dans `pre.001` :
1. `OptimisticConfirmation <-> Yellowstone Confirmed` et `Root <-> Yellowstone Finalized` représentent bien les mêmes faits de lifecycle lorsqu'ils sont normalisés avec des noms KSP qui conservent la sémantique source ;
2. la catégorie générique « transaction status » mélangeait snapshot, commitment et exécution, masquant un candidat event-only transversal possible.
## Corrections de conception
### Slot lifecycle
L'intersection admise devient :
```text
SlotLifecycleStage
+ Processed
+ FirstShredReceived
+ Completed
+ CreatedBank
+ Dead
+ OptimisticallyConfirmed
+ Rooted
```
Mappings corrigés :
```text
Solana slotsUpdates::OptimisticConfirmation
-> SlotLifecycleStage::OptimisticallyConfirmed
<- Yellowstone SlotStatus::Confirmed
Solana rootSubscribe / slotsUpdates::Root
-> SlotLifecycleStage::Rooted
<- Yellowstone SlotStatus::Finalized
```
Les noms `OptimisticallyConfirmed` et `Rooted` évitent de promouvoir les labels Yellowstone `Confirmed`/`Finalized` en terminologie KSP universelle. `Frozen` reste hors intersection.
### Transaction execution
`TransactionExecutionEvent` est rouvert comme **candidat actif de `0.3.5`**, conceptuellement :
```text
TransactionExecutionEvent {
slot,
signature,
outcome = Succeeded | Failed,
}
```
Producers candidats :
```text
Solana logsSubscribe
Yellowstone TransactionStatus
Helius transactionSubscribe
```
Le type n'est pas encore admis. `pre.003` doit d'abord auditer la représentation KSP de signature, les mappings exacts, l'anti-duplication Store et le consumer concret. Aucun type partiel ne doit être créé si ce gate échoue.
### Logs et commitment
Les familles sont désormais explicitement séparées :
```text
TransactionLogEvent
-> idée différée ; bornes/consumer/multi-producer à démontrer
getSignatureStatuses
-> snapshot Transport
signatureSubscribe
-> transition one-shot de commitment Transport
TransactionCommitmentEvent
-> non admis ; ne jamais fusionner avec TransactionExecutionEvent par Option soup
```
### Extensions provider/protocol
La règle d'ownership est clarifiée :
```text
DTO provider/protocol spécifique
-> Transport
fait passif provider-neutral réellement commun
-> Interface après gate sémantique
```
Ainsi `Helius transactionSubscribe` et Yellowstone `Entry` restent Transport-owned comme DTOs. Leur caractère non standard n'interdit pas qu'un futur fait commun qu'ils permettent d'observer soit Interface-owned.
## Traçabilité TODO/IDEAS
Le fix ne crée pas artificiellement une entrée durable séparée pendant que le gate `0.3.5` est encore actif :
```text
TransactionExecutionEvent -> suivi actif dans plan/validation 0.3.5
TransactionLogEvent -> idée différée explicitement tracée dans plan/validation
```
Si `TransactionLogEvent` reste hors scope à la réconciliation documentaire finale, il devra être transféré dans le TODO/IDEAS durable approprié.
## Plan de prereleases corrigé
```text
pre.002 implémentation SlotLifecycleEvent/Stage à 7 stages
pre.003 gate TransactionExecutionEvent + implémentation seulement si gate complet
pre.004 canaris externes + complétude API
pre.005 hardening final
pre.006 gate technique final
pre.007 réconciliation documentaire finale
pre.008 préparation de publication
rel.001 stable 0.3.5
```
## Gate opérateur reçu pour `pre.001`
Avant ce fix, l'opérateur a rejoué :
```text
cargo clean
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
```
Résultat fourni :
```text
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
Markdown table audit: clean (251 table(s), 137 file(s))
cargo check --workspace: PASS
cargo clippy --workspace --all-targets: PASS
cargo test -p ksp-interface-lib: PASS
```
Les tests Interface détaillés fournis sont tous propres : 10 unitaires, 4 dependency-boundary, 1 external-consumer, 5 public-api, 3 release-completeness, aucun échec de doc-test.
## Fichiers modifiés
```text
docs/plans/026-V0_3_5_INTERFACE_ACQUISITION_EVENTS_PLAN.md
docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md
```
## Fichiers ajoutés
```text
deltas/0.3.5/pre.001-fix.001.md
```
## Fichiers explicitement inchangés
```text
Cargo.toml
crates/**
README.md
ROADMAP.md
CHANGELOG.md
```
## Gate demandé après application du fix
Le fix ne change que Markdown. Rejouer :
```text
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
```
Aucun rerun Cargo n'est requis par le contenu du fix lui-même.

119
deltas/0.3.5/pre.001.md Normal file
View File

@@ -0,0 +1,119 @@
<!-- file: deltas/0.3.5/pre.001.md -->
<!-- version: 1 -->
# Delta `0.3.5-pre.001` — audit et planification Interface acquisition events
## Base requise
```text
v0.3.4
workspace.package.version = 0.3.4
```
Le log opérateur fourni avec l'archive d'entrée montre le gate stable `v0.3.4` propre. Cette tranche part exclusivement de l'archive `khadhroony-solana-project-v0.3.4.zip` fournie par l'opérateur.
## Objectif
Exécuter le gate `pre.001` prescrit par `prompts/024-V0_3_5_START_PROMPT.md` : lecture, inventaire producer/consumer, audit sémantique multi-transport actuel, ownership Interface/Transport/Store, threat model, sizing et planification.
Aucun modèle Rust n'est implémenté dans cette tranche.
## Décisions prises
Une seule famille passe le gate d'admission Interface :
```text
SlotLifecycleEvent
+ Processed
+ FirstShredReceived
+ Completed
+ CreatedBank
+ Dead
```
Cette intersection correspond uniquement aux faits communs démontrés entre Solana WebSocket et Yellowstone. Les metadata riches restent Transport.
Décisions négatives :
```text
Root -X-> Finalized
OptimisticConfirmation -X-> Confirmed
logsSubscribe -> reporté
TransactionStatusObservation -> reporté
vote -> reporté
account/program -> Store/Transport
transaction/block -> Store/Transport
Yellowstone Entry -> Transport
Helius Enhanced transaction -> Transport
notification after commit -> Store API selon KSP-NOTIFY-001..006
```
La release est redimensionnée autour d'une seule famille et réserve explicitement les couloirs hardening, gate technique final, réconciliation documentaire puis publication.
## Fichiers ajoutés
```text
docs/plans/026-V0_3_5_INTERFACE_ACQUISITION_EVENTS_PLAN.md
docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md
deltas/0.3.5/pre.001.md
```
## Fichiers modifiés
```text
Cargo.toml
```
Modifications Cargo :
```text
header version: 382 -> 383
workspace.package.version: 0.3.4 -> 0.3.5-pre.1
```
## Fichiers supprimés
Aucun.
## Validations exécutées
```text
python3 scripts/audit_rust_workspace_rules.py
-> General Rust rule audit: clean
-> Rust export completeness audit: 0 candidate(s)
-> KSP workspace Rust rule audit: clean
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.3.5
-> Markdown table audit: clean (251 table(s), 137 file(s))
```
## Validations non exécutées dans l'environnement d'assemblage
`cargo` n'est pas installé dans l'environnement d'assemblage. Les commandes suivantes ne sont donc pas déclarées réussies :
```text
cargo fmt --all
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test -p ksp-interface-lib
```
Elles doivent être rejouées par l'opérateur après application de l'overlay.
## Questions ouvertes
Aucune question bloquante pour `pre.002`.
Les familles logs/status/vote/root restent volontairement reportées et ne doivent être rouvertes que si un nouveau producer/consumer concret apporte une convergence qui n'existe pas dans ce gate.
## Suite prévue
```text
pre.002 implémentation SlotLifecycleEvent/Stage
pre.003 canaris externes + complétude API
pre.004 hardening final
pre.005 gate technique final
pre.006 réconciliation documentaire finale
pre.007 préparation de publication
rel.001 stable 0.3.5
```

View File

@@ -0,0 +1,67 @@
# Delta `0.3.5-pre.002-fix.001`
## Objet
Corriger les défauts de conformité Rust révélés par le gate opérateur de `0.3.5-pre.002`, sans modifier le contrat fonctionnel `SlotLifecycleEvent` / `SlotLifecycleStage`.
## Base
```text
0.3.5-pre.002
workspace.package.version = 0.3.5-pre.2
```
## Diagnostic opérateur
Le code fonctionnel et les tests ciblés passent, mais le gate global n'est pas propre :
- 15 violations `RUST-FMT-101` pour des lignes vides à l'intérieur de fonctions de tests ;
- 2 usages de l'opérateur `?` rejetés par `clippy::question_mark_used` ;
- closures à retour implicite rejetées par `clippy::implicit_return` ;
- documentation crate-level manquante dans les deux nouveaux tests d'intégration.
## Correction
- supprimer les lignes vides internes aux fonctions concernées ;
- remplacer `?` par une propagation explicite via `match` ;
- rendre explicites les `return` des closures concernées ;
- ajouter la documentation crate-level aux tests d'intégration ;
- conserver strictement les sept variantes de `SlotLifecycleStage` et la forme de `SlotLifecycleEvent` ;
- ne pas ajouter de `allow`, de dépendance, de codec, de runtime ou de conversion Transport.
## Version
Le fix modifie des fichiers Rust ; la version workspace est donc incrémentée suivant la convention de fix prerelease :
```text
workspace.package.version = 0.3.5-pre.2.fix.1
```
Le numéro documentaire reste :
```text
0.3.5-pre.002-fix.001
```
## Fichiers modifiés
```text
Cargo.toml
crates/ksp-interface-lib/tests/release_completeness.rs
crates/ksp-interface-lib/tests/slot_lifecycle_public_api.rs
crates/ksp-interface-lib/unit_tests/slot_lifecycle.rs
docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md
deltas/0.3.5/pre.002-fix.001.md
```
## Gate demandé
```bash
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 -p ksp-program-api
```

180
deltas/0.3.5/pre.002.md Normal file
View File

@@ -0,0 +1,180 @@
<!-- file: deltas/0.3.5/pre.002.md -->
<!-- version: 1 -->
# Delta `0.3.5-pre.002` — `SlotLifecycleEvent`
## Base requise
```text
0.3.5-pre.001-fix.001
workspace.package.version = 0.3.5-pre.1
```
Gate d'entrée opérateur reçu après le fix :
```text
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
Markdown table audit: clean (240 table(s), 138 file(s))
```
## Objectif
Matérialiser uniquement la famille passive provider-neutral `SlotLifecycleEvent` admise par le gate corrigé de `pre.001`, sans ouvrir la famille candidate `TransactionExecutionEvent` et sans déplacer les DTOs protocol/provider hors de Transport.
## Version
Cette tranche est une nouvelle prerelease non-fix. La version workspace devient :
```text
0.3.5-pre.2
```
Le manifest `crates/ksp-interface-lib/Cargo.toml` reste inchangé et continue d'hériter de la version workspace.
## Implémentation
Nouvelle surface crate-root :
```text
SlotLifecycleStage
SlotLifecycleEvent
```
`SlotLifecycleStage` est `#[non_exhaustive]` et contient exactement :
```text
Processed
FirstShredReceived
Completed
CreatedBank
Dead
OptimisticallyConfirmed
Rooted
```
`SlotLifecycleEvent` reste volontairement minimal :
```text
slot: u64
stage: SlotLifecycleStage
```
avec uniquement :
```text
SlotLifecycleEvent::new(slot, stage)
SlotLifecycleEvent::slot()
SlotLifecycleEvent::stage()
```
Aucune validation faillible n'est ajoutée : tout `u64` est un slot admissible au niveau de ce contrat passif.
## Ownership et dépendances
Préservé :
```text
ksp-interface-lib -> ksp-core-lib
```
Absent :
```text
serde
codec wire
tracing/logging runtime
tokio/channel/event bus
ksp-onchain-transport-lib
ksp-store-api
ksp-store-lib
ksp-store-postgres-lib
```
Aucun converter depuis les DTOs Solana WS, Yellowstone ou Helius n'est ajouté dans Interface. Les conversions appartiennent à la future composition/consumer.
## Canaris
Ajoutés :
```text
crates/ksp-interface-lib/unit_tests/slot_lifecycle.rs
crates/ksp-interface-lib/tests/slot_lifecycle_public_api.rs
```
Le premier verrouille les 7 stages, le domaine `u64`, les traits passifs retenus et un `Debug` borné/provider-neutral. Le second vérifie la consommation depuis crate root et la capacité d'évolution downstream de l'enum non-exhaustive.
Le canari historique `tests/release_completeness.rs` est ajusté afin que son inventaire exact accepte la seule extension prévue de cette tranche : `slot_lifecycle` et ses deux reexports publics.
## Fichiers modifiés
```text
Cargo.toml
crates/ksp-interface-lib/src/lib.rs
crates/ksp-interface-lib/tests/release_completeness.rs
docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md
```
## Fichiers ajoutés
```text
crates/ksp-interface-lib/src/slot_lifecycle.rs
crates/ksp-interface-lib/unit_tests/slot_lifecycle.rs
crates/ksp-interface-lib/tests/slot_lifecycle_public_api.rs
deltas/0.3.5/pre.002.md
```
## Fichiers explicitement hors scope
```text
crates/ksp-interface-lib/Cargo.toml
crates/ksp-onchain-transport-lib/**
crates/ksp-store-api/**
crates/ksp-store-lib/**
crates/ksp-store-postgres-lib/**
README.md
ROADMAP.md
CHANGELOG.md
```
## Validations exécutées dans l'environnement d'assemblage
Validations statiques de l'overlay :
```text
version workspace = 0.3.5-pre.2
nouvelle source Interface sans serde/tracing/tokio/Transport/Store
crate-root = 4 modules privés / 8 reexports publics attendus
SlotLifecycleStage = 7 variantes prévues
aucun TransactionExecutionEvent introduit
archive = fichiers modifiés/ajoutés uniquement
```
L'environnement d'assemblage ne fournit pas le workspace Cargo complet exécutable ; aucun `cargo check`, Clippy ou test Rust de cette tranche n'est donc déclaré réussi ici.
## Gate opérateur demandé
```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 -p ksp-program-api
```
`ksp-program-api` est inclus car il est le consumer actuel direct de `ksp-interface-lib`, même si `pre.002` ne modifie aucun contrat Program existant.
## Questions ouvertes
Aucune question ne bloque `SlotLifecycleEvent`.
Le seul gate fonctionnel suivant reste `TransactionExecutionEvent` : représentation de signature, convergence exacte Solana/Yellowstone/Helius, anti-duplication Store et consumer concret doivent être prouvés en `pre.003` avant toute implémentation.
## Suite
```text
0.3.5-pre.003 — gate TransactionExecutionEvent ; implémentation uniquement si le gate est complet
```

193
deltas/0.3.5/pre.003.md Normal file
View File

@@ -0,0 +1,193 @@
<!-- file: deltas/0.3.5/pre.003.md -->
<!-- version: 1 -->
# Delta `0.3.5-pre.003` — `TransactionExecutionEvent`
## Base requise
```text
0.3.5-pre.2.fix.1
```
Le gate opérateur de cette base confirme les audits Rust, `cargo check --workspace`, Clippy, `cargo test -p ksp-interface-lib` et `cargo test -p ksp-program-api`. Le seul défaut restant est une double ligne vide dans `docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md`; conformément à la décision opérateur, cette correction documentaire est absorbée par le présent delta au lieu de créer un fix dédié.
## Version
Le delta modifie la surface Rust Interface. La version workspace devient :
```text
0.3.5-pre.3
```
## Mission
Fermer le gate dédié à `TransactionExecutionEvent` prévu par le plan corrigé de `0.3.5` : représentation de signature, convergence Solana/Yellowstone/Helius, anti-duplication Store et consumer concret doivent tous être démontrés avant création du type.
## Décision de gate
Le gate passe avec une projection strictement minimale :
```text
TransactionSignature([u8; 64])
TransactionExecutionOutcome {
Succeeded,
Failed,
}
TransactionExecutionEvent {
slot,
signature,
outcome,
}
```
La convergence retenue est :
```text
Solana logsSubscribe
context.slot + signature + err
Yellowstone TransactionStatus
slot + signature + err
Helius transactionSubscribe
slot + signature + résultat d'exécution seulement lorsque la forme/detail mode le rend déterminable
```
`err` n'est jamais copié dans Interface. Solana `err: null`, Yellowstone `err: None` et un Helius `err: null` explicitement disponible donnent `Succeeded`; une erreur explicite donne `Failed`.
Pour Helius, la projection est conservative : un `err` omis, une forme `none`, inconnue ou sans information d'erreur exploitable ne produit pas artificiellement un `TransactionExecutionEvent`.
## Signature
`TransactionSignature` possède exactement 64 octets déjà décodés.
Interface n'ajoute :
```text
aucun base58
aucun serde
aucun codec
aucune crate Solana/Anza supplémentaire
aucune capability Wallet de signature
```
Le `Debug` est redacted et ne rend jamais les bytes.
`ksp-store-api::RawTransactionSignature` reste Store-owned. Aucun alias/reexport/converter cross-domain n'est ajouté ; la future composition convertit explicitement les 64 octets si nécessaire.
## Ownership
Restent Transport-owned :
```text
Solana logs notification DTO
Yellowstone TransactionStatus DTO
Helius transactionSubscribe DTOs
commitment
subscription/filter metadata
transaction index
logs
memo
block time
confirmation status
provider timestamps
opaque transaction errors
```
Interface possède uniquement le fait passif commun.
## Consumer
Le futur worker RAW live peut utiliser `TransactionExecutionEvent` comme signal provider-neutral d'exécution observée et décider ensuite d'hydrater/persister un `RawTransaction` par sa propre composition/policy.
Le premier backfill historique `0.3.6` reste indépendant de cette API.
## Surface Rust
Ajouts crate-root :
```text
TransactionSignature
TransactionExecutionOutcome
TransactionExecutionEvent
```
`TransactionExecutionOutcome` est `#[non_exhaustive]`.
Les champs des structs restent privés et la surface ne possède ni runtime ni comportement réseau.
## Tests/canaris
Ajoutés :
```text
unit_tests/transaction_execution.rs
tests/transaction_execution_public_api.rs
```
Le canari `tests/release_completeness.rs` évolue pour figer :
```text
5 modules privés de production
11 reexports crate-root
transaction_execution comme seule nouvelle famille pre.003
absence de serde/tracing/Transport/Store dans la racine Interface
```
## Documentation
Le plan et la validation passent `TransactionExecutionEvent` de candidat actif à `ADMIS pre.003`.
`TransactionLogEvent` reste une idée différée distincte. `TransactionCommitmentEvent` reste non admis. Yellowstone `Entry` et les DTOs Helius restent Transport-owned même lorsqu'ils pourront contribuer à de futurs faits Interface réellement transversaux.
La double ligne vide signalée par le gate opérateur précédent est supprimée dans la validation.
## Fichiers
Modifiés :
```text
Cargo.toml
crates/ksp-interface-lib/src/lib.rs
crates/ksp-interface-lib/tests/release_completeness.rs
docs/plans/026-V0_3_5_INTERFACE_ACQUISITION_EVENTS_PLAN.md
docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md
```
Ajoutés :
```text
crates/ksp-interface-lib/src/transaction_execution.rs
crates/ksp-interface-lib/unit_tests/transaction_execution.rs
crates/ksp-interface-lib/tests/transaction_execution_public_api.rs
deltas/0.3.5/pre.003.md
```
Explicitement inchangés :
```text
crates/ksp-interface-lib/Cargo.toml
crates/ksp-onchain-transport-lib/**
crates/ksp-store-api/**
crates/ksp-store-lib/**
crates/ksp-store-postgres-lib/**
README.md
ROADMAP.md
CHANGELOG.md
```
## Gate opérateur
```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 -p ksp-program-api
```
Les gates Cargo ne sont pas déclarés réussis par l'environnement d'assemblage.

153
deltas/0.3.5/pre.004.md Normal file
View File

@@ -0,0 +1,153 @@
<!-- file: deltas/0.3.5/pre.004.md -->
<!-- version: 1 -->
# Delta `0.3.5-pre.004` — canaris externes et complétude API
## Base requise
```text
0.3.5-pre.3
```
Le gate opérateur de `pre.003` est entièrement propre : audits Rust/Markdown, `cargo check --workspace`, Clippy, tests `ksp-interface-lib` et `ksp-program-api` passent.
## Version
La tranche modifie des sources Rust de test/canari. La version workspace devient :
```text
0.3.5-pre.4
```
## Mission
Verrouiller la surface des deux familles passives admises sans ajouter de comportement ni de type de production :
```text
SlotLifecycleEvent / SlotLifecycleStage
TransactionExecutionEvent / TransactionExecutionOutcome / TransactionSignature
```
## Consumer externe
`tests/external_consumer.rs` consomme maintenant les deux familles acquisition uniquement depuis le crate-root public.
Le canari prouve notamment qu'un consumer downstream peut récupérer :
```text
slot + stage
slot + signature + outcome
```
sans accès aux modules privés, à Transport ou à Store.
Le canari Program historique reste conservé.
## Inventaire exact
`tests/release_completeness.rs` fige :
```text
5 modules privés de production
11 reexports crate-root
2 familles passives d'acquisition exactement
```
Aucun nouveau module ni export de production n'est ajouté par `pre.004`.
## Evolutivité
Les deux enums publics évolutifs restent explicitement `#[non_exhaustive]` :
```text
SlotLifecycleStage
TransactionExecutionOutcome
```
Les canaris publics downstream de `pre.002` et `pre.003` restent inchangés et continuent d'utiliser un fallback `_`.
## Firewalls négatifs
Le scan de production inclut désormais les deux modules acquisition.
Un canari spécifique interdit dans `slot_lifecycle.rs` et `transaction_execution.rs` les paths de :
```text
Config
Logging
Transport
Store
reqwest
serde
Solana instruction
Tauri
Tokio
Tonic
tracing
borsh/bincode/wincode
```
Le manifeste `ksp-interface-lib` reste Core-only et inchangé.
## Hors scope
Toujours interdits/reportés :
```text
converters Transport -> Interface
provider DTOs
Store RAW
serde/codecs
runtime/channels
logging
TransactionLogEvent
TransactionCommitmentEvent
```
## Fichiers
Modifiés :
```text
Cargo.toml
crates/ksp-interface-lib/tests/dependency_boundary.rs
crates/ksp-interface-lib/tests/external_consumer.rs
crates/ksp-interface-lib/tests/release_completeness.rs
docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md
```
Ajouté :
```text
deltas/0.3.5/pre.004.md
```
Explicitement inchangés :
```text
crates/ksp-interface-lib/Cargo.toml
crates/ksp-interface-lib/src/**
crates/ksp-interface-lib/unit_tests/**
crates/ksp-onchain-transport-lib/**
crates/ksp-store-api/**
crates/ksp-store-lib/**
crates/ksp-store-postgres-lib/**
crates/ksp-program-api/**
README.md
ROADMAP.md
CHANGELOG.md
```
## Gate opérateur
```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 -p ksp-program-api
```
Les gates Cargo restent à exécuter par l'opérateur.

View File

@@ -0,0 +1,78 @@
# Delta `0.3.5-pre.005-fix.001`
## Objet
Corriger le faux négatif du canari de manifeste ajouté par `0.3.5-pre.005`, sans modifier le contrat fonctionnel ni le graphe de dépendances de `ksp-interface-lib`.
## Base
```text
0.3.5-pre.005
workspace.package.version = 0.3.5-pre.5
```
## Diagnostic opérateur
Le gate du `2026-08-31` est propre pour les audits Rust/Markdown et `cargo check --workspace`, mais le nouveau test :
```text
v0_3_5_pre_005_manifest_keeps_exact_core_only_graph_without_features_or_hidden_dependency_sections
```
échoue avec :
```text
left: 2
right: 1
```
La cause est le comptage textuel `manifest.matches("ksp-core-lib")` appliqué à la déclaration :
```text
ksp-core-lib = { path = "../ksp-core-lib" }
```
Cette ligne contient légitimement deux occurrences de la chaîne tout en déclarant une seule dépendance. Le manifeste lui-même est correct.
## Correction
- conserver les interdictions de sections `[features]`, `[dev-dependencies]` et `[build-dependencies]` ;
- conserver l'exigence d'une seule section `[dependencies]` ;
- extraire cette section puis réutiliser `manifest_dependency_names(...)` ;
- vérifier l'inventaire exact des clés de dépendance : `ksp-core-lib` uniquement ;
- ne modifier aucun manifeste de crate, source de production, type public ou dependency graph.
## Version
Le fix modifie une source Rust de test ; la version workspace devient :
```text
workspace.package.version = 0.3.5-pre.5.fix.1
```
Le numéro documentaire reste :
```text
0.3.5-pre.005-fix.001
```
## Fichiers modifiés
```text
Cargo.toml
crates/ksp-interface-lib/tests/dependency_boundary.rs
docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md
deltas/0.3.5/pre.005-fix.001.md
```
## Gate demandé
```bash
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 -p ksp-program-api
```

138
deltas/0.3.5/pre.005.md Normal file
View File

@@ -0,0 +1,138 @@
<!-- file: deltas/0.3.5/pre.005.md -->
<!-- version: 1 -->
# Delta `0.3.5-pre.005` — hardening et complétude Interface
## Base requise
```text
0.3.5-pre.4
```
Le gate opérateur de `pre.004` est entièrement propre : audits Rust/Markdown, `cargo check --workspace`, Clippy, tests `ksp-interface-lib` et `ksp-program-api` passent.
## Version
La tranche ajoute/modifie des sources Rust de test/canari. La version workspace devient :
```text
0.3.5-pre.5
```
## Mission
Fermer le hardening fonctionnel de `0.3.5` sans ajouter de troisième famille Interface ni modifier les contrats de production admis :
```text
SlotLifecycleEvent / SlotLifecycleStage
TransactionExecutionEvent / TransactionExecutionOutcome / TransactionSignature
```
## Hardening adversarial
Le nouveau `tests/security_hardening.rs` vérifie :
- conservation byte-exact d'une signature hostile de 64 octets ;
- redaction de la signature dans son `Debug` et dans celui de `TransactionExecutionEvent` ;
- conservation de `u64::MAX` sans narrowing ;
- structure minimale exacte des deux events ;
- absence de collection/payload/timestamp/provider/endpoint/filter/network dans les contrats acquisition ;
- absence de second RAW ou de famille reportée exportée silencieusement ;
- non-régression des bornes `ProgramInstruction` historiques.
## Graphe et dépendances
`tests/dependency_boundary.rs` ajoute un canari final imposant :
```text
une seule section [dependencies]
exactement une référence ksp-core-lib
aucune [features]
aucune [dev-dependencies]
aucune [build-dependencies]
```
Le manifeste `ksp-interface-lib` reste inchangé et Core-only.
## Candidats reportés
Aucun candidat reporté n'est promu par hardening :
```text
TransactionLogEvent -> idée différée
TransactionCommitmentEvent -> reporté
VoteEvent -> reporté
LedgerEntryEvent -> reporté
Account/Program RAW event -> rejet Interface
Transaction complète -> rejet Interface
Block event -> rejet Interface
Persisted-data available -> Store API owner
```
`TransactionLogEvent` devra être transféré vers TODO/IDEAS durable pendant `pre.007` s'il reste hors scope.
## Fichiers
Modifiés :
```text
Cargo.toml
crates/ksp-interface-lib/tests/dependency_boundary.rs
docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md
```
Ajoutés :
```text
crates/ksp-interface-lib/tests/security_hardening.rs
deltas/0.3.5/pre.005.md
```
Explicitement inchangés :
```text
crates/ksp-interface-lib/Cargo.toml
crates/ksp-interface-lib/src/**
crates/ksp-interface-lib/unit_tests/**
crates/ksp-onchain-transport-lib/**
crates/ksp-store-api/**
crates/ksp-store-lib/**
crates/ksp-store-postgres-lib/**
crates/ksp-program-api/**
README.md
ROADMAP.md
CHANGELOG.md
```
## Validations exécutées dans l'environnement d'assemblage
```text
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
```
## Validations non exécutées dans l'environnement d'assemblage
`cargo` n'est pas installé dans le sandbox d'assemblage. Restent donc à exécuter côté opérateur :
```text
cargo fmt --all
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test -p ksp-interface-lib
cargo test -p ksp-program-api
```
## Décisions
- deux familles acquisition exactement restent admises ;
- aucun contrat production n'est modifié ;
- aucun nouveau bound métier n'est inventé ;
- la signature reste une primitive passive fixe de 64 octets sans codec ;
- aucune metadata source/provider n'entre dans Interface ;
- aucun second RAW n'est créé ;
- Program instruction foundation reste inchangée.
## Questions ouvertes
Aucune question fonctionnelle bloquante pour `pre.006`. La seule idée différée explicitement à conserver durablement est `TransactionLogEvent`.

80
deltas/0.3.5/pre.006.md Normal file
View File

@@ -0,0 +1,80 @@
# Delta `0.3.5-pre.006`
## Objet
Matérialiser le gate technique final de `0.3.5` après le hardening Interface et son fix de canari, sans rouvrir la surface fonctionnelle.
## Base
```text
0.3.5-pre.005-fix.001
workspace.package.version = 0.3.5-pre.5.fix.1
```
## Gate d'entrée opérateur
Le gate du `2026-08-31` est propre :
```text
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
Markdown table audit: clean (240 table(s), 144 file(s))
cargo check --workspace: PASS
cargo clippy --workspace --all-targets: PASS
cargo test -p ksp-interface-lib: PASS
cargo test -p ksp-program-api: PASS
cargo test --workspace: PASS
```
Les tests réseau/live et probes diagnostiques restent `ignored` conformément à leur contrat. Aucun test non ignoré n'échoue.
## Scope
`pre.006` ne modifie aucun code Rust, aucun manifeste de crate et aucun contrat Interface. Elle prépare uniquement le gate technique final prévu par `prompts/024-V0_3_5_START_PROMPT.md`.
La clôture doit établir :
```text
ksp-interface-lib -> ksp-core-lib uniquement
aucune feature Interface
aucune dev/build dependency Interface
2 familles acquisition exactement
aucun runtime/serde/codec/logging/Transport/Store dans Interface
aucun duplicate dependency nouveau imputable à 0.3.5
```
## Version
La version workspace devient :
```text
workspace.package.version = 0.3.5-pre.6
```
## Fichiers
```text
Cargo.toml
docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md
deltas/0.3.5/pre.006.md
```
Aucun autre fichier n'est modifié.
## Gate demandé
```bash
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 smoke réseau n'est requis par défaut.

154
deltas/0.3.5/pre.007.md Normal file
View File

@@ -0,0 +1,154 @@
# Delta `0.3.5-pre.007`
## Objet
Réconcilier la documentation durable avec la surface Interface réellement acquise après le gate technique final propre de `pre.006`, sans rouvrir le code ni les contrats publics.
## Base
```text
0.3.5-pre.006
workspace.package.version = 0.3.5-pre.6
```
La base opérateur inclut les corrections locales de numéros d'en-tête signalées après l'overlay `pre.006` :
```text
Cargo.toml header 389
docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md header 8
```
Ces corrections d'en-tête ne changent pas le contenu fonctionnel du gate validé.
## Gate d'entrée
Le gate `pre.006` est intégralement propre :
```text
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
Markdown table audit: clean (240 table(s), 145 file(s))
cargo check --workspace: PASS
cargo clippy --workspace --all-targets: PASS
cargo test -p ksp-interface-lib: PASS
cargo test --workspace: PASS
```
Les graphes confirment :
```text
ksp-interface-lib -> ksp-core-lib uniquement
aucune feature Interface
aucune dev/build dependency Interface
aucun duplicate dependency nouveau imputable à 0.3.5
```
Les tests live/réseau et probes diagnostiques restent `ignored` conformément à leur contrat.
## Réconciliation
La documentation durable était encore partiellement figée sur la foundation Program-only :
```text
README Interface -> rôle Program-only et affirmation « aucun événement runtime »
USAGE Interface -> page explicitement matérialisée par 0.2.13
architecture -> rôle Interface décrit surtout comme façade wire Program
TransactionLogEvent -> idée différée encore enfermée dans le plan/validation de release
```
`pre.007` corrige ces incohérences sans transformer Interface en runtime :
```text
Program wire contracts -> Interface
provider-neutral passive acquisition -> Interface lorsque convergence exacte démontrée
rich HTTP/WS/gRPC/provider DTOs -> Transport
persistent/replayable RAW -> Store API
post-commit durable-data wake-up -> Store API
converters Transport -> Interface -> composition/consumer
backlog/recovery -> Store
```
La surface durable documentée contient exactement les deux familles acquisition acquises :
```text
SlotLifecycleEvent
TransactionExecutionEvent
```
`TransactionLogEvent` est transféré dans `docs/IDEAS.md` et reste soumis à un futur gate consumer + bornes + convergence multi-source.
## Version
```text
workspace.package.version = 0.3.5-pre.7
```
## Fichiers
Modifiés :
```text
Cargo.toml
crates/ksp-interface-lib/README.md
crates/ksp-interface-lib/USAGE.md
docs/IDEAS.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/009-ACQUISITION_WORKERS_AND_JOBS.md
docs/plans/026-V0_3_5_INTERFACE_ACQUISITION_EVENTS_PLAN.md
docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md
```
Ajouté :
```text
deltas/0.3.5/pre.007.md
```
Explicitement inchangés :
```text
CHANGELOG.md
ROADMAP.md
prompts/**
crates/ksp-interface-lib/src/**
crates/ksp-interface-lib/tests/**
crates/ksp-interface-lib/unit_tests/**
crates/ksp-interface-lib/Cargo.toml
crates/ksp-onchain-transport-lib/**
crates/ksp-store-api/**
crates/ksp-store-lib/**
crates/ksp-store-postgres-lib/**
crates/ksp-program-api/**
```
## Gate d'assemblage
Les contrôles statiques disponibles dans l'environnement d'assemblage sont propres :
```text
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
Markdown table audit: clean (240 table(s), 146 file(s))
```
Aucun code Rust n'est modifié par cette tranche. Les commandes Cargo restent à confirmer dans le repository opérateur après application de l'overlay.
## Gate demandé
```bash
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 -p ksp-program-api
```
Aucun smoke réseau n'est requis. Après gate propre, la tranche suivante est la préparation de publication minimale `0.3.5-pre.008`.

View File

@@ -0,0 +1,177 @@
<!-- file: deltas/0.3.5/pre.008-fix.001.md -->
<!-- version: 1 -->
# Delta `0.3.5-pre.008-fix.001` — enrichissement du prompt `0.3.6` et trajectoire Job/Worker
## Base requise
```text
0.3.5-pre.008
workspace.package.version = 0.3.5-pre.8
```
Ce fix est **strictement documentaire**. Il ne modifie aucun fichier Rust/build/runtime/config et ne change donc pas `workspace.package.version`.
`Cargo.toml` reste exactement celui de `pre.008` :
```text
workspace.package.version = 0.3.5-pre.8
```
## Motif du fix
Le prompt `025` de `pre.008` était insuffisamment détaillé par rapport :
- aux règles de construction des prompts KSP ;
- à la portée réelle attendue de `0.3.6` ;
- aux fonctionnalités de backfill déjà présentes historiquement dans kbot3 ;
- au besoin d'observabilité par une couche supérieure ;
- à la trajectoire Worker qui doit reprendre le même pattern de notifications.
Il laissait également le nom de la crate concrète à décider alors que la direction est désormais fixée :
```text
ksp-job-backfill-lib
```
## Corrections du prompt `0.3.6`
Le prompt version 2 ouvre désormais explicitement :
```text
ksp-job-api
+
ksp-job-backfill-lib
+
backfill RawTransaction historique fonctionnel
+
lifecycle/progression/outcome
+
notifications/listeners sûrs pour UI/composition
```
### Archive kbot3
L'archive :
```text
khadhroony-bot3_v0.5.3-pre.005-fix010.zip
```
redevient **obligatoire en `0.3.6-pre.001`**, mais uniquement comme inventaire fonctionnel historique.
Le plan doit construire une matrice :
```text
REPRENDRE FONCTIONNELLEMENT
REDESSINER POUR KSP
REPORTER
REJETER
```
Le code, les anciennes crates, DTOs, SQL, retries et choix runtime kbot3 ne sont pas à copier.
### Parité fonctionnelle minimale à réauditer
Le prompt rend explicites les invariants historiquement démontrés :
- scans latest/before/after/explicit selon audit ;
- anchors et directions ;
- candidats/pages dédupliqués ;
- nearest-newer/gap-fill si conservé ;
- frontier de complétion contiguë ;
- reprise après cancellation sans saut ;
- cancellation coopérative pendant RPC long et wait/backoff ;
- ownership séparé du retry Transport et du retry Job ;
- compteurs de campagne distinguant sélection, complétion, insert/idempotence/existing, missing et observations.
### Notifications Job
Le prompt exige désormais un vrai contrat d'observabilité `ksp-job-api` :
```text
identity
state/phase
progress/snapshot
checkpoint/frontier
sequence ou ordre observable
terminal outcome
safe error code
listener isolation/backpressure
resynchronisation après perte/coalescing
```
Ces notifications sont explicitement distinctes :
```text
Interface acquisition events
Store KSP-NOTIFY-* post-commit wake-ups
logging/tracing
```
Aucune dépendance Tokio/channel précise n'est imposée dans `ksp-job-api` avant l'audit `pre.001`.
### Alignement Worker futur
Le même pattern conceptuel devra être repris par `ksp-worker-api`, sans créer dès `0.3.6` une crate générique spéculative supplémentaire.
## Corrections du ROADMAP
La trajectoire RAW opérationnelle devient explicitement :
```text
0.3.6 ksp-job-api + ksp-job-backfill-lib + notifications
0.3.7 application backfill/inspection RAW
0.3.8 ksp-worker-api
0.3.9 ksp-worker-live-transactions-retriever-lib
0.3.10 application monitoring/visualisation Jobs + Workers
```
L'ancien placeholder générique « worker/service live ensuite » est supprimé au profit de releases nommées et ordonnées.
## Correction du CHANGELOG
Le paragraphe ouvrant `0.3.6` est aligné sur la portée réelle du prompt version 2 et sur la nouvelle trajectoire ROADMAP.
Aucune information sur le contenu fonctionnel déjà stabilisé de `0.3.5` n'est changée.
## Versions d'en-tête
```text
CHANGELOG.md 24 -> 25
ROADMAP.md 99 -> 100
prompts/025-V0_3_6_START_PROMPT.md 1 -> 2
deltas/0.3.5/pre.008-fix.001.md nouveau -> 1
```
## Fichiers modifiés
Exactement :
```text
CHANGELOG.md
ROADMAP.md
prompts/025-V0_3_6_START_PROMPT.md
deltas/0.3.5/pre.008-fix.001.md
```
Explicitement inchangés :
```text
Cargo.toml
crates/**
docs/architecture/**
docs/plans/026-V0_3_5_INTERFACE_ACQUISITION_EVENTS_PLAN.md
docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md
```
## Gate demandé
Le fix ne modifie aucun Rust. Le gate minimal est :
```bash
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
```
Aucun rerun Cargo n'est requis par ce fix documentaire lui-même.

112
deltas/0.3.5/pre.008.md Normal file
View File

@@ -0,0 +1,112 @@
# Delta `0.3.5-pre.008`
## Objet
Préparer la publication de `0.3.5` après le gate technique final et la réconciliation documentaire propres, sans rouvrir le code ni les documents de conception de la release.
## Base
```text
0.3.5-pre.007
workspace.package.version = 0.3.5-pre.7
```
Le gate opérateur de `pre.007` est propre :
```text
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
Markdown table audit: clean (240 table(s), 146 file(s))
cargo check --workspace: PASS
cargo clippy --workspace --all-targets: PASS
cargo test -p ksp-interface-lib: PASS
cargo test -p ksp-program-api: PASS
```
Le gate technique final `pre.006` avait déjà confirmé `cargo test --workspace` et les graphes Cargo Interface Core-only.
## Modifications
### Version workspace
```text
0.3.5-pre.8
```
Le numéro d'en-tête de `Cargo.toml` est incrémenté avec cette modification réelle.
### `CHANGELOG.md`
Ajout de l'entrée de release `0.3.5` décrivant :
- `SlotLifecycleEvent` et ses sept stages admis ;
- `TransactionSignature`, `TransactionExecutionOutcome` et `TransactionExecutionEvent` ;
- les frontières Transport / Interface / Store ;
- le graphe Interface Core-only ;
- les gates finaux propres ;
- l'ouverture de `0.3.6` sur Job API + premier backfill RAW.
### `ROADMAP.md`
- `0.3.5` passe à terminé ;
- `0.3.6` est précisé comme premier backfill RAW avec ownership Job de la policy/progression/checkpoint ;
- les TODO historiques sont réconciliés avec les deux familles Interface réellement acquises ;
- `TransactionLogEvent` reste une idée différée et `vote` reste hors Interface.
### Prompt suivant
Ajout de :
```text
prompts/025-V0_3_6_START_PROMPT.md
```
Le prompt ouvre `0.3.6` sur :
```text
ksp-job-api
+ premier backfill historique RAW concret
+ audit prioritaire RawTransaction par adresse
+ getSignaturesForAddress -> getTransaction comme candidat
+ policy/batch/progression/checkpoint/cancellation Job-owned
```
L'archive historique kbot3 n'est pas requise pour cette release.
## Fichiers de la tranche
Exactement :
```text
Cargo.toml
CHANGELOG.md
ROADMAP.md
prompts/025-V0_3_6_START_PROMPT.md
deltas/0.3.5/pre.008.md
```
Explicitement inchangés :
```text
crates/**
docs/architecture/**
docs/plans/026-V0_3_5_INTERFACE_ACQUISITION_EVENTS_PLAN.md
docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md
crates/ksp-interface-lib/README.md
crates/ksp-interface-lib/USAGE.md
```
## Gate demandé
```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 -p ksp-program-api
```
Si ce gate reste propre, la tranche suivante est `0.3.5-rel.001` et doit être strictement mécanique : version stable `0.3.5`, delta de release et aucune correction fonctionnelle/documentaire.

123
deltas/0.3.5/rel.001.md Normal file
View File

@@ -0,0 +1,123 @@
<!-- file: deltas/0.3.5/rel.001.md -->
<!-- version: 1 -->
# Delta `0.3.5-rel.001` — publication stable
## Base requise
```text
0.3.5-pre.008
workspace.package.version = 0.3.5-pre.8
```
La base inclut le fix documentaire :
```text
0.3.5-pre.008-fix.001
```
dont le gate minimal est propre :
```text
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
Markdown table audit: clean (240 table(s), 149 file(s))
```
Les gates techniques et documentaires précédents ont déjà fermé la release :
- `pre.006` : gate technique final complet, tests workspace et graphes Cargo Interface ;
- `pre.007` : réconciliation documentaire finale ;
- `pre.008` : lane minimale de publication ;
- `pre.008-fix.001` : enrichissement documentaire du prompt `0.3.6` et de la trajectoire Job/Worker.
## Objet
Publier mécaniquement la version stable :
```text
0.3.5
```
Aucune correction fonctionnelle, architecturale ou documentaire n'est autorisée dans cette tranche.
## Modification
### `Cargo.toml`
Le numéro d'en-tête est incrémenté avec la modification réelle :
```text
391 -> 392
```
La version workspace devient :
```text
0.3.5-pre.8 -> 0.3.5
```
Aucune autre ligne de `Cargo.toml` n'est modifiée.
## Surface publiée
`0.3.5` stabilise dans `ksp-interface-lib` les deux familles passives d'acquisition retenues par les gates de la release :
```text
SlotLifecycleEvent
TransactionExecutionEvent
```
La surface reste provider-neutral, Core-only, sans runtime/event bus/serde/codec, sans duplication de la couche RAW Store et sans élargissement de la fondation Program existante.
Le prompt suivant déjà préparé reste :
```text
prompts/025-V0_3_6_START_PROMPT.md
```
et ouvre `0.3.6` sur le développement parallèle de `ksp-job-api` et `ksp-job-backfill-lib`, avec reprise fonctionnelle du backfill historique kbot3 et notifications/listeners observables.
## Fichiers de la tranche
Exactement :
```text
Cargo.toml
deltas/0.3.5/rel.001.md
```
Explicitement inchangés :
```text
CHANGELOG.md
ROADMAP.md
prompts/025-V0_3_6_START_PROMPT.md
crates/**
docs/**
```
## Gate demandé
```bash
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
```
Après ce gate, la release stable peut être taguée :
```text
v0.3.5
```
La session suivante démarre depuis cette base stable en exécutant :
```text
prompts/025-V0_3_6_START_PROMPT.md
```

View File

@@ -1,5 +1,5 @@
<!-- file: docs/IDEAS.md --> <!-- file: docs/IDEAS.md -->
<!-- version: 26 --> <!-- version: 27 -->
# Idées à explorer # Idées à explorer
@@ -96,6 +96,14 @@ Aucun `ksp-data-api` global n'est prévu actuellement. Les modèles appartiennen
Réévaluer seulement si les premières implémentations montrent une duplication réellement nuisible impossible à résoudre sans contrat commun supplémentaire. Réévaluer seulement si les premières implémentations montrent une duplication réellement nuisible impossible à résoudre sans contrat commun supplémentaire.
### Événement passif de logs transaction
**Status :** À explorer après la première surface d'événements Interface
Conserver `TransactionLogEvent` comme idée différée, sans type public tant qu'un consumer concret et des bornes sûres ne sont pas démontrés. Le futur gate doit comparer au minimum `logsSubscribe` et les formes transaction/meta des transports réellement consommés, sans supposer que leur richesse, leur volumétrie ou leur disponibilité sont identiques.
Un éventuel contrat partagé doit rester un fait passif provider-neutral, distinct de `TransactionExecutionEvent` et des logs replayables contenus dans `RawTransaction`. Il ne doit créer ni `RawLog` Store par défaut, ni event bus Interface, ni wrapper d'un DTO Transport.
### Mesure du trafic et metering futur ### Mesure du trafic et metering futur
**Status :** À explorer plus tard **Status :** À explorer plus tard

View File

@@ -1,5 +1,5 @@
<!-- file: docs/architecture/002-LAYERS_AND_DEPENDENCIES.md --> <!-- file: docs/architecture/002-LAYERS_AND_DEPENDENCIES.md -->
<!-- version: 7 --> <!-- version: 8 -->
# Couches et dépendances KSP # Couches et dépendances KSP
@@ -150,7 +150,7 @@ Les exécutables et couches supérieures n'importent pas directement les crates
Exceptions bas niveau explicitement autorisées restent limitées aux primitives stables décidées par les règles KSP. Exceptions bas niveau explicitement autorisées restent limitées aux primitives stables décidées par les règles KSP.
`ksp-interface-lib` concentre les interfaces/wires officielles ou compatibles afin d'éviter les doublons de générations et les dépendances protocolaires dans les couches supérieures. `ksp-interface-lib` concentre les contrats passifs KSP réellement partagés — wires officiels/compatibles et faits provider-neutral admis — afin d'éviter les doublons de générations, les dépendances protocolaires dans les couches supérieures et la promotion accidentelle d'un DTO Transport en contrat transversal. Les converters depuis Transport restent dans la composition.
## Principe contract-first ## Principe contract-first

View File

@@ -1,5 +1,5 @@
<!-- file: docs/architecture/003-COMPONENT_CONTRACTS.md --> <!-- file: docs/architecture/003-COMPONENT_CONTRACTS.md -->
<!-- version: 11 --> <!-- version: 12 -->
# Contrats initiaux des composants KSP # Contrats initiaux des composants KSP
@@ -94,13 +94,13 @@ Il ne possède pas `WalletPolicy` ni les règles d'autorisation d'exécution.
Le wallet temporaire JSON historique n'est pas migré. Le wallet temporaire JSON historique n'est pas migré.
## Interface / wire ## Interface / contrats passifs
`ksp-interface-lib` est la façade wire officielle KSP et expose aussi une API publique wire réutilisable par `ksp-program-lib` et les extensions Program externes. `ksp-interface-lib` possède les contrats passifs KSP réellement partagés entre composants : façade wire officielle lorsqu'un protocole commun l'exige, et faits d'acquisition provider-neutral lorsqu'une sémantique commune exacte est démontrée. Sa surface publique est réutilisable notamment par `ksp-program-lib`, les extensions Program externes et les composants de composition/acquisition qui ne doivent pas dépendre d'un DTO Transport particulier.
Aucune `ksp-interface-api` séparée n'est retenue actuellement. Aucune `ksp-interface-api` séparée n'est retenue actuellement.
La crate sélectionne entre réexport contrôlé, wrapper ou implémentation wire compatible selon stabilité, ownership et graphe de dépendances des interfaces externes. La crate sélectionne entre réexport contrôlé, wrapper ou implémentation KSP-owned selon stabilité, ownership et graphe de dépendances. Elle ne possède ni Transport/runtime, ni Store/persistence, ni event bus ; les conversions depuis les DTOs Transport restent dans la composition.
## Program ## Program

View File

@@ -1,5 +1,5 @@
<!-- file: docs/architecture/004-COMPONENT_INVENTORY.md --> <!-- file: docs/architecture/004-COMPONENT_INVENTORY.md -->
<!-- version: 26 --> <!-- version: 27 -->
# Inventaire initial des composants KSP # Inventaire initial des composants KSP
@@ -32,7 +32,7 @@ Ce document maintient l'inventaire synthétique des composants retenus ou presse
| Yellowstone | `ksp-onchain-transport-lib` | lib | Stable | `0.2.9` | client gRPC standard/provider-neutral | | Yellowstone | `ksp-onchain-transport-lib` | lib | Stable | `0.2.9` | client gRPC standard/provider-neutral |
| Off-chain price | `ksp-offchain-transport-lib` | lib | Stable | `0.2.11` | prix SOL/USD multi-provider, limits et availability | | Off-chain price | `ksp-offchain-transport-lib` | lib | Stable | `0.2.11` | prix SOL/USD multi-provider, limits et availability |
| SOL Prices Desk | `ksp-app-solprices-desk` | app | Stable | `0.2.12` | HID provider-agnostic pour visualisation/refresh prix | | SOL Prices Desk | `ksp-app-solprices-desk` | app | Stable | `0.2.12` | HID provider-agnostic pour visualisation/refresh prix |
| Wire | `ksp-interface-lib` | lib | Stable | `0.2.13` | façade wire officielle + API publique wire | | Interface passive | `ksp-interface-lib` | lib | Stable | `0.2.13` | façade wire + contrats passifs partagés, dont événements acquisition provider-neutral |
| Program API | `ksp-program-api` | API | Stable | `0.2.14` | contrats extensibles Program | | Program API | `ksp-program-api` | API | Stable | `0.2.14` | contrats extensibles Program |
| Program impl. | `ksp-program-lib` | lib | Retenu | vertical slices ultérieurs | implementations Program officielles | | Program impl. | `ksp-program-lib` | lib | Retenu | vertical slices ultérieurs | implementations Program officielles |
| Program extension | `ksp-program-<name>-lib` | lib externe | À la demande | dès besoin | implementation externe de `ksp-program-api` | | Program extension | `ksp-program-<name>-lib` | lib externe | À la demande | dès besoin | implementation externe de `ksp-program-api` |

View File

@@ -1,5 +1,5 @@
<!-- file: docs/architecture/005-DEPENDENCY_GRAPH.md --> <!-- file: docs/architecture/005-DEPENDENCY_GRAPH.md -->
<!-- version: 17 --> <!-- version: 18 -->
# Graphe de dépendances KSP # Graphe de dépendances KSP
@@ -169,16 +169,17 @@ Le Wallet stocke/ouvre/signe. Il ne décide pas si une dépense est autorisée.
`WalletPolicy` historique migre conceptuellement vers execution policy, pas vers `ksp-wallet-lib`. `WalletPolicy` historique migre conceptuellement vers execution policy, pas vers `ksp-wallet-lib`.
## Interface / wire ## Interface / contrats passifs
```text ```text
ksp-interface-lib ksp-interface-lib
-> ksp-core-lib -> ksp-core-lib
-> ksp-logging-lib lorsque runtime logging réel -> official/compatible wire dependencies uniquement lorsqu'un protocole réel les exige
-> official/compatible wire dependencies retenues
``` ```
`ksp-interface-lib` contient **à la fois** la façade wire officielle et une API publique wire utilisable par les implémentations Program officielles/externes. `ksp-interface-lib` contient la façade wire officielle **et** les contrats passifs KSP-owned dont la sémantique est réellement partagée, y compris des faits d'acquisition provider-neutral. Les composants Program et les composants de composition peuvent consommer ces contrats sans importer un runtime Transport.
Un événement passif n'autorise aucune dépendance Interface vers Transport, Store, worker/job ou logging runtime. Les converters depuis des DTOs Transport restent dans la composition.
Aucune `ksp-interface-api` séparée n'est retenue actuellement. Aucune `ksp-interface-api` séparée n'est retenue actuellement.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/architecture/006-WIRE_AND_PROGRAM.md --> <!-- file: docs/architecture/006-WIRE_AND_PROGRAM.md -->
<!-- version: 2 --> <!-- version: 3 -->
# Wire, Program API et implémentations Program # Wire, Program API et implémentations Program
@@ -22,7 +22,7 @@ Les types Rust exacts restent volontairement à définir lors de la première im
## `ksp-interface-lib` ## `ksp-interface-lib`
`ksp-interface-lib` est la façade wire officielle KSP pour les programmes Solana supportés officiellement. `ksp-interface-lib` est la façade des contrats passifs KSP réellement partagés. Elle possède notamment les wires officiels nécessaires aux programmes Solana supportés et peut aussi posséder des faits d'acquisition provider-neutral lorsqu'ils ne sont ni des DTOs Transport ni des modèles persistants Store.
Elle possède ou réexporte de manière contrôlée les contrats nécessaires tels que : Elle possède ou réexporte de manière contrôlée les contrats nécessaires tels que :
@@ -38,29 +38,51 @@ Les Program IDs fondamentaux restent possédés par `ksp-core-lib`.
`ksp-interface-lib` ne possède pas : `ksp-interface-lib` ne possède pas :
- RPC/WS/provider ; - RPC/WS/gRPC/provider DTOs ou sessions ;
- wallet/signature ; - event bus, worker, job ou scheduler ;
- persistence ; - wallet/signature capability ;
- persistence, Store, cursor ou retention ;
- matérialisation ; - matérialisation ;
- interprétation canonique/métier d'une instruction ; - interprétation canonique/métier d'une instruction ;
- policy/safety ; - policy/safety ;
- lifecycle d'exécution réseau. - lifecycle d'exécution réseau.
Les événements d'acquisition admis dans Interface sont de simples faits passifs supplémentaires. Ils ne remplacent pas le DTO Transport riche et leurs conversions appartiennent à la composition qui connaît les deux contrats.
## API publique de `ksp-interface-lib` ## API publique de `ksp-interface-lib`
`ksp-interface-lib` reste une seule crate pour l'instant : aucune `ksp-interface-api` séparée n'est créée. `ksp-interface-lib` reste une seule crate pour l'instant : aucune `ksp-interface-api` séparée n'est créée.
La façade doit néanmoins exposer une API publique wire stable et réutilisable par : La façade doit néanmoins exposer une API publique stable et réutilisable par :
```text ```text
ksp-program-lib ksp-program-lib
external ksp-program-<name>-lib external ksp-program-<name>-lib
compositions/acquisition consumers utilisant un fait passif partagé
``` ```
Une implementation externe peut donc expérimenter contre les mêmes contrats wire publics avant intégration officielle dans KSP. Lorsqu'un wire externe devient officiel, son intégration dans `ksp-interface-lib` doit rester compatible avec l'API publique retenue, sauf évolution de contrat explicitement versionnée/documentée. Une implementation externe peut donc expérimenter contre les mêmes contrats wire publics avant intégration officielle dans KSP. Lorsqu'un wire externe devient officiel, son intégration dans `ksp-interface-lib` doit rester compatible avec l'API publique retenue, sauf évolution de contrat explicitement versionnée/documentée.
Si une future contrainte de dépendances démontre qu'un split `ksp-interface-api` apporte une valeur réelle, il pourra être étudié selon `KSP-API-007`; la symétrie avec Program ne suffit pas. Si une future contrainte de dépendances démontre qu'un split `ksp-interface-api` apporte une valeur réelle, il pourra être étudié selon `KSP-API-007`; la symétrie avec Program ne suffit pas.
## Événements passifs d'acquisition
Interface peut posséder un événement d'acquisition seulement si plusieurs sources/consumers partagent exactement le même fait et que le type reste plus petit que les DTOs producteurs. Le contrat ne doit pas accumuler des champs `Option` pour simuler l'union de protocoles hétérogènes.
La frontière durable est :
```text
DTO Transport riche et lossless
|
| conversion explicite par composition/consumer
v
événement Interface passif provider-neutral
```
Un événement Interface ne devient ni un message de bus obligatoire ni une donnée durable. Les informations replayables appartiennent à `ksp-store-api`; les détails de transport, provider, commitment, session, timestamp source-specific et diagnostics qui ne font pas partie du fait commun restent Transport-owned.
Les familles actuellement démontrées sont le lifecycle de slot et le résultat d'exécution d'une transaction. Toute nouvelle famille exige un nouveau gate de convergence, consumer et bornes.
## Propriété des codecs wire ## Propriété des codecs wire
Pour le code KSP officiel, les dépendances directement utilisées pour encoder/décoder les formats wire, notamment `borsh`, `wincode` ou codecs équivalents, appartiennent normalement à `ksp-interface-lib`. Pour le code KSP officiel, les dépendances directement utilisées pour encoder/décoder les formats wire, notamment `borsh`, `wincode` ou codecs équivalents, appartiennent normalement à `ksp-interface-lib`.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/architecture/009-ACQUISITION_WORKERS_AND_JOBS.md --> <!-- file: docs/architecture/009-ACQUISITION_WORKERS_AND_JOBS.md -->
<!-- version: 7 --> <!-- version: 8 -->
# Acquisition, workers, jobs et pipelines spécialisés # Acquisition, workers, jobs et pipelines spécialisés
@@ -353,11 +353,14 @@ ksp-job-backfill
ksp-worker-raw-retriever ksp-worker-raw-retriever
-> ksp-worker-api -> ksp-worker-api
-> ksp-onchain-transport-lib -> ksp-onchain-transport-lib
-> ksp-interface-lib # seulement si un fait passif partagé aide la composition live
-> ksp-store-lib # façade Store ; backend sélectionné par feature + Config -> ksp-store-lib # façade Store ; backend sélectionné par feature + Config
-> ksp-config-lib -> ksp-config-lib
-> ksp-logging-lib -> ksp-logging-lib
``` ```
Les événements Interface peuvent servir de signal provider-neutral à la composition live, mais ne constituent jamais le backlog durable. Après crash ou perte d'un événement, la reprise s'appuie sur Store et sur les primitives de replay/hydratation appropriées.
### CORE replay/worker ### CORE replay/worker
```text ```text

View File

@@ -0,0 +1,757 @@
<!-- file: docs/plans/026-V0_3_5_INTERFACE_ACQUISITION_EVENTS_PLAN.md -->
<!-- version: 4 -->
# 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<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 :
```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.

View File

@@ -0,0 +1,936 @@
<!-- file: docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md -->
<!-- version: 9 -->
# Validation `0.3.5` — Interface passive acquisition events
## 1. Portée
Ce document suit toute la fermeture de `0.3.5`. `pre.002` matérialise `SlotLifecycleEvent`; `pre.003` admet `TransactionExecutionEvent`; `pre.004` verrouille les canaris externes et la complétude ; `pre.005` ajoute le hardening adversarial ; son fix corrige uniquement un faux négatif du canari de manifeste ; `pre.006` ferme le gate technique final ; `pre.007` réconcilie la documentation durable sans modifier la surface Rust.
Base :
```text
v0.3.4
workspace.package.version = 0.3.4
```
Version de travail :
```text
0.3.5-pre.7
```
## 2. Gate d'entrée `v0.3.4`
Le log opérateur fourni avec l'archive indique un gate stable `v0.3.4` propre : audits Rust/Markdown, `cargo check`, Clippy, tests ciblés des crates, `cargo test --workspace`, `ksp-store-lib --no-default-features` et graphes Store ont été exécutés sans échec visible ; les smokes réseau restent explicitement ignorés/opt-in.
Cette preuve est l'entrée de `0.3.5`. Elle n'est pas réétiquetée comme validation exécutée dans l'environnement d'assemblage de `pre.001`.
## 3. Gate de lecture `pre.001`
| Contrôle | Résultat | Preuve / décision |
| ------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------- |
| règles globales relues | PASS | familles KSP-API/TRANSPORT/STORE/NOTIFY/PROC/REL et DEP applicables relues |
| architecture durable relue | PASS | Interface passif, Transport protocol/runtime, Store persistant, composition owner des conversions |
| fondation Interface stable relue | PASS | surface Program petite ; dépendance normale Core-only |
| Store API RAW relu | PASS | `RawTransaction*` + `RawAccount*` restent Store-owned |
| Store/PostgreSQL `0.3.2``0.3.4` relu | PASS | RAW physique 10/10 fermé ; aucune rétention account |
| surfaces HTTP/WS/gRPC/Helius relues | PASS | status/log/slot/vote/account/transaction/block/Entry inventoriés |
| documentation Solana courante auditée | PASS | `slotSubscribe`, `slotsUpdatesSubscribe`, status/log/vote/account/program, getTransaction/getBlock |
| Yellowstone upstream courant audité | PASS | `SlotStatus` et familles SubscribeUpdate courantes |
| Helius courant audité | PASS | standard WSS préserve wire Solana ; Enhanced `transactionSubscribe` reste extension provider |
| producer/consumer inventory | PASS | lifecycle slot 7 stages admis ; TransactionExecutionEvent rouvert comme candidat actif |
| threat model | PASS | mega enum, Option soup, Store duplication, mapping lifecycle explicite et provider promotion couverts |
| sizing release | PASS | 8 prereleases prévues avant `rel.001` ; second candidat soumis à gate dédié |
## 4. Matrice de décision par famille
| Famille | Décision | Owner | Motif déterminant |
| ------------------------------------- | ------------------- | ------------------------------ | ---------------------------------------------------------------------------------------- |
| slot lifecycle intersection | ADMIS | Interface | 7 faits compacts normalisés ; détails protocol/provider restent Transport |
| transaction execution | ADMIS `pre.003` | Interface | `slot + TransactionSignature + Succeeded/Failed` multi-producer |
| logs realtime | IDÉE DIFFÉRÉE | Interface si futur gate | payload variable/bornes/consumer non démontrés ; ne pas mélanger avec execution |
| signature commitment/snapshot | REPORTÉ | Transport | HTTP snapshot != WS one-shot commitment ; distinct de l'exécution |
| vote | REPORTÉ | Transport | gossip pré-consensus != transaction qualifiée `is_vote` |
| account/program | REJET Interface | Store/Transport | risque de doublon `RawAccountState`/observation ou wrapper Transport |
| transaction | REJET Interface | Store/Transport | `RawTransaction`/observation déjà persistants |
| block | REJET Interface | Transport | conteneur/payload d'acquisition ; aucun besoin passif transversal démontré |
| Yellowstone Entry | REPORTÉ | Transport DTO | mono-producer aujourd'hui ; un futur fait transversal KSP serait réaudité pour Interface |
| persisted-data available notification | REJET Interface | Store API | ownership normatif `KSP-NOTIFY-001..006` |
| Helius Enhanced transaction | PRODUCTEUR ADMIS | Transport DTO / Interface fact | DTO provider-specific ; projection conservative vers le fait commun admis |
## 5. Intersection lifecycle retenue
| Stage Interface prévu | Producer Solana standard | Producer Yellowstone | Verdict | Détails exclus du contrat commun |
| ------------------------- | -------------------------------------- | -------------------- | ------- | -------------------------------------------------------------------------------- |
| `Processed` | `slotSubscribe` | `Processed` | PASS | WS parent/root ; Yellowstone filters/created_at/parent |
| `FirstShredReceived` | `slotsUpdates::FirstShredReceived` | `FirstShredReceived` | PASS | timestamps/metadata source |
| `Completed` | `slotsUpdates::Completed` | `Completed` | PASS | timestamps/metadata source |
| `CreatedBank` | `slotsUpdates::CreatedBank` | `CreatedBank` | PASS | parent reste dans le DTO Transport riche |
| `Dead` | `slotsUpdates::Dead` | `Dead` | PASS | diagnostic `err`/`dead_error` et timestamp restent Transport |
| `OptimisticallyConfirmed` | `slotsUpdates::OptimisticConfirmation` | `Confirmed` | PASS | nom KSP conserve l'optimistic confirmation ; delivery/metadata restent Transport |
| `Rooted` | `rootSubscribe` / `slotsUpdates::Root` | `Finalized` | PASS | nom KSP conserve le fait root ; `Finalized` reste un label Yellowstone source |
| — | `slotsUpdates::Frozen` | — | FAIL | pas d'intersection |
## 6. Validation anti-Option-soup
Les modèles suivants sont explicitement **non admis** par le gate :
```text
TransactionStatusObservation {
confirmations?: ...,
confirmation_status?: ...,
received?: ...,
is_vote?: ...,
transaction_index?: ...,
logs?: ...,
...
}
SlotLifecycleEvent {
parent?: ...,
root?: ...,
timestamp?: ...,
dead_error?: ...,
provider?: ...,
...
}
```
La surface admise reste conceptuellement :
```text
SlotLifecycleEvent { slot, stage }
```
Le candidat séparé, non encore admis, reste :
```text
TransactionExecutionEvent { slot, signature, outcome }
```
## 7. Validation anti-duplication Store
| Modèle Store stable | Action `0.3.5` | Justification |
| ------------------------------- | ------------------- | ---------------------------------------------- |
| `RawTransaction` | inchangé | payload durable/replayable |
| `RawTransactionObservation` | inchangé | provenance persistante |
| `RawAccountState` | inchangé | état account durable |
| `RawAccountObservation` | inchangé | observation persistante |
| pagination/cursors RAW | inchangés | contrats de navigation Store |
| retention/tombstone transaction | inchangés | lifecycle de persistence Store |
| notification after commit | non implémentée ici | ownership Store API réservé par `KSP-NOTIFY-*` |
Canari conceptuel : aucun futur fichier source Interface de `0.3.5` ne doit importer `ksp_store_api`, `ksp_store_lib` ou copier leurs modèles.
## 8. Validation dependency/ownership
État de l'archive stable inspecté :
```text
crates/ksp-interface-lib/Cargo.toml
runtime dependency = ksp-core-lib seulement
```
Cible `0.3.5` :
```text
ksp-interface-lib -> ksp-core-lib
ksp-interface-lib -X-> ksp-onchain-transport-lib
ksp-interface-lib -X-> ksp-store-api
ksp-interface-lib -X-> ksp-store-lib
ksp-interface-lib -X-> ksp-program-api
ksp-interface-lib -X-> serde/tracing/tokio/channel runtime additions
```
Le gate opérateur de `pre.001` confirme `cargo check --workspace`, Clippy et les tests `ksp-interface-lib`. Les graphes `cargo tree` restent réservés au gate technique final de la release.
## 9. Consumer concret retenu
Consumer proche documenté par l'architecture : futur worker RAW live / composition d'acquisition.
Usage possible sans dépendance protocol-specific :
```text
Solana standard WS -----------+
+-> converter composition -> SlotLifecycleEvent -> réaction lifecycle du consumer
Yellowstone gRPC -------------+
```
`0.3.6` backfill n'est pas forcé d'utiliser ce type ; il reste une composition historique Transport + Store.
## 10. Threat gates
| Gate | Attendu `0.3.5` | Statut courant |
| ------------------------------------ | ------------------------------------------------- | ------------------ |
| mega enum | absent | PLAN PASS |
| protocol/provider enum leakage | absent | PLAN PASS |
| timestamp semantics conflated | absent | PLAN PASS |
| root/finalized label confusion | évitée par le stage KSP `Rooted` | PLAN PASS |
| optimistic/confirmed label confusion | évitée par le stage KSP `OptimisticallyConfirmed` | PLAN PASS |
| dead diagnostic in shared API | absent | PLAN PASS |
| RAW Store duplication | absent | PLAN PASS |
| event persistence | absent | PLAN PASS |
| event bus/runtime | absent | PLAN PASS |
| network id duplicated from Store | absent | PLAN PASS |
| cross-network use | composition doit conserver son scope réseau | À CANARIER |
| public enum evolution | `SlotLifecycleStage` doit être non-exhaustive | IMPLÉMENTÉ pre.002 |
| Core-only graph | aucune dependency nouvelle | PASS opérateur |
| external consumer | type consommable depuis crate externe | CANARI pre.002 |
## 11. Fichiers de `pre.001`
Ajoutés :
```text
docs/plans/026-V0_3_5_INTERFACE_ACQUISITION_EVENTS_PLAN.md
docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md
deltas/0.3.5/pre.001.md
```
Modifié :
```text
Cargo.toml
```
Aucun autre fichier ne doit changer dans cet overlay.
## 12. Validations statiques de l'overlay initial
L'overlay `pre.001` avait exécuté les audits disponibles dans l'environnement d'assemblage :
```text
python3 scripts/audit_rust_workspace_rules.py
-> General Rust rule audit: clean
-> Rust export completeness audit: 0 candidate(s)
-> KSP workspace Rust rule audit: clean
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.3.5
-> Markdown table audit: clean (251 table(s), 137 file(s))
```
## 13. Gate opérateur `pre.001` reçu avant `fix.001`
L'opérateur a rejoué depuis un `cargo clean` :
```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
```
Résultat : **PASS intégral**.
Preuves synthétiques :
```text
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
Markdown table audit: clean (251 table(s), 137 file(s))
cargo check --workspace -> PASS
cargo clippy --workspace --all-targets -> PASS
ksp-interface-lib unit tests -> 10 passed
ksp-interface-lib dependency_boundary -> 4 passed
ksp-interface-lib external_consumer -> 1 passed
ksp-interface-lib public_api -> 5 passed
ksp-interface-lib release_completeness -> 3 passed
doc-tests -> 0 failed
```
`pre.001-fix.001` est documentation-only ; il ne modifie ni Cargo ni Rust. L'opérateur doit néanmoins rejouer les deux audits Markdown/Rust après application du fix pour confirmer l'overlay final.
## 14. Exit gate `pre.001-fix.001`
Le gate corrigé impose désormais :
```text
workspace.package.version = 0.3.5-pre.1 inchangée
SlotLifecycle admis avec 7 stages
stages = Processed / FirstShredReceived / Completed / CreatedBank / Dead / OptimisticallyConfirmed / Rooted
Frozen reste hors intersection
TransactionExecutionEvent = candidat actif mais non encore admis
TransactionLogEvent = idée différée, distincte de l'exécution
getSignatureStatuses/signatureSubscribe = snapshot/commitment Transport, non fusionnés avec execution
Helius transactionSubscribe et Yellowstone Entry restent des DTOs Transport
un futur fait provider-neutral produit par une extension provider peut appartenir à Interface après gate
aucun fichier Rust/Transport/Store/Cargo n'est modifié par le fix
```
## 15. Gate opérateur post-`pre.001-fix.001`
Avant l'ouverture de `pre.002`, l'opérateur a rejoué les audits demandés sur l'état corrigé :
```text
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
```
Résultat fourni : **PASS**.
```text
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
Markdown table audit: clean (240 table(s), 138 file(s))
```
Ce résultat constitue le gate d'entrée immédiat de `0.3.5-pre.002`.
## 16. Implémentation `0.3.5-pre.002`
`pre.002` ajoute exactement une famille passive provider-neutral à `ksp-interface-lib` :
```text
SlotLifecycleStage
Processed
FirstShredReceived
Completed
CreatedBank
Dead
OptimisticallyConfirmed
Rooted
SlotLifecycleEvent
new(slot, stage)
slot()
stage()
```
Propriétés de représentation matérialisées :
```text
SlotLifecycleStage -> #[non_exhaustive] + Clone + Copy + Debug + Eq + PartialEq
SlotLifecycleEvent -> champs privés + Clone + Copy + Debug + Eq + PartialEq
slot -> u64 conservé exactement, y compris u64::MAX
validation -> aucune admission faillible
error code -> aucun nouvel error code
serde/codec -> absent
runtime/logging -> absent
Transport/Store -> aucune dépendance ni conversion
```
Le graphe Cargo reste inchangé pour `ksp-interface-lib` : son manifest n'est pas modifié et sa seule dépendance normale demeure `ksp-core-lib`.
Canaris ajoutés/ajustés :
```text
unit_tests/slot_lifecycle.rs
-> 7 stages distincts
-> traits Copy/Eq attendus
-> domaine u64 complet
-> Debug borné et provider-neutral
tests/slot_lifecycle_public_api.rs
-> construction et getters depuis crate root
-> match downstream avec wildcard imposé par #[non_exhaustive]
tests/release_completeness.rs
-> inventaire crate-root mis à jour à 8 reexports
-> inventaire de modules mis à jour avec slot_lifecycle uniquement
-> absence de serde/tracing/Transport/Store dans la racine Interface
```
`TransactionExecutionEvent` reste **non implémenté** et réservé au gate `pre.003`.
## 17. Fichiers de `pre.002`
Modifiés :
```text
Cargo.toml
crates/ksp-interface-lib/src/lib.rs
crates/ksp-interface-lib/tests/release_completeness.rs
docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md
```
Ajoutés :
```text
crates/ksp-interface-lib/src/slot_lifecycle.rs
crates/ksp-interface-lib/unit_tests/slot_lifecycle.rs
crates/ksp-interface-lib/tests/slot_lifecycle_public_api.rs
deltas/0.3.5/pre.002.md
```
Explicitement inchangés :
```text
crates/ksp-interface-lib/Cargo.toml
crates/ksp-onchain-transport-lib/**
crates/ksp-store-api/**
crates/ksp-store-lib/**
crates/ksp-store-postgres-lib/**
README.md
ROADMAP.md
CHANGELOG.md
```
## 18. Gate opérateur demandé pour `pre.002`
Après application de l'overlay :
```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 -p ksp-program-api
```
Les validations Cargo de `pre.002` restent **à exécuter par l'opérateur** ; elles ne sont pas déclarées réussies par l'environnement d'assemblage.
## 19. Gate opérateur `pre.002` et correction `pre.002-fix.001`
Le gate opérateur de `pre.002` confirme que le contrat fonctionnel `SlotLifecycleEvent` compile et que ses tests ciblés passent, mais la tranche n'est pas admissible en l'état : l'audit Rust détecte quinze violations `RUST-FMT-101` dans les nouveaux tests et Clippy rejette deux usages de `?` ainsi que des closures à retour implicite. Les deux nouveaux tests d'intégration exposent également un warning `missing_docs` au niveau crate.
`pre.002-fix.001` corrige uniquement ces défauts de conformité : suppression des lignes vides à l'intérieur des fonctions, propagation d'erreur explicite par `match`, `return` explicite dans les closures concernées et documentation crate-level des tests d'intégration. Aucun contrat public `SlotLifecycleEvent` / `SlotLifecycleStage` n'est modifié.
Comme le fix modifie des sources Rust, la version workspace évolue conformément à la politique KSP :
```text
0.3.5-pre.2.fix.1
```
Le nom documentaire de la correction reste `0.3.5-pre.002-fix.001`.
## 20. Gate opérateur `pre.002-fix.001`
Le gate opérateur du `2026-08-31` confirme :
```text
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
cargo check --workspace: PASS
cargo clippy --workspace --all-targets: PASS
cargo test -p ksp-interface-lib: PASS
cargo test -p ksp-program-api: PASS
```
Le seul défaut restant est documentaire : deux lignes vides consécutives dans ce fichier de validation. L'opérateur demande explicitement de ne pas créer un fix dédié pour ce seul défaut ; `pre.003` le corrige avec ses propres modifications.
## 21. Gate `TransactionExecutionEvent` — `pre.003`
### 21.1 Représentation de signature
Verdict : `PASS`.
La primitive admise est :
```text
TransactionSignature([u8; 64])
```
Contraintes :
- construction depuis 64 octets déjà décodés uniquement ;
- aucun base58/serde/codec dans Interface ;
- `Debug` masque systématiquement les bytes ;
- aucune dependency Solana supplémentaire ;
- aucune capability de signature Wallet ; il s'agit uniquement d'une identité transactionnelle passive observée.
`ksp-store-api::RawTransactionSignature` reste Store-owned. Aucun alias, reexport ou conversion implicite cross-domain n'est introduit. Une future composition peut effectuer explicitement la conversion via les 64 octets lorsque nécessaire.
### 21.2 Convergence producer
| Producer | Slot | Signature | Outcome | Metadata volontairement exclue | Verdict |
| -------------------------------------- | ----------------------- | ----------------------------------- | ----------------------------------- | -------------------------------------------------------------- | ------- |
| Solana WS `logsSubscribe` | `context.slot` | base58, décodée par la composition | `err == null` / `err != null` | logs, commitment, subscription | PASS |
| Yellowstone `TransactionStatus` | `slot` | 64 bytes | `err None` / `err Some` | filters, created_at, is_vote, index, opaque error bytes | PASS |
| Helius `transactionSubscribe` | `slot` si forme typée | signature si forme typée | seulement si erreur déterminable | detail mode, transactionIndex, memo, blockTime, confirmation | PASS* |
`PASS*` Helius signifie **projection conservative** : aucune `TransactionExecutionEvent` n'est fabriquée pour une forme `none`, inconnue ou dont l'erreur d'exécution est absente/indéterminable. Le DTO provider reste Transport-owned.
### 21.3 Outcome commun
Le contrat n'encode pas les erreurs provider :
```text
Solana `err: null` / Yellowstone `err: None` / Helius `err: null` explicite -> Succeeded
Solana/Yellowstone/Helius erreur explicite -> Failed
Helius `err` omis ou forme inconnue -> aucune projection
```
Cette normalisation décrit uniquement le résultat d'exécution observé. Elle ne prétend pas encoder un commitment, une finalité, une cause d'erreur, un index de transaction ou une garantie de replay.
### 21.4 Consumer concret
Verdict : `PASS`.
Le futur worker RAW live décrit dans `docs/architecture/009-ACQUISITION_WORKERS_AND_JOBS.md` peut consommer le fait compact pour déclencher une hydratation/acquisition par signature sans dépendre du DTO source. Le premier backfill historique `0.3.6` ne dépend pas de cet event.
### 21.5 Anti-duplication Store
Verdict : `PASS`.
`TransactionExecutionEvent` ne contient ni payload RAW, format, content hash, block time, provenance, observation key, retention state ni network id durable. Il ne peut donc pas remplacer `RawTransaction`, `RawTransactionReference` ou `RawTransactionObservation`.
### 21.6 Décision finale
```text
TransactionSignature
TransactionExecutionOutcome
TransactionExecutionEvent
-> ADMIS Interface
TransactionLogEvent
-> idée différée
TransactionCommitmentEvent
-> non admis
DTOs logsSubscribe / Yellowstone TransactionStatus / Helius transactionSubscribe
-> Transport-owned
```
## 22. Implémentation `0.3.5-pre.003`
Surface ajoutée au crate-root :
```text
TransactionSignature
TransactionExecutionOutcome
TransactionExecutionEvent
```
API :
```text
TransactionSignature::new([u8; 64])
TransactionSignature::as_bytes()
TransactionExecutionOutcome::{Succeeded, Failed}
TransactionExecutionEvent::new(slot, signature, outcome)
TransactionExecutionEvent::slot()
TransactionExecutionEvent::signature()
TransactionExecutionEvent::outcome()
```
`TransactionExecutionOutcome` est `#[non_exhaustive]`. Les deux `Debug` contenant une signature sont bornés et n'affichent aucun byte de signature.
Le graphe normal visé reste :
```text
ksp-interface-lib
└── ksp-core-lib
```
Aucun converter Transport, aucune dependency Store, aucun `serde`, logging, runtime, channel ou codec n'est ajouté.
## 23. Fichiers de `pre.003`
Modifiés :
```text
Cargo.toml
crates/ksp-interface-lib/src/lib.rs
crates/ksp-interface-lib/tests/release_completeness.rs
docs/plans/026-V0_3_5_INTERFACE_ACQUISITION_EVENTS_PLAN.md
docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md
```
Ajoutés :
```text
crates/ksp-interface-lib/src/transaction_execution.rs
crates/ksp-interface-lib/unit_tests/transaction_execution.rs
crates/ksp-interface-lib/tests/transaction_execution_public_api.rs
deltas/0.3.5/pre.003.md
```
Explicitement inchangés :
```text
crates/ksp-interface-lib/Cargo.toml
crates/ksp-onchain-transport-lib/**
crates/ksp-store-api/**
crates/ksp-store-lib/**
crates/ksp-store-postgres-lib/**
README.md
ROADMAP.md
CHANGELOG.md
```
## 24. Gate opérateur demandé pour `pre.003`
```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 -p ksp-program-api
```
Les gates Cargo de `pre.003` restent à exécuter par l'opérateur dans le repository réel.
## 25. Gate opérateur `pre.003`
Le gate opérateur du `2026-08-31` est intégralement propre :
```text
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
Markdown table audit: clean (240 table(s), 141 file(s))
cargo check --workspace: PASS
cargo clippy --workspace --all-targets: PASS
cargo test -p ksp-interface-lib: PASS
cargo test -p ksp-program-api: PASS
```
`ksp-interface-lib` exécute alors seize tests unitaires, dont les trois canaris `transaction_execution`, et les deux tests publics `transaction_execution_public_api` passent. `ksp-program-api` reste vert avec ses frontières historiques inchangées.
## 26. Complétude API et canaris externes — `pre.004`
`pre.004` n'ajoute aucun contrat de production. La version workspace évolue vers `0.3.5-pre.4` parce que la tranche modifie des sources Rust de test/canari.
Les canaris figent désormais explicitement :
```text
2 familles passives d'acquisition exactement
5 modules privés de production exactement
11 reexports crate-root exactement
SlotLifecycleStage #[non_exhaustive]
TransactionExecutionOutcome #[non_exhaustive]
consumer externe crate-root pour SlotLifecycleEvent
consumer externe crate-root pour TransactionExecutionEvent
aucune dependency/path Transport, Store, runtime, serde ou logging dans les sources acquisition
```
Le consumer externe ne dépend d'aucun module privé et consomme les deux contrats uniquement via `ksp_interface_lib::*`. Le firewall de dépendances balaie maintenant également `slot_lifecycle.rs` et `transaction_execution.rs`.
Restent hors de la surface :
```text
converters Transport -> Interface
DTOs provider
Store RAW
serde/codecs
logging/tracing
runtime/channels
TransactionLogEvent
TransactionCommitmentEvent
```
Aucun changement n'est apporté à `crates/ksp-interface-lib/Cargo.toml`, aux sources de production Interface, à Transport, Store, Program API, README, ROADMAP ou CHANGELOG.
## 27. Gate opérateur demandé pour `pre.004`
```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 -p ksp-program-api
```
Les gates Cargo de `pre.004` restent à exécuter par l'opérateur dans le repository réel.
## 28. Gate opérateur `pre.004`
Le gate opérateur du `2026-08-31` est intégralement propre :
```text
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
Markdown table audit: clean (240 table(s), 142 file(s))
cargo check --workspace: PASS
cargo clippy --workspace --all-targets: PASS
cargo test -p ksp-interface-lib: PASS
cargo test -p ksp-program-api: PASS
```
`ksp-interface-lib` exécute alors seize tests unitaires, cinq canaris de dependency boundary, deux canaris external consumer, quatre canaris de release completeness et les quatre tests publics des deux familles acquisition. `ksp-program-api` reste vert sans changement de contrat.
## 29. Hardening final Interface — `pre.005`
`pre.005` n'ajoute aucun type ni champ de production. La version workspace évolue vers `0.3.5-pre.5` parce que la tranche ajoute/modifie des sources Rust de test/canari.
Le hardening verrouille explicitement :
```text
TransactionSignature = exactement 64 octets déjà décodés
Debug TransactionSignature = redacted
Debug TransactionExecutionEvent = aucune signature brute
SlotLifecycleEvent = exactement slot + stage
TransactionExecutionEvent = exactement slot + signature + outcome
aucun Vec/String/Box/payload/timestamp/provider/endpoint/filter/network dans les events acquisition
aucun second RAW exporté depuis Interface
aucune famille deferred promue silencieusement
manifest Interface = Core-only, sans features/dev/build dependencies
ProgramInstruction foundation = bornes historiques inchangées
```
Le canari adversarial injecte une signature contenant un marqueur hostile lisible et vérifie que les octets exacts restent accessibles par `as_bytes()` tout en restant absents des surfaces `Debug`. L'event d'exécution conserve `u64::MAX` sans narrowing.
Le contrôle anti-persistence interdit explicitement dans la façade Interface les familles `RawTransaction`, `RawAccount`, payload/cursor/retention/observation RAW. Interface conserve uniquement des faits event-only compacts.
Le graphe reste :
```text
ksp-interface-lib
└── ksp-core-lib
```
Le manifeste ne possède toujours ni `[features]`, ni `[dev-dependencies]`, ni `[build-dependencies]`. Aucun `serde`, codec, logging, runtime, Transport ou Store n'est ajouté.
## 30. Inventaire des candidats reportés après hardening
La clôture fonctionnelle de `pre.005` conserve exactement deux familles acquisition admises. Les autres candidats restent :
```text
TransactionLogEvent -> idée différée ; aucun type 0.3.5
TransactionCommitmentEvent -> reporté ; signatureSubscribe/getSignatureStatuses ne sont pas fusionnés
VoteEvent -> reporté ; vote gossip != transaction is_vote
LedgerEntryEvent -> reporté ; Yellowstone Entry reste mono-producer/protocol-specific
Account/Program event RAW -> rejet Interface ; Store/Transport owner
Transaction complète -> rejet Interface ; RawTransaction/Transport owner
Block event -> rejet Interface ; Transport/acquisition container
Persisted-data available -> rejet Interface ; Store API notification owner
```
`TransactionExecutionEvent` n'est plus un candidat reporté : il est admis depuis `pre.003`. `TransactionLogEvent` reste l'idée différée à transférer vers la surface TODO/IDEAS durable pendant la réconciliation documentaire `pre.007` si elle demeure hors scope.
## 31. Fichiers de `pre.005`
Modifiés :
```text
Cargo.toml
crates/ksp-interface-lib/tests/dependency_boundary.rs
docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md
```
Ajoutés :
```text
crates/ksp-interface-lib/tests/security_hardening.rs
deltas/0.3.5/pre.005.md
```
Explicitement inchangés :
```text
crates/ksp-interface-lib/Cargo.toml
crates/ksp-interface-lib/src/**
crates/ksp-interface-lib/unit_tests/**
crates/ksp-onchain-transport-lib/**
crates/ksp-store-api/**
crates/ksp-store-lib/**
crates/ksp-store-postgres-lib/**
crates/ksp-program-api/**
README.md
ROADMAP.md
CHANGELOG.md
```
## 32. Gate opérateur demandé pour `pre.005`
```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 -p ksp-program-api
```
Les gates Cargo de `pre.005` restent à exécuter par l'opérateur dans le repository réel.
## 33. Gate opérateur `pre.005` et correction `pre.005-fix.001`
Le gate opérateur du `2026-08-31` confirme que les audits Rust/Markdown et `cargo check --workspace` sont propres, mais révèle un défaut dans le nouveau canari de manifeste :
```text
v0_3_5_pre_005_manifest_keeps_exact_core_only_graph_without_features_or_hidden_dependency_sections: FAILED
left: 2
right: 1
```
Le manifeste Interface reste pourtant strictement Core-only. Le canari comptait naïvement les occurrences textuelles de `ksp-core-lib` dans :
```text
ksp-core-lib = { path = "../ksp-core-lib" }
```
Le nom apparaît donc deux fois sur une seule déclaration de dépendance : une fois comme clé et une fois dans le chemin. `pre.005-fix.001` corrige uniquement ce faux négatif en réutilisant le parseur de clés de dépendances déjà présent dans `dependency_boundary.rs`. Le canari vérifie désormais que la section `[dependencies]` existe exactement une fois et que son inventaire de clés vaut exactement `ksp-core-lib`, tout en conservant les interdictions `[features]`, `[dev-dependencies]` et `[build-dependencies]`.
Le fix modifie une source Rust de test ; la version workspace évolue donc vers :
```text
0.3.5-pre.5.fix.1
```
Aucun manifeste de crate, source de production, contrat public ou dependency graph n'est modifié.
## 34. Gate opérateur `pre.005-fix.001`
Le gate opérateur du `2026-08-31` est intégralement propre après correction du faux négatif du canari de manifeste :
```text
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
Markdown table audit: clean (240 table(s), 144 file(s))
cargo check --workspace: PASS
cargo clippy --workspace --all-targets: PASS
cargo test -p ksp-interface-lib: PASS
cargo test -p ksp-program-api: PASS
cargo test --workspace: PASS
```
Les tests live/diagnostiques restent uniquement `ignored` et n'introduisent aucun échec. Le canari corrigé `v0_3_5_pre_005_manifest_keeps_exact_core_only_graph_without_features_or_hidden_dependency_sections` passe. La surface Interface reste limitée aux deux familles acquisition admises et au graphe Core-only.
## 35. Gate technique final — `pre.006`
`pre.006` n'ajoute ni type, ni test, ni dépendance, ni source de production. La tranche matérialise uniquement le gate technique final prévu par le prompt de démarrage et fait évoluer la version workspace vers `0.3.5-pre.6`.
Le gate final doit confirmer l'état suivant :
```text
ksp-interface-lib -> ksp-core-lib uniquement
aucune feature Interface
aucune dev/build dependency Interface
aucun duplicate dependency introduit par 0.3.5
2 familles acquisition exactement
SlotLifecycleEvent
TransactionExecutionEvent
aucun serde/codec/logging/runtime/Transport/Store dans Interface
```
Les commandes opérateur de clôture technique sont :
```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 smoke réseau n'est requis : `0.3.5` ne modifie aucun moteur Transport et les contrats Interface sont passifs. Les résultats `cargo tree` sont capturés dans le résultat opérateur de clôture ci-dessous.
## 36. Fichiers de `pre.006`
Modifiés :
```text
Cargo.toml
docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md
```
Ajouté :
```text
deltas/0.3.5/pre.006.md
```
Explicitement inchangés :
```text
crates/ksp-interface-lib/**
crates/ksp-program-api/**
crates/ksp-onchain-transport-lib/**
crates/ksp-store-api/**
crates/ksp-store-lib/**
crates/ksp-store-postgres-lib/**
README.md
ROADMAP.md
CHANGELOG.md
```
## 37. Résultat du gate technique final `pre.006`
Le gate opérateur du `2026-08-31` est propre après correction locale des numéros d'en-tête réellement modifiés dans `Cargo.toml` et ce document de validation :
```text
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
Markdown table audit: clean (240 table(s), 145 file(s))
cargo check --workspace: PASS
cargo clippy --workspace --all-targets: PASS
cargo test -p ksp-interface-lib: PASS
cargo test --workspace: PASS
```
Les tests live/réseau et probes diagnostiques restent explicitement `ignored` conformément à leur contrat ; aucun test non ignoré n'échoue.
Les trois preuves Cargo finales confirment :
```text
cargo tree -p ksp-interface-lib --edges normal
ksp-interface-lib -> ksp-core-lib -> solana-pubkey -> solana-address
cargo tree -p ksp-interface-lib -e features
aucune feature propre Interface ; seule la feature default de ksp-core-lib est activée
cargo tree --duplicates
doublons du workspace global présents, aucun nouveau doublon imputable à ksp-interface-lib/0.3.5
```
Le manifeste Interface reste sans `[features]`, `[dev-dependencies]` ni `[build-dependencies]`. La surface fonctionnelle finale demeure exactement :
```text
ProgramAccountMeta / ProgramInstruction
SlotLifecycleEvent / SlotLifecycleStage
TransactionSignature / TransactionExecutionEvent / TransactionExecutionOutcome
```
Aucun serde, codec, logging, runtime, Transport ou Store n'est entré dans le graphe Interface.
## 38. Réconciliation documentaire finale — `pre.007`
`pre.007` ne modifie aucun code Rust ni manifeste de crate. La version workspace devient :
```text
0.3.5-pre.7
```
La réconciliation ferme les incohérences documentaires restantes :
- `crates/ksp-interface-lib/README.md` décrit désormais les contrats Program et les deux familles d'événements passifs sans présenter Interface comme un runtime ;
- `crates/ksp-interface-lib/USAGE.md` devient version-neutral et documente la construction/consommation des événements via le crate-root ;
- les documents d'architecture concernés distinguent explicitement contrat passif Interface, DTO riche Transport et modèle replayable Store ;
- le futur RAW worker peut dépendre d'Interface pour un fait passif partagé, mais son backlog/recovery reste Store-owned ;
- `TransactionLogEvent` reste hors scope et est transféré vers `docs/IDEAS.md` avec un nouveau gate consumer/bornes obligatoire ;
- `CHANGELOG.md`, `ROADMAP.md` et le prompt `0.3.6` restent réservés à la tranche de publication suivante.
Fichiers modifiés par la tranche :
```text
Cargo.toml
crates/ksp-interface-lib/README.md
crates/ksp-interface-lib/USAGE.md
docs/IDEAS.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/009-ACQUISITION_WORKERS_AND_JOBS.md
docs/plans/026-V0_3_5_INTERFACE_ACQUISITION_EVENTS_PLAN.md
docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md
deltas/0.3.5/pre.007.md
```
Aucun autre fichier ne doit être modifié.

File diff suppressed because it is too large Load Diff