From 7d490eb5d8e66da208bf8e71c9e29f25603ec595 Mon Sep 17 00:00:00 2001 From: SinuS Von SifriduS Date: Mon, 31 Aug 2026 11:01:45 +0200 Subject: [PATCH] v0.3.5-pre.003 --- Cargo.toml | 4 +- crates/ksp-interface-lib/src/lib.rs | 9 +- .../src/transaction_execution.rs | 85 ++++++++ .../tests/release_completeness.rs | 20 +- .../tests/transaction_execution_public_api.rs | 28 +++ .../unit_tests/transaction_execution.rs | 28 +++ deltas/0.3.5/pre.003.md | 193 ++++++++++++++++++ ...0_3_5_INTERFACE_ACQUISITION_EVENTS_PLAN.md | 132 +++++++----- ...022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md | 187 ++++++++++++++++- 9 files changed, 620 insertions(+), 66 deletions(-) create mode 100644 crates/ksp-interface-lib/src/transaction_execution.rs create mode 100644 crates/ksp-interface-lib/tests/transaction_execution_public_api.rs create mode 100644 crates/ksp-interface-lib/unit_tests/transaction_execution.rs create mode 100644 deltas/0.3.5/pre.003.md diff --git a/Cargo.toml b/Cargo.toml index 1143bb3..b72702a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ # file: Cargo.toml -# version: 385 +# version: 386 [workspace] 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"] [workspace.package] -version = "0.3.5-pre.2.fix.1" +version = "0.3.5-pre.3" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" diff --git a/crates/ksp-interface-lib/src/lib.rs b/crates/ksp-interface-lib/src/lib.rs index 0b0183b..5c3fb50 100644 --- a/crates/ksp-interface-lib/src/lib.rs +++ b/crates/ksp-interface-lib/src/lib.rs @@ -1,5 +1,5 @@ // file: crates/ksp-interface-lib/src/lib.rs -// version: 4 +// version: 5 #![warn(missing_docs)] #![deny(unreachable_pub)] @@ -16,6 +16,7 @@ mod error; mod program_account_meta; mod program_instruction; mod slot_lifecycle; +mod transaction_execution; /// Error code used when an Interface-owned Program instruction admission limit is exceeded. pub use self::error::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED; @@ -31,5 +32,11 @@ pub use self::program_instruction::ProgramInstruction; 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`. pub use ksp_core_lib::Pubkey; diff --git a/crates/ksp-interface-lib/src/transaction_execution.rs b/crates/ksp-interface-lib/src/transaction_execution.rs new file mode 100644 index 0000000..0585ab9 --- /dev/null +++ b/crates/ksp-interface-lib/src/transaction_execution.rs @@ -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", &"") + .field("outcome", &self.outcome) + .finish(); + } +} + +#[cfg(test)] +#[path = "../unit_tests/transaction_execution.rs"] +mod tests; diff --git a/crates/ksp-interface-lib/tests/release_completeness.rs b/crates/ksp-interface-lib/tests/release_completeness.rs index 1bd4d33..5760af8 100644 --- a/crates/ksp-interface-lib/tests/release_completeness.rs +++ b/crates/ksp-interface-lib/tests/release_completeness.rs @@ -1,5 +1,5 @@ // file: crates/ksp-interface-lib/tests/release_completeness.rs -// version: 3 +// version: 4 //! Release-completeness canaries for the public `ksp-interface-lib` surface. @@ -9,7 +9,7 @@ fn crate_root_source() -> std::result::Result std::result::Result<(), std::io::Error> { +fn v0_3_5_pre_003_exact_crate_root_export_inventory_includes_both_acquisition_families() -> 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, 8); + 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;")); @@ -38,12 +43,15 @@ fn v0_3_5_pre_002_exact_crate_root_export_inventory_includes_slot_lifecycle() -> 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_002_production_module_inventory_adds_only_slot_lifecycle() -> std::result::Result<(), std::io::Error> { +fn v0_3_5_pre_003_production_module_inventory_adds_only_transaction_execution() -> std::result::Result<(), std::io::Error> { let source = match crate_root_source() { Ok(source) => source, Err(error) => return Err(error), @@ -52,7 +60,7 @@ fn v0_3_5_pre_002_production_module_inventory_adds_only_slot_lifecycle() -> std: .lines() .filter_map(|line| return line.strip_prefix("mod ").and_then(|module| return module.strip_suffix(';'))) .collect::>(); - assert_eq!(modules, std::vec!["error", "program_account_meta", "program_instruction", "slot_lifecycle"]); + 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")); diff --git a/crates/ksp-interface-lib/tests/transaction_execution_public_api.rs b/crates/ksp-interface-lib/tests/transaction_execution_public_api.rs new file mode 100644 index 0000000..53214ef --- /dev/null +++ b/crates/ksp-interface-lib/tests/transaction_execution_public_api.rs @@ -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"); +} diff --git a/crates/ksp-interface-lib/unit_tests/transaction_execution.rs b/crates/ksp-interface-lib/unit_tests/transaction_execution.rs new file mode 100644 index 0000000..64e1d14 --- /dev/null +++ b/crates/ksp-interface-lib/unit_tests/transaction_execution.rs @@ -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: \"\", outcome: Failed }"); +} diff --git a/deltas/0.3.5/pre.003.md b/deltas/0.3.5/pre.003.md new file mode 100644 index 0000000..19b3cae --- /dev/null +++ b/deltas/0.3.5/pre.003.md @@ -0,0 +1,193 @@ + + + +# 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. diff --git a/docs/plans/026-V0_3_5_INTERFACE_ACQUISITION_EVENTS_PLAN.md b/docs/plans/026-V0_3_5_INTERFACE_ACQUISITION_EVENTS_PLAN.md index 2ddb958..5c758c4 100644 --- a/docs/plans/026-V0_3_5_INTERFACE_ACQUISITION_EVENTS_PLAN.md +++ b/docs/plans/026-V0_3_5_INTERFACE_ACQUISITION_EVENTS_PLAN.md @@ -1,5 +1,5 @@ - + # Plan `0.3.5` — Interface passive acquisition events @@ -21,7 +21,7 @@ label = 0.3.5-pre.001 `0.3.5-pre.001` est un gate de lecture, inventaire, convergence sémantique, threat model, sizing et planification. Il ne modifie aucun contrat public Rust, aucun DTO Transport, aucun modèle Store, aucun runtime et aucune persistence. -Le résultat du gate reste volontairement étroit, mais `pre.001-fix.001` corrige deux conclusions trop conservatrices : le lifecycle de slot possède une intersection de **sept** stages communs après normalisation sémantique explicite, et `TransactionExecutionEvent` est rouvert comme candidat actif de `0.3.5` sans être encore admis. Les payloads riches, les logs complets, les snapshots/commitments de signature et les DTOs provider/protocol restent Transport ou Store selon leur ownership. +Le résultat du gate reste volontairement étroit. `pre.001-fix.001` a corrigé deux conclusions trop conservatrices : le lifecycle de slot possède une intersection de **sept** stages communs après normalisation sémantique explicite et `TransactionExecutionEvent` a été rouvert comme candidat actif. `pre.002` matérialise `SlotLifecycleEvent`; `pre.003` ferme ensuite le gate transaction execution et admet une seconde famille passive compacte sans déplacer les DTOs Transport ni les modèles RAW Store. ## 2. Mission @@ -260,7 +260,7 @@ Une égalité de noms ne suffit pas. Une proximité de commitment ne suffit pas. | 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 | candidat `slot + signature + outcome` | signature primitive KSP à définir ; metadata/provider/detail modes ; delivery semantics | à confirmer par gate dédié avant création du type | futur worker RAW live | Interface si gate passe | CANDIDAT ACTIF | +| 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É | @@ -269,7 +269,7 @@ Une égalité de noms ne suffit pas. Une proximité de commitment ne suffit pas. | 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 contribuer au candidat TransactionExecutionEvent | filtres/options/provider behavior et DTO riche | DTO non ; projection sémantique possible via converter | futur worker RAW live | Transport DTO / Interface fact | PRODUCTEUR CANDIDAT | +| 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 @@ -290,39 +290,51 @@ Le timestamp n'entre pas dans le contrat commun : `slotsUpdatesSubscribe.timesta 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 — CANDIDAT ACTIF +### 7.2 Transaction execution — ADMIS `pre.003` -`pre.001-fix.001` sépare désormais l'**exécution observée d'une transaction** des snapshots/commitments de signature. Trois producers peuvent exposer le même fait minimal : +`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 + err + -> context slot + signature base58 + err Yellowstone TransactionStatus - -> slot + signature + err + metadata Yellowstone + -> slot + signature [u8; 64] + err + metadata Yellowstone Helius transactionSubscribe - -> slot + signature/transaction + résultat d'exécution selon le detail mode + -> slot + signature + résultat d'exécution lorsque le detail mode expose l'erreur ``` -Le contrat candidat est conceptuellement : +Le contrat admis est : ```text +TransactionSignature([u8; 64]) + +TransactionExecutionOutcome { + Succeeded, + Failed, +} + TransactionExecutionEvent { slot, signature, - outcome = Succeeded | Failed, + outcome, } ``` -Il n'est **pas encore admis** dans `pre.001-fix.001`. Un gate dédié doit encore démontrer : +Décisions du gate : -- une représentation KSP-owned de la signature compatible avec le dependency firewall actuel ; -- que le mapping Helius retenu reste valide pour les detail modes réellement supportés sans dépendre d'un payload complet ; -- que `err == null` / `err != null` suffit bien à définir le fait `Succeeded` / `Failed` sans copier l'erreur source ; -- que le consumer proche justifie cette projection sans concurrencer `RawTransaction`/`RawTransactionObservation`. +- `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. -Les DTOs `logsSubscribe`, Yellowstone `TransactionStatus` et Helius `transactionSubscribe` restent intégralement Transport-owned. Seule une éventuelle projection passive commune appartiendrait à Interface. +`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 @@ -455,36 +467,39 @@ subscriptions/fetch live Ce worker est le consumer proche qui peut recevoir des événements de progression/lifecycle de slot venant soit des WebSockets standard, soit de Yellowstone sans connaître les DTOs des deux protocoles lorsqu'il n'a besoin que de déclencher/ordonner une réaction de composition propre au lifecycle. -Cette API ne devient pas un prérequis de `0.3.6` : le premier backfill historique reste fondé sur Transport + Store et peut ne jamais consommer `SlotLifecycleEvent`. +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 avec `SlotLifecycleEvent` | Pourquoi il n'y a pas de seconde vérité | -| --------------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------- | -| `RawTransaction` | slot possible seulement | aucun payload/signature/format/hash ; l'event ne peut reconstruire une transaction | -| `RawTransactionObservation` | slot indirect possible | aucune provenance acquisition, observation key ou timestamp durable | -| `RawAccountState` | slot possible seulement | aucune pubkey/state hash/lamports/owner/data ; aucun état account | -| `RawAccountObservation` | slot indirect possible | aucune référence account/provenance Geyser | -| `RawAcquisitionProvenance` | aucun | pas d'origin/provider code/timestamps ; les metadata source restent Transport/composition | -| `RawTimestamp` | aucun | aucun timestamp dans l'event, précisément pour éviter la fusion de clocks différentes | -| `RawContentHash` | aucun | aucune identité de contenu durable | -| `RawObservationKey` | aucun | aucune identité d'observation persistante | -| notification after commit | aucun | SlotLifecycle précède/observe le réseau ; le wake-up Store référence une donnée déjà commitée | +| 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 doit conserver les canaris conceptuels suivants : +Le gate conserve les canaris conceptuels suivants : ```text SlotLifecycleEvent -X-> RawTransaction -SlotLifecycleEvent -X-> RawAccountState -SlotLifecycleEvent -X-> RawAcquisitionProvenance -SlotLifecycleEvent -X-> persisted-data notification - +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 ``` -Aucun champ du modèle admis ne doit pouvoir servir de substitut à une référence RAW durable. +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 @@ -505,7 +520,7 @@ ksp-program-api future acquisition composition / RAW live worker -> ksp-onchain-transport-lib - -> ksp-interface-lib # SlotLifecycleEvent si utile + -> ksp-interface-lib # SlotLifecycleEvent / TransactionExecutionEvent -> ksp-store-lib / ksp-store-api selon ownership ``` @@ -514,7 +529,7 @@ future acquisition composition / RAW live worker | 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 ; candidat TransactionExecution ; logs/commitment restent distincts | +| 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é | @@ -522,8 +537,8 @@ future acquisition composition / RAW live worker | 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é | -| unbounded payload | logs/JSON/bytes entrent dans Interface | aucun payload variable dans la famille admise | -| public enum breakage | ajout futur casse les matches externes | `SlotLifecycleStage` non-exhaustive | +| 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 | @@ -546,7 +561,23 @@ Tests d'intégration : - dependency boundary Core-only ; - exact production module/export inventory. -### 13.2 Convergence sans dépendance Transport +### 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. @@ -567,13 +598,13 @@ 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.3 Traçabilité des candidats différés +### 13.4 Traçabilité des candidats différés -`TransactionExecutionEvent` reste un candidat actif de cette release et est donc suivi dans le présent plan/validation plutôt que déplacé dans un TODO durable avant son gate. `TransactionLogEvent` est 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. +`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 la seule famille admise immédiatement ; `TransactionExecutionEvent` devient un second candidat actif qui doit passer un gate dédié avant toute implémentation. +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 @@ -592,13 +623,14 @@ La release reste dimensionnée pour une seule session. `SlotLifecycleEvent` est - tests unitaires et public API ciblés ; - préserver le graphe Core-only. -### `0.3.5-pre.003` — gate `TransactionExecutionEvent` +### `0.3.5-pre.003` — `TransactionExecutionEvent` -- auditer la représentation de signature compatible avec les règles de dépendances ; -- prouver les mappings exacts Solana `logsSubscribe` / Yellowstone `TransactionStatus` / Helius `transactionSubscribe` ; -- vérifier anti-duplication Store et consumer concret ; -- si et seulement si le gate est complet, implémenter la famille minimale `slot + signature + outcome` ; -- sinon enregistrer explicitement le rejet/report sans type partiel. +- 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 @@ -711,7 +743,7 @@ La release peut fermer si et seulement si : une famille minimale SlotLifecycle est publique et bornée intersection = Processed + FirstShredReceived + Completed + CreatedBank + Dead + OptimisticallyConfirmed + Rooted Frozen et toute variante future sans mapping audité ne sont pas normalisées artificiellement -TransactionExecutionEvent est soit admis après son gate dédié, soit explicitement reporté sans type partiel +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é diff --git a/docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md b/docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md index 97bd0d4..6a47774 100644 --- a/docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md +++ b/docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md @@ -1,11 +1,11 @@ - + # Validation `0.3.5` — Interface passive acquisition events ## 1. Portée -Ce document est ouvert par `0.3.5-pre.001`, corrigé par `0.3.5-pre.001-fix.001` puis enrichi par `0.3.5-pre.002`. `pre.002` matérialise uniquement la famille `SlotLifecycleEvent` admise par le gate corrigé, sans avancer `TransactionExecutionEvent`. +Ce document est ouvert par `0.3.5-pre.001`, corrigé par `0.3.5-pre.001-fix.001`, enrichi par `0.3.5-pre.002` puis par `0.3.5-pre.003`. `pre.002` matérialise `SlotLifecycleEvent`; `pre.003` ferme le gate `TransactionExecutionEvent` et admet la seconde famille passive minimale. Base : @@ -17,7 +17,7 @@ workspace.package.version = 0.3.4 Version de travail : ```text -0.3.5-pre.2 +0.3.5-pre.3 ``` ## 2. Gate d'entrée `v0.3.4` @@ -48,7 +48,7 @@ Cette preuve est l'entrée de `0.3.5`. Elle n'est pas réétiquetée comme valid | 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 | CANDIDAT ACTIF | Interface si gate passe | intersection candidate `slot + signature + outcome` sur Solana/Yellowstone/Helius | +| 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` | @@ -57,7 +57,7 @@ Cette preuve est l'entrée de `0.3.5`. Elle n'est pas réétiquetée comme valid | 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 CANDIDAT | Transport DTO / Interface fact | DTO provider-specific ; projection possible seulement vers un fait commun admis | +| Helius Enhanced transaction | PRODUCTEUR ADMIS | Transport DTO / Interface fact | DTO provider-specific ; projection conservative vers le fait commun admis | ## 5. Intersection lifecycle retenue @@ -386,8 +386,7 @@ 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. - -## 17. Gate opérateur `pre.002` et correction `pre.002-fix.001` +## 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. @@ -400,3 +399,177 @@ Comme le fix modifie des sources Rust, la version workspace évolue conformémen ``` 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. +