From 04234926ce647e6bc135d8a956559e83225dda5c Mon Sep 17 00:00:00 2001 From: SinuS Von SifriduS Date: Sun, 30 Aug 2026 23:15:48 +0200 Subject: [PATCH] v0.3.4-pre.008 --- Cargo.toml | 4 +- crates/ksp-store-lib/src/lib.rs | 9 +- crates/ksp-store-lib/src/store.rs | 152 +++++++++++- .../tests/dependency_boundary.rs | 28 +-- .../tests/hardening_completeness.rs | 23 +- crates/ksp-store-lib/tests/public_api.rs | 18 +- crates/ksp-store-postgres-lib/src/lib.rs | 8 +- crates/ksp-store-postgres-lib/src/runtime.rs | 61 ++++- .../tests/dependency_boundary.rs | 49 ++-- .../tests/hardening_completeness.rs | 26 +-- .../tests/public_api.rs | 18 +- deltas/0.3.4/pre.008.md | 219 ++++++++++++++++++ ...-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT_PLAN.md | 30 ++- .../021-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT.md | 55 ++++- 14 files changed, 590 insertions(+), 110 deletions(-) create mode 100644 deltas/0.3.4/pre.008.md diff --git a/Cargo.toml b/Cargo.toml index 36c26cd..f01242d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ # file: Cargo.toml -# version: 374 +# version: 375 [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.4-pre.7.fix.1" +version = "0.3.4-pre.8" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" diff --git a/crates/ksp-store-lib/src/lib.rs b/crates/ksp-store-lib/src/lib.rs index 502e0e9..7984c96 100644 --- a/crates/ksp-store-lib/src/lib.rs +++ b/crates/ksp-store-lib/src/lib.rs @@ -1,5 +1,5 @@ // file: crates/ksp-store-lib/src/lib.rs -// version: 8 +// version: 9 #![warn(missing_docs)] #![deny(unreachable_pub)] @@ -8,9 +8,10 @@ //! Common backend-neutral Store runtime facade for KSP. //! //! The runtime facade owns backend selection, lifecycle, safe diagnostics and -//! backend-neutral capability dispatch. `0.3.3-pre.008` completes the PostgreSQL -//! `RawTransaction` vertical slice by implementing the six transaction capabilities -//! on both the physical backend and this common facade without exposing physical types. +//! backend-neutral capability dispatch. `0.3.3-pre.008` completed the six PostgreSQL +//! `RawTransaction` capabilities. `0.3.4-pre.008` adds the four `RawAccount*` capabilities +//! on both the physical backend and this common facade, completing the RAW inventory at +//! ten capabilities without exposing physical types. //! //! The default `postgres` feature compiles the official PostgreSQL backend as //! an optional implementation dependency. No backend implementation type is diff --git a/crates/ksp-store-lib/src/store.rs b/crates/ksp-store-lib/src/store.rs index efde194..2d519c4 100644 --- a/crates/ksp-store-lib/src/store.rs +++ b/crates/ksp-store-lib/src/store.rs @@ -1,5 +1,5 @@ // file: crates/ksp-store-lib/src/store.rs -// version: 6 +// version: 7 /// Opaque common Store runtime facade. /// @@ -116,6 +116,156 @@ impl std::fmt::Debug for Store { } } +impl ksp_store_api::RawAccountObservationRead for Store { + fn get_raw_account_observation<'a>( + &'a self, + observation_key: &'a ksp_store_api::RawObservationKey, + ) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result>> { + return std::boxed::Box::pin(async move { + #[cfg(feature = "postgres")] + { + return match &self.runtime { + StoreRuntime::Postgres(backend) => { + let result = backend.get_raw_account_observation(observation_key).await; + result.map_err(|error| return map_postgres_error(error, self.backend_kind, self.network.as_str())) + }, + }; + } + #[cfg(not(feature = "postgres"))] + { + let _ = observation_key; + return std::result::Result::Err(unavailable_runtime_error(self.backend_kind)); + } + }); + } +} + +impl ksp_store_api::RawAccountObservationWrite for Store { + fn record_raw_account_observation<'a>( + &'a self, + observation: ksp_store_api::RawAccountObservation, + ) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result> { + let network_check = validate_operation_network(&self.network, observation.account().network(), self.backend_kind); + if let std::result::Result::Err(error) = network_check { + return std::boxed::Box::pin(async move { + return std::result::Result::Err(error); + }); + } + return std::boxed::Box::pin(async move { + #[cfg(feature = "postgres")] + { + return match &self.runtime { + StoreRuntime::Postgres(backend) => { + let result = backend.record_raw_account_observation(observation).await; + result.map_err(|error| return map_postgres_error(error, self.backend_kind, self.network.as_str())) + }, + }; + } + #[cfg(not(feature = "postgres"))] + { + let _ = observation; + return std::result::Result::Err(unavailable_runtime_error(self.backend_kind)); + } + }); + } +} + +impl ksp_store_api::RawAccountStateRead for Store { + fn get_raw_account_state<'a>( + &'a self, + reference: &'a ksp_store_api::RawAccountStateReference, + ) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result>> { + let network_check = validate_operation_network(&self.network, reference.network(), self.backend_kind); + if let std::result::Result::Err(error) = network_check { + return std::boxed::Box::pin(async move { + return std::result::Result::Err(error); + }); + } + return std::boxed::Box::pin(async move { + #[cfg(feature = "postgres")] + { + return match &self.runtime { + StoreRuntime::Postgres(backend) => { + let result = backend.get_raw_account_state(reference).await; + result.map_err(|error| return map_postgres_error(error, self.backend_kind, self.network.as_str())) + }, + }; + } + #[cfg(not(feature = "postgres"))] + { + let _ = reference; + return std::result::Result::Err(unavailable_runtime_error(self.backend_kind)); + } + }); + } + + fn list_raw_account_states<'a>( + &'a self, + query: &'a ksp_store_api::RawAccountStateQuery, + ) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result>> { + let network_check = validate_operation_network(&self.network, query.network(), self.backend_kind); + if let std::result::Result::Err(error) = network_check { + return std::boxed::Box::pin(async move { + return std::result::Result::Err(error); + }); + } + return std::boxed::Box::pin(async move { + #[cfg(feature = "postgres")] + { + return match &self.runtime { + StoreRuntime::Postgres(backend) => { + let result = backend.list_raw_account_states(query).await; + result.map_err(|error| return map_postgres_error(error, self.backend_kind, self.network.as_str())) + }, + }; + } + #[cfg(not(feature = "postgres"))] + { + let _ = query; + return std::result::Result::Err(unavailable_runtime_error(self.backend_kind)); + } + }); + } +} + +impl ksp_store_api::RawAccountStateWrite for Store { + fn persist_raw_account_acquisition<'a>( + &'a self, + state: ksp_store_api::RawAccountState, + observation: ksp_store_api::RawAccountObservation, + ) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result> { + let state_network = validate_operation_network(&self.network, state.reference().network(), self.backend_kind); + if let std::result::Result::Err(error) = state_network { + return std::boxed::Box::pin(async move { + return std::result::Result::Err(error); + }); + } + let observation_network = validate_operation_network(&self.network, observation.account().network(), self.backend_kind); + if let std::result::Result::Err(error) = observation_network { + return std::boxed::Box::pin(async move { + return std::result::Result::Err(error); + }); + } + return std::boxed::Box::pin(async move { + #[cfg(feature = "postgres")] + { + return match &self.runtime { + StoreRuntime::Postgres(backend) => { + let result = backend.persist_raw_account_acquisition(state, observation).await; + result.map_err(|error| return map_postgres_error(error, self.backend_kind, self.network.as_str())) + }, + }; + } + #[cfg(not(feature = "postgres"))] + { + let _ = state; + let _ = observation; + return std::result::Result::Err(unavailable_runtime_error(self.backend_kind)); + } + }); + } +} + impl ksp_store_api::RawTransactionRead for Store { fn get_raw_transaction<'a>( &'a self, diff --git a/crates/ksp-store-lib/tests/dependency_boundary.rs b/crates/ksp-store-lib/tests/dependency_boundary.rs index d4a417f..16c02a0 100644 --- a/crates/ksp-store-lib/tests/dependency_boundary.rs +++ b/crates/ksp-store-lib/tests/dependency_boundary.rs @@ -1,5 +1,5 @@ // file: crates/ksp-store-lib/tests/dependency_boundary.rs -// version: 7 +// version: 8 #![warn(missing_docs)] #![deny(unreachable_pub)] @@ -60,34 +60,28 @@ fn pre_005_facade_exposes_no_physical_postgres_types_or_environment_bypass() { } #[test] -fn pre_008_facade_dispatches_six_raw_transaction_capabilities_without_physical_leak() { +fn pre_008_facade_dispatches_exact_ten_raw_capabilities_without_physical_leak() { let store = include_str!("../src/store.rs"); for required in [ - "impl ksp_store_api::RawTransactionRead for Store", - "impl ksp_store_api::RawTransactionWrite for Store", + "impl ksp_store_api::RawAccountObservationRead for Store", + "impl ksp_store_api::RawAccountObservationWrite for Store", + "impl ksp_store_api::RawAccountStateRead for Store", + "impl ksp_store_api::RawAccountStateWrite for Store", "impl ksp_store_api::RawTransactionObservationRead for Store", "impl ksp_store_api::RawTransactionObservationWrite for Store", + "impl ksp_store_api::RawTransactionRead for Store", "impl ksp_store_api::RawTransactionRetentionRead for Store", "impl ksp_store_api::RawTransactionRetentionWrite for Store", + "impl ksp_store_api::RawTransactionWrite for Store", "validate_operation_network", "StoreRuntime::Postgres(backend)", "map_postgres_error", ] { assert!(store.contains(required), "missing pre.008 Store capability dispatch contract: {required}"); } - for forbidden in [ - "impl ksp_store_api::RawAccountStateRead for Store", - "impl ksp_store_api::RawAccountStateWrite for Store", - "impl ksp_store_api::RawAccountObservationRead for Store", - "impl ksp_store_api::RawAccountObservationWrite for Store", - "tokio_postgres::", - "deadpool_postgres::", - "CREATE TABLE", - "INSERT INTO", - "UPDATE ksp_", - "DELETE FROM", - ] { - assert!(!store.contains(forbidden), "pre.008 facade leaked physical or RawAccount scope: {forbidden}"); + assert_eq!(store.matches("impl ksp_store_api::Raw").count(), 10); + for forbidden in ["tokio_postgres::", "deadpool_postgres::", "CREATE TABLE", "INSERT INTO", "UPDATE ksp_", "DELETE FROM"] { + assert!(!store.contains(forbidden), "pre.008 facade leaked physical backend material: {forbidden}"); } return; } diff --git a/crates/ksp-store-lib/tests/hardening_completeness.rs b/crates/ksp-store-lib/tests/hardening_completeness.rs index 98a3cc8..a892152 100644 --- a/crates/ksp-store-lib/tests/hardening_completeness.rs +++ b/crates/ksp-store-lib/tests/hardening_completeness.rs @@ -1,5 +1,5 @@ // file: crates/ksp-store-lib/tests/hardening_completeness.rs -// version: 4 +// version: 5 #![warn(missing_docs)] #![deny(unreachable_pub)] @@ -263,27 +263,24 @@ fn pre_009_facade_production_sources_keep_config_env_physical_sql_and_backend_ha } #[test] -fn pre_010_facade_raw_transaction_capability_inventory_is_exact_and_raw_account_scope_stays_closed() { +fn pre_010_facade_raw_capability_inventory_is_exactly_ten() { let store = include_str!("../src/store.rs"); let capability_impls = [ - "impl ksp_store_api::RawTransactionRead for Store", - "impl ksp_store_api::RawTransactionWrite for Store", + "impl ksp_store_api::RawAccountObservationRead for Store", + "impl ksp_store_api::RawAccountObservationWrite for Store", + "impl ksp_store_api::RawAccountStateRead for Store", + "impl ksp_store_api::RawAccountStateWrite for Store", "impl ksp_store_api::RawTransactionObservationRead for Store", "impl ksp_store_api::RawTransactionObservationWrite for Store", + "impl ksp_store_api::RawTransactionRead for Store", "impl ksp_store_api::RawTransactionRetentionRead for Store", "impl ksp_store_api::RawTransactionRetentionWrite for Store", + "impl ksp_store_api::RawTransactionWrite for Store", ]; for implementation in capability_impls { assert_eq!(store.matches(implementation).count(), 1, "unexpected Store capability implementation inventory: {implementation}"); } - for forbidden in [ - "impl ksp_store_api::RawAccountStateRead for Store", - "impl ksp_store_api::RawAccountStateWrite for Store", - "impl ksp_store_api::RawAccountObservationRead for Store", - "impl ksp_store_api::RawAccountObservationWrite for Store", - ] { - assert!(!store.contains(forbidden), "RawAccountState scope opened in Store during RawTransaction hardening: {forbidden}"); - } - assert_eq!(store.matches("validate_operation_network(").count(), 9); + assert_eq!(store.matches("impl ksp_store_api::Raw").count(), 10); + assert_eq!(store.matches("validate_operation_network(").count(), 14); return; } diff --git a/crates/ksp-store-lib/tests/public_api.rs b/crates/ksp-store-lib/tests/public_api.rs index eb5c375..8ad3b0b 100644 --- a/crates/ksp-store-lib/tests/public_api.rs +++ b/crates/ksp-store-lib/tests/public_api.rs @@ -1,5 +1,5 @@ // file: crates/ksp-store-lib/tests/public_api.rs -// version: 7 +// version: 8 #![warn(missing_docs)] #![deny(unreachable_pub)] @@ -74,21 +74,25 @@ fn pre_007_health_and_runtime_snapshot_types_are_portable_crate_root_contracts() return; } -fn assert_raw_transaction_capabilities() +fn assert_raw_capabilities() where - T: ksp_store_lib::RawTransactionRead - + ksp_store_lib::RawTransactionWrite + T: ksp_store_lib::RawAccountObservationRead + + ksp_store_lib::RawAccountObservationWrite + + ksp_store_lib::RawAccountStateRead + + ksp_store_lib::RawAccountStateWrite + ksp_store_lib::RawTransactionObservationRead + ksp_store_lib::RawTransactionObservationWrite + + ksp_store_lib::RawTransactionRead + ksp_store_lib::RawTransactionRetentionRead - + ksp_store_lib::RawTransactionRetentionWrite, + + ksp_store_lib::RawTransactionRetentionWrite + + ksp_store_lib::RawTransactionWrite, { let _marker = std::marker::PhantomData::; return; } #[test] -fn pre_008_store_facade_implements_all_six_raw_transaction_capabilities() { - assert_raw_transaction_capabilities::(); +fn pre_008_store_facade_implements_exact_raw_capability_set_10_of_10() { + assert_raw_capabilities::(); return; } diff --git a/crates/ksp-store-postgres-lib/src/lib.rs b/crates/ksp-store-postgres-lib/src/lib.rs index a516d71..55784f8 100644 --- a/crates/ksp-store-postgres-lib/src/lib.rs +++ b/crates/ksp-store-postgres-lib/src/lib.rs @@ -1,5 +1,5 @@ // file: crates/ksp-store-postgres-lib/src/lib.rs -// version: 20 +// version: 21 #![warn(missing_docs)] #![deny(unreachable_pub)] @@ -29,8 +29,10 @@ //! adds backend-private RAW account state/observation read mapping and hostile-row //! guards. `0.3.4-pre.005` adds atomic account state+observation acquisition writes //! with exact idempotence/conflict classification. `0.3.4-pre.006` adds additional -//! account observations guarded by the existing state reference while pagination and -//! all four account trait implementations remain closed. +//! account observations guarded by the existing state reference. `0.3.4-pre.007` adds +//! deterministic account keyset pagination with the fixed `KSPA` cursor. `0.3.4-pre.008` +//! implements the four `RawAccount*` capabilities directly on `PostgresBackend`, completing +//! the backend RAW capability inventory at ten without exposing physical PostgreSQL types. //! //! This crate depends on `ksp-store-api` and never on `ksp-store-lib`. The //! common facade consumes only this crate's narrow backend bridge and never diff --git a/crates/ksp-store-postgres-lib/src/runtime.rs b/crates/ksp-store-postgres-lib/src/runtime.rs index b05fa0f..90bc7ab 100644 --- a/crates/ksp-store-postgres-lib/src/runtime.rs +++ b/crates/ksp-store-postgres-lib/src/runtime.rs @@ -1,5 +1,5 @@ // file: crates/ksp-store-postgres-lib/src/runtime.rs -// version: 14 +// version: 15 const APPLICATION_NAME: &str = "ksp-store"; const MAX_CONNECTION_URI_BYTES: usize = 4_096; @@ -469,6 +469,65 @@ impl std::fmt::Debug for PostgresBackend { } } +impl ksp_store_api::RawAccountObservationRead for PostgresBackend { + fn get_raw_account_observation<'a>( + &'a self, + observation_key: &'a ksp_store_api::RawObservationKey, + ) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result>> { + return std::boxed::Box::pin(async move { + let result = PostgresBackend::get_raw_account_observation(self, observation_key).await; + return result.map_err(map_capability_error); + }); + } +} + +impl ksp_store_api::RawAccountObservationWrite for PostgresBackend { + fn record_raw_account_observation<'a>( + &'a self, + observation: ksp_store_api::RawAccountObservation, + ) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result> { + return std::boxed::Box::pin(async move { + let result = PostgresBackend::record_raw_account_observation(self, observation).await; + return result.map_err(map_capability_error); + }); + } +} + +impl ksp_store_api::RawAccountStateRead for PostgresBackend { + fn get_raw_account_state<'a>( + &'a self, + reference: &'a ksp_store_api::RawAccountStateReference, + ) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result>> { + return std::boxed::Box::pin(async move { + let result = PostgresBackend::get_raw_account_state(self, reference).await; + return result.map_err(map_capability_error); + }); + } + + fn list_raw_account_states<'a>( + &'a self, + query: &'a ksp_store_api::RawAccountStateQuery, + ) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result>> { + return std::boxed::Box::pin(async move { + let result = PostgresBackend::list_raw_account_states(self, query).await; + return result.map_err(map_capability_error); + }); + } +} + +impl ksp_store_api::RawAccountStateWrite for PostgresBackend { + fn persist_raw_account_acquisition<'a>( + &'a self, + state: ksp_store_api::RawAccountState, + observation: ksp_store_api::RawAccountObservation, + ) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result> { + return std::boxed::Box::pin(async move { + let result = PostgresBackend::persist_raw_account_acquisition(self, state, observation).await; + return result.map_err(map_capability_error); + }); + } +} + impl ksp_store_api::RawTransactionRead for PostgresBackend { fn get_raw_transaction<'a>( &'a self, diff --git a/crates/ksp-store-postgres-lib/tests/dependency_boundary.rs b/crates/ksp-store-postgres-lib/tests/dependency_boundary.rs index 14128e0..9041cec 100644 --- a/crates/ksp-store-postgres-lib/tests/dependency_boundary.rs +++ b/crates/ksp-store-postgres-lib/tests/dependency_boundary.rs @@ -1,5 +1,5 @@ // file: crates/ksp-store-postgres-lib/tests/dependency_boundary.rs -// version: 22 +// version: 23 #![warn(missing_docs)] #![deny(unreachable_pub)] @@ -54,7 +54,6 @@ fn pre_005_backend_keeps_environment_sql_migrations_and_physical_types_private() ] { assert!(!crate_root.contains(forbidden), "forbidden PostgreSQL crate-root surface detected: {forbidden}"); } - let runtime = include_str!("../src/runtime.rs"); for forbidden in [ "std::env", "dotenv", @@ -126,7 +125,7 @@ fn pre_003_fix_001_migration_engine_uses_split_schema_contract_and_binds_network } #[test] -fn pre_003_v002_schema_is_complete_without_account_trait_or_write_dispatch() { +fn pre_003_v002_schema_is_complete_and_keeps_capability_implementation_out_of_schema_layers() { let migration = include_str!("../src/migration.rs"); let schema = include_str!("../src/schema.rs"); let runtime = include_str!("../src/runtime.rs"); @@ -147,12 +146,13 @@ fn pre_003_v002_schema_is_complete_without_account_trait_or_write_dispatch() { assert!(slot_check.contains("slot >= 0 AND slot <= 18446744073709551615")); assert!(index.contains("ON ksp_raw_account_states (slot, pubkey, state_hash)")); assert!(!index.contains("WHERE")); - assert!(!runtime.contains("impl ksp_store_api::RawAccount")); + assert!(!migration.contains("impl ksp_store_api::RawAccount")); + assert!(!schema.contains("impl ksp_store_api::RawAccount")); return; } #[test] -fn pre_005_raw_account_acquisition_is_atomic_idempotent_and_keeps_destructive_trait_scope_closed() { +fn pre_005_raw_account_acquisition_is_atomic_idempotent_and_keeps_trait_impls_out_of_sql_module() { let crate_root = include_str!("../src/lib.rs"); let raw = include_str!("../src/raw_account.rs"); let runtime = include_str!("../src/runtime.rs"); @@ -183,13 +183,14 @@ fn pre_005_raw_account_acquisition_is_atomic_idempotent_and_keeps_destructive_tr for forbidden in ["UPDATE ", "DELETE FROM", "ON CONFLICT DO UPDATE", " OFFSET "] { assert!(!raw.contains(forbidden), "pre.005 account module contains later/destructive scope: {forbidden}"); } - for forbidden in [ + for implementation in [ "impl ksp_store_api::RawAccountStateRead for PostgresBackend", "impl ksp_store_api::RawAccountStateWrite for PostgresBackend", "impl ksp_store_api::RawAccountObservationRead for PostgresBackend", "impl ksp_store_api::RawAccountObservationWrite for PostgresBackend", ] { - assert!(!runtime.contains(forbidden), "pre.005 opened RawAccount trait scope prematurely: {forbidden}"); + assert!(!raw.contains(implementation), "account capability implementation leaked into SQL/mapping module: {implementation}"); + assert_eq!(runtime.matches(implementation).count(), 1, "missing or duplicated account runtime bridge implementation: {implementation}"); } for forbidden in ["std::env", "dotenv", "ksp_store_lib", "ksp_config_lib", "sqlx::", "SELECT *"] { assert!(!raw.contains(forbidden), "RAW account module contains forbidden ownership/query material: {forbidden}"); @@ -216,13 +217,14 @@ fn pre_006_raw_account_additional_observation_is_reference_guarded_cancellation_ for forbidden in ["UPDATE ", "DELETE FROM", "ON CONFLICT DO UPDATE", " OFFSET "] { assert!(!raw.contains(forbidden), "pre.006 account module contains later/destructive scope: {forbidden}"); } - for forbidden in [ + for implementation in [ "impl ksp_store_api::RawAccountStateRead for PostgresBackend", "impl ksp_store_api::RawAccountStateWrite for PostgresBackend", "impl ksp_store_api::RawAccountObservationRead for PostgresBackend", "impl ksp_store_api::RawAccountObservationWrite for PostgresBackend", ] { - assert!(!runtime.contains(forbidden), "pre.006 opened RawAccount trait scope prematurely: {forbidden}"); + assert!(!raw.contains(implementation), "account capability implementation leaked into SQL/mapping module: {implementation}"); + assert_eq!(runtime.matches(implementation).count(), 1, "missing or duplicated account runtime bridge implementation: {implementation}"); } return; } @@ -368,7 +370,7 @@ fn pre_007_raw_account_pagination_is_keyset_cursor_bound_and_policy_free() { "impl ksp_store_api::RawAccountObservationRead for PostgresBackend", "impl ksp_store_api::RawAccountObservationWrite for PostgresBackend", ] { - assert!(!raw.contains(forbidden), "pre.007 opened account trait scope prematurely: {forbidden}"); + assert!(!raw.contains(forbidden), "account trait implementation leaked into raw account SQL/mapping module: {forbidden}"); } return; } @@ -406,25 +408,22 @@ fn pre_007_raw_retention_is_atomic_compare_and_transition_without_fake_compactio } #[test] -fn pre_008_backend_trait_implementations_stay_in_runtime_bridge_and_raw_account_scope_stays_closed() { +fn pre_008_backend_trait_implementations_cover_exact_ten_raw_capabilities_in_runtime_bridge() { let runtime = include_str!("../src/runtime.rs"); - for required in [ - "impl ksp_store_api::RawTransactionRead for PostgresBackend", - "impl ksp_store_api::RawTransactionWrite for PostgresBackend", - "impl ksp_store_api::RawTransactionObservationRead for PostgresBackend", - "impl ksp_store_api::RawTransactionObservationWrite for PostgresBackend", - "impl ksp_store_api::RawTransactionRetentionRead for PostgresBackend", - "impl ksp_store_api::RawTransactionRetentionWrite for PostgresBackend", - ] { - assert!(runtime.contains(required), "missing pre.008 PostgreSQL capability implementation: {required}"); - } - for forbidden in [ - "impl ksp_store_api::RawAccountStateRead for PostgresBackend", - "impl ksp_store_api::RawAccountStateWrite for PostgresBackend", + for implementation in [ "impl ksp_store_api::RawAccountObservationRead for PostgresBackend", "impl ksp_store_api::RawAccountObservationWrite for PostgresBackend", + "impl ksp_store_api::RawAccountStateRead for PostgresBackend", + "impl ksp_store_api::RawAccountStateWrite for PostgresBackend", + "impl ksp_store_api::RawTransactionObservationRead for PostgresBackend", + "impl ksp_store_api::RawTransactionObservationWrite for PostgresBackend", + "impl ksp_store_api::RawTransactionRead for PostgresBackend", + "impl ksp_store_api::RawTransactionRetentionRead for PostgresBackend", + "impl ksp_store_api::RawTransactionRetentionWrite for PostgresBackend", + "impl ksp_store_api::RawTransactionWrite for PostgresBackend", ] { - assert!(!runtime.contains(forbidden), "pre.008 opened RawAccount capability scope prematurely: {forbidden}"); + assert_eq!(runtime.matches(implementation).count(), 1, "unexpected PostgreSQL RAW capability inventory: {implementation}"); } + assert_eq!(runtime.matches("impl ksp_store_api::Raw").count(), 10); return; } diff --git a/crates/ksp-store-postgres-lib/tests/hardening_completeness.rs b/crates/ksp-store-postgres-lib/tests/hardening_completeness.rs index 3f3de3e..9e56bcb 100644 --- a/crates/ksp-store-postgres-lib/tests/hardening_completeness.rs +++ b/crates/ksp-store-postgres-lib/tests/hardening_completeness.rs @@ -1,5 +1,5 @@ // file: crates/ksp-store-postgres-lib/tests/hardening_completeness.rs -// version: 14 +// version: 15 #![warn(missing_docs)] #![deny(unreachable_pub)] @@ -186,7 +186,7 @@ fn pre_009_backend_error_bridge_cannot_retain_external_error_or_secret_text() { } #[test] -fn pre_009_backend_has_no_env_bypass_reverse_facade_edge_or_raw_account_trait_implementation() { +fn pre_009_backend_has_no_env_bypass_reverse_facade_edge_and_keeps_account_impls_in_runtime_bridge() { let production = std::format!( "{} {} @@ -224,9 +224,8 @@ fn pre_009_backend_has_no_env_bypass_reverse_facade_edge_or_raw_account_trait_im "ksp_store_lib", "ksp_config_lib", "sqlx::", - "impl ksp_store_api::RawAccount", ] { - assert!(!production.contains(forbidden), "forbidden backend ownership/reverse-edge/RawAccount material detected: {forbidden}"); + assert!(!production.contains(forbidden), "forbidden backend ownership/reverse-edge material detected: {forbidden}"); } let bootstrap_sql = include_str!("../migrations/v000_bootstrap/tables/001_ksp_store_schema_migrations.sql"); assert!(bootstrap_sql.contains("ksp_store_schema_migrations")); @@ -262,27 +261,24 @@ fn pre_009_live_raw_transaction_proof_is_opt_in_isolated_and_secret_safe() { } #[test] -fn pre_010_raw_transaction_capability_implementation_inventory_is_exact_and_raw_account_scope_stays_closed() { +fn pre_010_raw_capability_implementation_inventory_is_exactly_ten() { let runtime = include_str!("../src/runtime.rs"); let capability_impls = [ - "impl ksp_store_api::RawTransactionRead for PostgresBackend", - "impl ksp_store_api::RawTransactionWrite for PostgresBackend", + "impl ksp_store_api::RawAccountObservationRead for PostgresBackend", + "impl ksp_store_api::RawAccountObservationWrite for PostgresBackend", + "impl ksp_store_api::RawAccountStateRead for PostgresBackend", + "impl ksp_store_api::RawAccountStateWrite for PostgresBackend", "impl ksp_store_api::RawTransactionObservationRead for PostgresBackend", "impl ksp_store_api::RawTransactionObservationWrite for PostgresBackend", + "impl ksp_store_api::RawTransactionRead for PostgresBackend", "impl ksp_store_api::RawTransactionRetentionRead for PostgresBackend", "impl ksp_store_api::RawTransactionRetentionWrite for PostgresBackend", + "impl ksp_store_api::RawTransactionWrite for PostgresBackend", ]; for implementation in capability_impls { assert_eq!(runtime.matches(implementation).count(), 1, "unexpected PostgreSQL capability implementation inventory: {implementation}"); } - for forbidden in [ - "impl ksp_store_api::RawAccountStateRead for PostgresBackend", - "impl ksp_store_api::RawAccountStateWrite for PostgresBackend", - "impl ksp_store_api::RawAccountObservationRead for PostgresBackend", - "impl ksp_store_api::RawAccountObservationWrite for PostgresBackend", - ] { - assert!(!runtime.contains(forbidden), "RawAccountState scope opened during RawTransaction hardening: {forbidden}"); - } + assert_eq!(runtime.matches("impl ksp_store_api::Raw").count(), 10); let migration = include_str!("../src/migration.rs"); assert!(migration.contains("raw_account_state")); assert!(migration.contains("crate::V002_RESOURCES")); diff --git a/crates/ksp-store-postgres-lib/tests/public_api.rs b/crates/ksp-store-postgres-lib/tests/public_api.rs index ae1710a..c436022 100644 --- a/crates/ksp-store-postgres-lib/tests/public_api.rs +++ b/crates/ksp-store-postgres-lib/tests/public_api.rs @@ -1,5 +1,5 @@ // file: crates/ksp-store-postgres-lib/tests/public_api.rs -// version: 13 +// version: 14 #![warn(missing_docs)] #![deny(unreachable_pub)] @@ -128,21 +128,25 @@ fn pre_007_raw_retention_write_bridge_uses_backend_independent_transition_and_ou return; } -fn assert_raw_transaction_capabilities() +fn assert_raw_capabilities() where - T: ksp_store_api::RawTransactionRead - + ksp_store_api::RawTransactionWrite + T: ksp_store_api::RawAccountObservationRead + + ksp_store_api::RawAccountObservationWrite + + ksp_store_api::RawAccountStateRead + + ksp_store_api::RawAccountStateWrite + ksp_store_api::RawTransactionObservationRead + ksp_store_api::RawTransactionObservationWrite + + ksp_store_api::RawTransactionRead + ksp_store_api::RawTransactionRetentionRead - + ksp_store_api::RawTransactionRetentionWrite, + + ksp_store_api::RawTransactionRetentionWrite + + ksp_store_api::RawTransactionWrite, { let _marker = std::marker::PhantomData::; return; } #[test] -fn pre_008_postgres_backend_implements_all_six_raw_transaction_capabilities() { - assert_raw_transaction_capabilities::(); +fn pre_008_postgres_backend_implements_exact_raw_capability_set_10_of_10() { + assert_raw_capabilities::(); return; } diff --git a/deltas/0.3.4/pre.008.md b/deltas/0.3.4/pre.008.md new file mode 100644 index 0000000..bdbd8ce --- /dev/null +++ b/deltas/0.3.4/pre.008.md @@ -0,0 +1,219 @@ + + + +# Delta `0.3.4-pre.008` — capabilities account + conformance RAW 10/10 + +## 1. Base requise + +```text +0.3.4-pre.007-fix.001 +workspace.package.version = 0.3.4-pre.7.fix.1 +``` + +Le gate opérateur fourni le 2026-08-30 pour `pre.007-fix.001` est entièrement vert : + +```text +cargo fmt --all PASS +audit Rust général / exports / workspace PASS +audit Markdown PASS — 239 tables / 145 files +cargo check --workspace PASS +cargo clippy --workspace --all-targets PASS +cargo test -p ksp-store-api PASS +cargo test -p ksp-store-lib PASS +cargo test -p ksp-store-postgres-lib PASS — 63 unit tests + canaris, live ignored +cargo test -p ksp-config-lib PASS — 128 unit tests + ownership/public API +cargo check -p ksp-store-lib --no-default-features PASS +``` + +La verticale account possède donc déjà ses primitives physiques read/write/pagination validées avant ouverture des traits. + +## 2. Objectif + +Ouvrir uniquement les quatre capabilities account existantes : + +```text +RawAccountStateRead +RawAccountStateWrite +RawAccountObservationRead +RawAccountObservationWrite +``` + +sur : + +```text +PostgresBackend +Store +``` + +et transformer les canaris pour fermer l'inventaire RAW à : + +```text +6 RawTransaction* + 4 RawAccount* = 10/10 +``` + +Aucun nouveau SQL, modèle, migration, type public ou comportement physique n'est ajouté. + +## 3. Version + +Le workspace passe à : + +```text +0.3.4-pre.8 +``` + +## 4. `PostgresBackend` — quatre traits account + +`src/runtime.rs` implémente exactement une fois : + +```text +RawAccountStateRead +RawAccountStateWrite +RawAccountObservationRead +RawAccountObservationWrite +``` + +Chaque méthode adapte exclusivement le bridge étroit déjà présent : + +```text +get_raw_account_state +list_raw_account_states +persist_raw_account_acquisition +get_raw_account_observation +record_raw_account_observation +``` + +Les erreurs physiques passent par `map_capability_error`, comme les six traits transaction. Aucun SQL ou type PostgreSQL n'entre dans l'API de capability. + +## 5. `Store` — dispatch backend-neutral + +`Store` implémente les mêmes quatre traits et délègue uniquement au backend compilé. + +Guards réseau avant dispatch : + +```text +get state reference.network +list state query.network +persist state state.reference.network + observation.account.network +record obs observation.account.network +get obs aucun input réseau — RawObservationKey uniquement +``` + +Le backend conserve en plus le guard d'égalité : + +```text +observation.account == state.reference +``` + +avant `pool.get()` sur l'acquisition atomique. + +Avec `postgres` désactivé, les dix traits restent implémentés sur `Store` et leur chemin runtime retourne `backend_not_compiled`, sans dépendance physique compilée. + +## 6. Canaris transformés + +Les canaris historiques anti-account ne sont pas supprimés. Ils deviennent des canaris de placement et de complétude : + +```text +PostgresBackend : exactement 10 impl Raw* +Store : exactement 10 impl Raw* +chaque impl account apparaît exactement une fois +aucun impl account dans raw_account.rs +aucun impl métier dans migration.rs/schema.rs +aucun SQL/type PostgreSQL dans ksp-store-lib +validate_operation_network occurrences Store = 14 +``` + +Les canaris `pre.005`, `pre.006` et `pre.007` conservent leurs interdictions destructives et de policy (`UPDATE`, `DELETE`, `ON CONFLICT DO UPDATE`, `OFFSET`, clamp/batch policy), sans exiger que les traits account restent fermés après leur tranche d'ouverture légitime. + +## 7. Invariants préservés + +```text +ksp-store-api inchangée +raw_account.rs inchangé +raw_account/cursor.rs inchangé +SQL runtime account inchangé +V000/V001/V002 byte-inchangées +aucun account retention/archive/purge +aucun worker/job/backfill policy +aucun Transport -> Store +aucun nouveau backend +``` + +Checksum V002 inchangé : + +```text +ff21605ed45f7ab4c0f92bbb692700b4118a9488b04d50a31d259ac59bdb550e +``` + +## 8. Fichiers ajoutés + +```text +deltas/0.3.4/pre.008.md +``` + +## 9. Fichiers modifiés + +```text +Cargo.toml +crates/ksp-store-lib/src/lib.rs +crates/ksp-store-lib/src/store.rs +crates/ksp-store-lib/tests/dependency_boundary.rs +crates/ksp-store-lib/tests/hardening_completeness.rs +crates/ksp-store-lib/tests/public_api.rs +crates/ksp-store-postgres-lib/src/lib.rs +crates/ksp-store-postgres-lib/src/runtime.rs +crates/ksp-store-postgres-lib/tests/dependency_boundary.rs +crates/ksp-store-postgres-lib/tests/hardening_completeness.rs +crates/ksp-store-postgres-lib/tests/public_api.rs +docs/plans/025-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT_PLAN.md +docs/validation/021-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT.md +``` + +## 10. Fichiers supprimés + +```text +aucun +``` + +## 11. Validations exécutées à l'assemblage + +```text +python3 scripts/audit_rust_workspace_rules.py PASS +python3 scripts/audit_markdown_tables.py ... deltas/0.3.4 PASS +inventaire PostgresBackend Raw* = 10 PASS +inventaire Store Raw* = 10 PASS +validate_operation_network occurrences Store = 14 PASS +migrations V000/V001/V002 byte-identiques à pre.007-fix.001 PASS +raw_account.rs / raw_account/cursor.rs inchangés PASS +ksp-store-api inchangée PASS +``` + +`cargo`, `rustfmt`, Clippy et les tests Rust ne sont pas disponibles dans l'environnement d'assemblage ; le gate opérateur complet doit être rejoué. + +## 12. Gate opérateur à rejouer + +```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.4 +cargo check --workspace +cargo clippy --workspace --all-targets +cargo test -p ksp-store-api +cargo test -p ksp-store-lib +cargo test -p ksp-store-postgres-lib +cargo test -p ksp-config-lib +cargo check -p ksp-store-lib --no-default-features +``` + +## 13. Décisions prises + +- `pre.008` n'ajoute aucune primitive physique : elle ouvre uniquement les traits déjà prévus par `ksp-store-api`. +- `RawAccountObservationRead` n'ajoute pas de faux guard réseau, car `RawObservationKey` ne porte pas de réseau. +- La conformance 10/10 est vérifiée par inventaire exact, pas par simple présence partielle. +- Les canaris historiques sont transformés vers les invariants encore pertinents au lieu d'être supprimés. +- La preuve réelle PostgreSQL account reste réservée à `pre.009`. + +## 14. Questions ouvertes + +```text +aucune question bloquante pour pre.008 +``` diff --git a/docs/plans/025-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT_PLAN.md b/docs/plans/025-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT_PLAN.md index 95d0d16..d8aa756 100644 --- a/docs/plans/025-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT_PLAN.md +++ b/docs/plans/025-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT_PLAN.md @@ -1,11 +1,11 @@ - + # Plan `0.3.4` — Store/PostgreSQL `RawAccountState` + complétude RAW ## 1. Statut de la release -`0.3.4-pre.001` a figé le design. `0.3.4-pre.002` a matérialisé la fondation physique V002 minimale puis `pre.002-fix.001` a corrigé deux canaris sans toucher au SQL. `0.3.4-pre.003` complète V002 avec les contraintes de domaine, l'index de navigation, la compatibilité de schéma et le checksum final ; `pre.003-fix.001` corrige uniquement l'ordre alphabétique du bloc `const` de `migration.rs` et son gate opérateur complet est PASS. `0.3.4-pre.004` ouvre le mapping PostgreSQL privé et les deux lectures `get` account ; `pre.004-fix.001` corrige uniquement leur conformité au profil Clippy KSP et un canari inutilisé, sans write, pagination ni implémentation de capability. `pre.005` ajoute l'acquisition atomique, `pre.006` l'observation supplémentaire, et `pre.007` la pagination/cursor account ; son gate opérateur a révélé uniquement cinq `clone()` inutiles sur `Pubkey: Copy` et deux canaris historiques encore configurés pour interdire la pagination désormais légitime. +`0.3.4-pre.001` a figé le design. `0.3.4-pre.002` a matérialisé la fondation physique V002 minimale puis `pre.002-fix.001` a corrigé deux canaris sans toucher au SQL. `0.3.4-pre.003` complète V002 avec les contraintes de domaine, l'index de navigation, la compatibilité de schéma et le checksum final ; `pre.003-fix.001` corrige uniquement l'ordre alphabétique du bloc `const` de `migration.rs` et son gate opérateur complet est PASS. `0.3.4-pre.004` ouvre le mapping PostgreSQL privé et les deux lectures `get` account ; `pre.004-fix.001` corrige uniquement leur conformité au profil Clippy KSP et un canari inutilisé. `pre.005` ajoute l'acquisition atomique, `pre.006` l'observation supplémentaire, `pre.007` la pagination/cursor account et `pre.007-fix.001` réconcilie ses canaris ; le gate opérateur complet du fix est PASS. `pre.008` ouvre maintenant les quatre capabilities account sur `PostgresBackend` puis leur dispatch dans `Store`, pour porter l'inventaire RAW à 10/10 sans nouveau SQL. Base canonique auditée : @@ -17,8 +17,8 @@ workspace.package.version = 0.3.3 Version de travail de cette prerelease : ```text -workspace.package.version = 0.3.4-pre.7.fix.1 -label = 0.3.4-pre.007-fix.001 +workspace.package.version = 0.3.4-pre.8 +label = 0.3.4-pre.008 ``` Décision de scope : `ksp-store-api` reste inchangée. L'audit n'a révélé aucun gap backend-agnostic bloquant ; la difficulté restante est exclusivement l'implémentation physique PostgreSQL et son dispatch par la façade. @@ -71,7 +71,7 @@ Canaries qui ferment encore volontairement la surface account dans `v0.3.3` : - `crates/ksp-store-postgres-lib/tests/dependency_boundary.rs` interdit les quatre `impl RawAccount* for PostgresBackend` ; - `crates/ksp-store-postgres-lib/tests/hardening_completeness.rs` interdit encore `impl ksp_store_api::RawAccount*` et toute matérialisation `ksp_raw_account` dans la slice transaction. -Ces canaries devront être **transformées**, pas simplement supprimées : à `pre.008`, elles devront prouver exactement six capabilities transaction + quatre account. Les canaries qui imposent que V000 et `health.rs` restent business-free demeurent valides et ne doivent pas être assouplies. +Ces canaries sont **transformées**, pas simplement supprimées, en `pre.008` : elles prouvent désormais exactement six capabilities transaction + quatre account. Les canaries qui imposent que V000 et `health.rs` restent business-free demeurent valides et ne doivent pas être assouplies. ### 3.1 Matrice détaillée des quatre capabilities account @@ -647,7 +647,7 @@ Les canaris unitaires figent l'ordre transactionnel `BEGIN -> FOR KEY SHARE -> i ### `pre.007` — Liste account et cursor keyset V1 -**Statut : réalisé ; gate opérateur complet à rejouer.** +**Statut : réalisé ; gate opérateur complet PASS via `pre.007-fix.001`.** Budget cible : **15-20 min**. `RawAccountStateQuery` est matérialisée dans le backend PostgreSQL avec navigation keyset sur l'ordre total `(slot, pubkey, state_hash)`, entièrement inversé en DESC. Les requêtes sans filtre utilisent l'index V002 `(slot, pubkey, state_hash)` ; les requêtes filtrées par pubkey conservent le même ordre total et sont compatibles avec la PK `(pubkey, slot, state_hash)`. Aucun `OFFSET` ni plafond fonctionnel KSP n'est ajouté. @@ -657,15 +657,27 @@ Le cursor V1 account est fixe à **109 bytes** : magic `KSPA`, version `1`, `las #### `pre.007-fix.001` — Réconciliation Clippy/canaris pagination -**Statut : réalisé ; gate opérateur complet à rejouer.** +**Statut : réalisé ; gate opérateur complet PASS.** Fix strictement borné aux tests/canaris : suppression de cinq `clone()` sur `Pubkey: Copy` dans les canaris cursor, et transformation des canaris hérités de `pre.005/pre.006` pour qu'ils continuent d'interdire `UPDATE`, `DELETE`, `ON CONFLICT DO UPDATE`, `OFFSET` et les quatre implémentations `RawAccount*`, sans interdire `list_raw_account_states` désormais possédée par `pre.007`. Aucun code de pagination, SQL métier ou migration n'est modifié. ### `pre.008` — Implémentations backend, dispatch Store et conformance 10/10 -**Statut : planifié.** +**Statut : réalisé ; gate opérateur complet à rejouer.** -Budget cible : **15-20 min**. Ouvrir les quatre capabilities account dans `PostgresBackend` et `Store`, puis faire évoluer les canaries vers l'inventaire RAW complet de dix capabilities. +Budget cible : **15-20 min**. Les quatre capabilities account sont implémentées directement sur `PostgresBackend` en adaptant exclusivement les bridges déjà validés en `pre.004` à `pre.007`, puis sur `Store` avec le même mapping d'erreur et les mêmes guards réseau que la verticale transaction. Aucun nouveau SQL, migration, DTO ou type public n'est ajouté. + +L'inventaire devient exactement : + +```text +6 RawTransaction* + 4 RawAccount* = 10/10 +PostgresBackend = 10 capabilities RAW +Store = 10 capabilities RAW +``` + +`RawAccountStateRead` délègue `get` et `list`; `RawAccountStateWrite` délègue l'acquisition atomique state+observation; `RawAccountObservationRead` délègue la lecture par observation key; `RawAccountObservationWrite` délègue l'observation supplémentaire avec référence state existante. La façade vérifie le réseau sur reference/query/state/observation avant dispatch lorsque l'input porte un réseau ; `RawAccountObservationRead` reste sans guard d'input réseau puisque sa clé n'en contient pas. + +Les canaris historiques sont transformés pour vérifier l'emplacement des implémentations dans `runtime.rs`, l'absence d'implémentation directe dans `raw_account.rs`, l'inventaire exact 10/10 et l'absence de types/SQL PostgreSQL dans `ksp-store-lib`. `--no-default-features` doit conserver les dix traits sur `Store` tout en retournant `backend_not_compiled` au runtime faute de backend compilé. ### `pre.009` — Preuve PostgreSQL live account diff --git a/docs/validation/021-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT.md b/docs/validation/021-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT.md index 87a6ec1..1717783 100644 --- a/docs/validation/021-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT.md +++ b/docs/validation/021-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT.md @@ -1,11 +1,11 @@ - + # Validation `0.3.4` — Store/PostgreSQL `RawAccountState` + complétude RAW ## 1. Portée -Cette matrice est ouverte par `0.3.4-pre.001`. `0.3.4-pre.002` matérialise la fondation V002 minimale et `pre.002-fix.001` corrige deux canaris sans modifier le SQL ; son gate opérateur complet est PASS. `0.3.4-pre.003` complète les contraintes de domaine, l'index de navigation, la compatibilité externe et le checksum V002 final ; `pre.003-fix.001` corrige uniquement l'ordre alphabétique du bloc `const` et son gate opérateur complet est PASS. `pre.004` matérialise le mapping privé et les deux lectures `get` account ; `pre.004-fix.001` corrige la conformité Clippy du mapping `Row` et un canari inutilisé. Les writes, la pagination, le dispatch Store et les quatre implémentations de capabilities account restent volontairement pending. +Cette matrice est ouverte par `0.3.4-pre.001`. `0.3.4-pre.002` matérialise la fondation V002 minimale et `pre.002-fix.001` corrige deux canaris sans modifier le SQL ; son gate opérateur complet est PASS. `0.3.4-pre.003` complète les contraintes de domaine, l'index de navigation, la compatibilité externe et le checksum V002 final ; `pre.003-fix.001` corrige uniquement l'ordre alphabétique du bloc `const` et son gate opérateur complet est PASS. `pre.004` à `pre.007` matérialisent successivement mapping/read, writes, observation supplémentaire et pagination account ; `pre.007-fix.001` clôt son gate opérateur en PASS. `pre.008` ouvre les quatre capabilities account dans `PostgresBackend` et `Store`, portant l'inventaire RAW statique à 10/10 ; la preuve PostgreSQL live reste réservée à `pre.009`. Base : @@ -277,8 +277,8 @@ Le test live account devra être opt-in/ignored, URI stdin, sans environnement n | pre.004 | mapping privé state/observation + get reads + hostile rows | PASS | | pre.005 | acquisition atomique state+observation + idempotence/conflict | PASS | | pre.006 | observation supplémentaire + races/cancellation unitaires | PASS | -| pre.007 | list RawAccountStateQuery + keyset cursor V1 account | RECHECK | -| pre.008 | 4 impl backend + 4 dispatch Store + conformance 10/10 | PLANNED | +| pre.007 | list RawAccountStateQuery + keyset cursor V1 account | PASS | +| pre.008 | 4 impl backend + 4 dispatch Store + conformance 10/10 | CURRENT | | pre.009 | preuve PostgreSQL live account + coexistence RawTransaction | PLANNED | | pre.010 | hardening/completeness cross-family + canaries ownership | PLANNED | | pre.011 | gate technique final + replay live ciblé + graphes | PLANNED | @@ -503,7 +503,7 @@ La preuve de race/cancellation de cette tranche est volontairement unitaire/stru Restent fermés : `list_raw_account_states`, cursor `KSPA`, `OFFSET`, `UPDATE`/`DELETE`, `ON CONFLICT DO UPDATE`, les quatre implémentations `RawAccount*` sur `PostgresBackend` et tout dispatch account dans `Store`. Les migrations V000/V001/V002 sont inchangées ; le checksum V002 final reste `ff21605ed45f7ab4c0f92bbb692700b4118a9488b04d50a31d259ac59bdb550e`. ## 24. Verdict `pre.007` -Pagination `RawAccountStateQuery` et cursor `KSPA` : **PASS fonctionnel ; gate `pre.007` non clos uniquement à cause de cinq warnings Clippy `clone_on_copy` et de deux canaris historiques stales. `pre.007-fix.001` corrige exclusivement ces tests/canaris ; gate opérateur complet à rejouer.** +Pagination `RawAccountStateQuery` et cursor `KSPA` : **PASS complet opérateur via `pre.007-fix.001`**. Le gate initial avait isolé cinq warnings Clippy `clone_on_copy` et deux canaris historiques stales ; le fix les corrige sans toucher au code de pagination. Ordre physique figé : @@ -543,4 +543,47 @@ Les quatre `impl RawAccount* for PostgresBackend` et tout dispatch account dans Le fix ne modifie ni `src/raw_account.rs`, ni `src/raw_account/cursor.rs`, ni le SQL runtime, ni aucune migration. Les cinq `Pubkey::clone()` inutiles des tests cursor sont supprimés. Les canaris `pre.005/pre.006` sont transformés pour accepter `list_raw_account_states`, devenu scope légitime en `pre.007`, tout en continuant d'interdire les surfaces destructives (`UPDATE`, `DELETE`, `ON CONFLICT DO UPDATE`, `OFFSET`) et les quatre implémentations `RawAccount*` réservées à `pre.008`. -Le checksum V002 reste `ff21605ed45f7ab4c0f92bbb692700b4118a9488b04d50a31d259ac59bdb550e`. +Le checksum V002 reste `ff21605ed45f7ab4c0f92bbb692700b4118a9488b04d50a31d259ac59bdb550e`. Le gate opérateur complet du 2026-08-30 est PASS : audits Rust/Markdown, workspace check, Clippy all-targets, Store API, Store façade, backend PostgreSQL (63 tests unitaires), Config et `--no-default-features` sont verts. + +## 26. Verdict `pre.008` + +Conformance RAW backend/façade : **10/10 matérialisée ; gate opérateur complet à rejouer**. + +Inventaire exact attendu et désormais canarisé : + +```text +PostgresBackend + RawTransactionRead + RawTransactionWrite + RawTransactionObservationRead + RawTransactionObservationWrite + RawTransactionRetentionRead + RawTransactionRetentionWrite + RawAccountStateRead + RawAccountStateWrite + RawAccountObservationRead + RawAccountObservationWrite + +Store + même inventaire exact de 10 capabilities +``` + +Les quatre implémentations backend ne créent aucune nouvelle primitive physique : elles adaptent les méthodes étroites `get_raw_account_state`, `list_raw_account_states`, `persist_raw_account_acquisition`, `get_raw_account_observation` et `record_raw_account_observation` déjà validées. Les quatre implémentations de `Store` délèguent uniquement au backend compilé, appliquent `validate_operation_network` aux inputs réseau-scopés et réutilisent `map_postgres_error`. + +La lecture `RawAccountObservationRead` reste la seule capability account sans guard réseau pré-dispatch parce que `RawObservationKey` ne porte aucun réseau ; le backend mono-network reconstruit la référence avec son binding déjà validé. L'acquisition state+observation vérifie séparément les deux réseaux dans la façade puis conserve le guard d'égalité de référence backend avant `pool.get()`. + +Canaris transformés : + +```text +inventaire exact PostgresBackend = 10 +inventaire exact Store = 10 +impl account uniquement dans runtime bridge backend +aucun impl account direct dans raw_account.rs +aucun SQL/type PostgreSQL dans ksp-store-lib +validate_operation_network occurrences Store = 14 +feature postgres optionnelle inchangée +no-default-features doit continuer à compiler +``` + +Aucune migration V000/V001/V002 n'est modifiée ; le checksum V002 reste `ff21605ed45f7ab4c0f92bbb692700b4118a9488b04d50a31d259ac59bdb550e`. `ksp-store-api` reste inchangée. La preuve réelle PostgreSQL de coexistence, concurrence et cancellation account reste en `pre.009`. +