diff --git a/Cargo.toml b/Cargo.toml index 508f4ec..8ac0fbb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ # file: Cargo.toml -# version: 383 +# version: 384 [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.1" +version = "0.3.5-pre.2" 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 f43abfc..0b0183b 100644 --- a/crates/ksp-interface-lib/src/lib.rs +++ b/crates/ksp-interface-lib/src/lib.rs @@ -1,20 +1,21 @@ // file: crates/ksp-interface-lib/src/lib.rs -// version: 3 +// version: 4 #![warn(missing_docs)] #![deny(unreachable_pub)] #![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 -//! `ksp-core-lib` and exposes only bounded, passive Program-facing structures. -//! Runtime, transport, persistence and Program behavior remain outside this -//! crate. +//! The crate reuses canonical Solana primitives owned by `ksp-core-lib` and +//! exposes only bounded, passive Program-facing and provider-neutral +//! acquisition structures. Runtime, transport, persistence and Program +//! behavior remain outside this crate. mod error; mod program_account_meta; mod program_instruction; +mod slot_lifecycle; /// Error code used when an Interface-owned Program instruction admission limit is exceeded. pub use self::error::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED; @@ -26,5 +27,9 @@ pub use self::program_account_meta::ProgramAccountMeta; pub use self::program_instruction::MAX_PROGRAM_INSTRUCTION_DATA_LEN; /// Passive, bounded Program instruction wire contract. 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; /// Canonical Solana account address primitive owned by `ksp-core-lib`. pub use ksp_core_lib::Pubkey; diff --git a/crates/ksp-interface-lib/src/slot_lifecycle.rs b/crates/ksp-interface-lib/src/slot_lifecycle.rs new file mode 100644 index 0000000..3f6e918 --- /dev/null +++ b/crates/ksp-interface-lib/src/slot_lifecycle.rs @@ -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; diff --git a/crates/ksp-interface-lib/tests/release_completeness.rs b/crates/ksp-interface-lib/tests/release_completeness.rs index 8b9e6ec..774a984 100644 --- a/crates/ksp-interface-lib/tests/release_completeness.rs +++ b/crates/ksp-interface-lib/tests/release_completeness.rs @@ -1,75 +1,56 @@ // file: crates/ksp-interface-lib/tests/release_completeness.rs -// version: 1 +// version: 2 -//! Release-level completeness canaries for the `0.2.13` Interface foundation. - -#[test] -fn pre_005_exact_crate_root_export_inventory_is_stable() { - let crate_root = include_str!("../src/lib.rs"); - 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; +fn crate_root_source() -> std::result::Result { + let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src/lib.rs"); + return std::fs::read_to_string(path); } #[test] -fn pre_005_production_module_inventory_contains_no_second_wire_domain() -> std::io::Result<()> { - 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() { +fn v0_3_5_pre_002_foundation_keeps_one_error_code_and_adds_one_passive_acquisition_family() { 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::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([0xB1_u8; 32]); let account = ksp_interface_lib::ProgramAccountMeta::readonly(ksp_interface_lib::Pubkey::new_from_array([0xB2_u8; 32]), false); - let instruction = ksp_interface_lib::ProgramInstruction::try_new(program_id, std::vec![account], std::vec![0xB3_u8]); - assert!(instruction.is_ok()); - return; + let instruction_result = ksp_interface_lib::ProgramInstruction::try_new(program_id, std::vec![account], std::vec![0xB3_u8]); + assert!(instruction_result.is_ok()); + + let lifecycle = ksp_interface_lib::SlotLifecycleEvent::new(u64::MAX, ksp_interface_lib::SlotLifecycleStage::Rooted); + assert_eq!(lifecycle.slot(), u64::MAX); + assert_eq!(lifecycle.stage(), ksp_interface_lib::SlotLifecycleStage::Rooted); +} + +#[test] +fn v0_3_5_pre_002_exact_crate_root_export_inventory_includes_slot_lifecycle() -> std::result::Result<(), std::io::Error> { + let source = crate_root_source()?; + let public_use_count = source.lines().filter(|line| line.starts_with("pub use ")).count(); + + assert_eq!(public_use_count, 8); + 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 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> { + let source = crate_root_source()?; + let modules = source.lines().filter_map(|line| line.strip_prefix("mod ").and_then(|module| module.strip_suffix(';'))).collect::>(); + + assert_eq!(modules, std::vec!["error", "program_account_meta", "program_instruction", "slot_lifecycle"]); + assert!(!source.contains("serde")); + assert!(!source.contains("tracing")); + assert!(!source.contains("ksp_onchain_transport")); + assert!(!source.contains("ksp_store")); + + return Ok(()); } diff --git a/crates/ksp-interface-lib/tests/slot_lifecycle_public_api.rs b/crates/ksp-interface-lib/tests/slot_lifecycle_public_api.rs new file mode 100644 index 0000000..f29b3ba --- /dev/null +++ b/crates/ksp-interface-lib/tests/slot_lifecycle_public_api.rs @@ -0,0 +1,29 @@ +// file: crates/ksp-interface-lib/tests/slot_lifecycle_public_api.rs +// version: 1 + +#[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"); +} diff --git a/crates/ksp-interface-lib/unit_tests/slot_lifecycle.rs b/crates/ksp-interface-lib/unit_tests/slot_lifecycle.rs new file mode 100644 index 0000000..91b3ea7 --- /dev/null +++ b/crates/ksp-interface-lib/unit_tests/slot_lifecycle.rs @@ -0,0 +1,52 @@ +// file: crates/ksp-interface-lib/unit_tests/slot_lifecycle.rs +// version: 1 + +#[test] +fn slot_lifecycle_stages_are_distinct_copy_and_complete_for_the_admitted_family() { + fn assert_copy() {} + + assert_copy::(); + + 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() {} + + assert_copy::(); + + 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")); +} diff --git a/deltas/0.3.5/pre.002.md b/deltas/0.3.5/pre.002.md new file mode 100644 index 0000000..c225005 --- /dev/null +++ b/deltas/0.3.5/pre.002.md @@ -0,0 +1,180 @@ + + + +# 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 +``` 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 8770350..c47ce6d 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` et corrigé par `0.3.5-pre.001-fix.001`. Le fix enregistre les corrections sémantiques du gate ainsi que le gate Cargo réellement rejoué par l'opérateur sur `0.3.5-pre.1`. +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`. Base : @@ -17,7 +17,7 @@ workspace.package.version = 0.3.4 Version de travail : ```text -0.3.5-pre.1 +0.3.5-pre.2 ``` ## 2. Gate d'entrée `v0.3.4` @@ -161,22 +161,22 @@ Yellowstone gRPC -------------+ ## 10. Threat gates -| Gate | Attendu `0.3.5` | Statut `pre.001` | -| ------------------------------------ | ------------------------------------------------- | ---------------- | -| 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ÉMENTER | -| Core-only graph | aucune dependency nouvelle | PASS opérateur | -| external consumer | type consommable depuis crate externe | À IMPLÉMENTER | +| 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` @@ -260,3 +260,129 @@ 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. +