From eb6dbc31e82d37f6a56ca10ba6209203366c6beb Mon Sep 17 00:00:00 2001 From: SinuS Von SifriduS Date: Sun, 30 Aug 2026 22:06:24 +0200 Subject: [PATCH] v0.3.4-pre.004-fix.001 --- Cargo.toml | 4 +- .../ksp-store-postgres-lib/src/raw_account.rs | 159 ++++++++++++++---- .../tests/dependency_boundary.rs | 3 +- deltas/0.3.4/pre.004-fix.001.md | 119 +++++++++++++ ...-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT_PLAN.md | 16 +- .../021-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT.md | 25 ++- 6 files changed, 283 insertions(+), 43 deletions(-) create mode 100644 deltas/0.3.4/pre.004-fix.001.md diff --git a/Cargo.toml b/Cargo.toml index eb51f5c..c41bcb2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ # file: Cargo.toml -# version: 369 +# version: 370 [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.4" +version = "0.3.4-pre.4.fix.1" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" diff --git a/crates/ksp-store-postgres-lib/src/raw_account.rs b/crates/ksp-store-postgres-lib/src/raw_account.rs index 6c08371..ad5c8f7 100644 --- a/crates/ksp-store-postgres-lib/src/raw_account.rs +++ b/crates/ksp-store-postgres-lib/src/raw_account.rs @@ -1,5 +1,5 @@ // file: crates/ksp-store-postgres-lib/src/raw_account.rs -// version: 1 +// version: 2 const GET_ACCOUNT_OBSERVATION_SQL: &str = "SELECT observation_key, account_pubkey, account_slot::text AS account_slot_text, account_state_hash, provider, protocol, acquisition_method, origin, received_at_unix_millis, capture_session_id, commitment, endpoint_id, filter_id, observed_at_unix_millis, source_payload_hash, source_payload_size_bytes, is_startup, transaction_signature, write_version::text AS write_version_text FROM ksp_raw_account_observations WHERE observation_key = $1"; const GET_ACCOUNT_STATE_SQL: &str = "SELECT pubkey, slot::text AS slot_text, state_hash, lamports::text AS lamports_text, owner, executable, rent_epoch::text AS rent_epoch_text, data FROM ksp_raw_account_states WHERE pubkey = $1 AND slot = $2::TEXT::NUMERIC AND state_hash = $3"; @@ -131,40 +131,139 @@ pub(crate) async fn get_raw_account_observation( } fn raw_account_observation_db_row(row: &tokio_postgres::Row) -> std::result::Result { + let account_pubkey: std::vec::Vec = match row.try_get("account_pubkey") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_observation_decode")), + }; + let account_slot_text: std::string::String = match row.try_get("account_slot_text") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_observation_decode")), + }; + let account_state_hash: std::vec::Vec = match row.try_get("account_state_hash") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_observation_decode")), + }; + let acquisition_method: std::string::String = match row.try_get("acquisition_method") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_observation_decode")), + }; + let capture_session_id: std::option::Option = match row.try_get("capture_session_id") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_observation_decode")), + }; + let commitment: std::option::Option = match row.try_get("commitment") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_observation_decode")), + }; + let endpoint_id: std::option::Option = match row.try_get("endpoint_id") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_observation_decode")), + }; + let filter_id: std::option::Option = match row.try_get("filter_id") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_observation_decode")), + }; + let is_startup: std::option::Option = match row.try_get("is_startup") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_observation_decode")), + }; + let observation_key: std::vec::Vec = match row.try_get("observation_key") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_observation_decode")), + }; + let observed_at_unix_millis: std::option::Option = match row.try_get("observed_at_unix_millis") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_observation_decode")), + }; + let origin: std::string::String = match row.try_get("origin") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_observation_decode")), + }; + let protocol: std::string::String = match row.try_get("protocol") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_observation_decode")), + }; + let provider: std::string::String = match row.try_get("provider") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_observation_decode")), + }; + let received_at_unix_millis: i64 = match row.try_get("received_at_unix_millis") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_observation_decode")), + }; + let source_payload_hash: std::option::Option> = match row.try_get("source_payload_hash") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_observation_decode")), + }; + let source_payload_size_bytes: std::option::Option = match row.try_get("source_payload_size_bytes") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_observation_decode")), + }; + let transaction_signature: std::option::Option> = match row.try_get("transaction_signature") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_observation_decode")), + }; + let write_version_text: std::option::Option = match row.try_get("write_version_text") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_observation_decode")), + }; return std::result::Result::Ok(RawAccountObservationDbRow { - account_pubkey: row.try_get("account_pubkey").map_err(|_| data_invalid("raw_account_observation_decode"))?, - account_slot_text: row.try_get("account_slot_text").map_err(|_| data_invalid("raw_account_observation_decode"))?, - account_state_hash: row.try_get("account_state_hash").map_err(|_| data_invalid("raw_account_observation_decode"))?, - acquisition_method: row.try_get("acquisition_method").map_err(|_| data_invalid("raw_account_observation_decode"))?, - capture_session_id: row.try_get("capture_session_id").map_err(|_| data_invalid("raw_account_observation_decode"))?, - commitment: row.try_get("commitment").map_err(|_| data_invalid("raw_account_observation_decode"))?, - endpoint_id: row.try_get("endpoint_id").map_err(|_| data_invalid("raw_account_observation_decode"))?, - filter_id: row.try_get("filter_id").map_err(|_| data_invalid("raw_account_observation_decode"))?, - is_startup: row.try_get("is_startup").map_err(|_| data_invalid("raw_account_observation_decode"))?, - observation_key: row.try_get("observation_key").map_err(|_| data_invalid("raw_account_observation_decode"))?, - observed_at_unix_millis: row.try_get("observed_at_unix_millis").map_err(|_| data_invalid("raw_account_observation_decode"))?, - origin: row.try_get("origin").map_err(|_| data_invalid("raw_account_observation_decode"))?, - protocol: row.try_get("protocol").map_err(|_| data_invalid("raw_account_observation_decode"))?, - provider: row.try_get("provider").map_err(|_| data_invalid("raw_account_observation_decode"))?, - received_at_unix_millis: row.try_get("received_at_unix_millis").map_err(|_| data_invalid("raw_account_observation_decode"))?, - source_payload_hash: row.try_get("source_payload_hash").map_err(|_| data_invalid("raw_account_observation_decode"))?, - source_payload_size_bytes: row.try_get("source_payload_size_bytes").map_err(|_| data_invalid("raw_account_observation_decode"))?, - transaction_signature: row.try_get("transaction_signature").map_err(|_| data_invalid("raw_account_observation_decode"))?, - write_version_text: row.try_get("write_version_text").map_err(|_| data_invalid("raw_account_observation_decode"))?, + account_pubkey, + account_slot_text, + account_state_hash, + acquisition_method, + capture_session_id, + commitment, + endpoint_id, + filter_id, + is_startup, + observation_key, + observed_at_unix_millis, + origin, + protocol, + provider, + received_at_unix_millis, + source_payload_hash, + source_payload_size_bytes, + transaction_signature, + write_version_text, }); } fn raw_account_state_db_row(row: &tokio_postgres::Row) -> std::result::Result { - return std::result::Result::Ok(RawAccountStateDbRow { - data: row.try_get("data").map_err(|_| data_invalid("raw_account_state_decode"))?, - executable: row.try_get("executable").map_err(|_| data_invalid("raw_account_state_decode"))?, - lamports_text: row.try_get("lamports_text").map_err(|_| data_invalid("raw_account_state_decode"))?, - owner: row.try_get("owner").map_err(|_| data_invalid("raw_account_state_decode"))?, - pubkey: row.try_get("pubkey").map_err(|_| data_invalid("raw_account_state_decode"))?, - rent_epoch_text: row.try_get("rent_epoch_text").map_err(|_| data_invalid("raw_account_state_decode"))?, - slot_text: row.try_get("slot_text").map_err(|_| data_invalid("raw_account_state_decode"))?, - state_hash: row.try_get("state_hash").map_err(|_| data_invalid("raw_account_state_decode"))?, - }); + let data: std::vec::Vec = match row.try_get("data") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_state_decode")), + }; + let executable: bool = match row.try_get("executable") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_state_decode")), + }; + let lamports_text: std::string::String = match row.try_get("lamports_text") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_state_decode")), + }; + let owner: std::vec::Vec = match row.try_get("owner") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_state_decode")), + }; + let pubkey: std::vec::Vec = match row.try_get("pubkey") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_state_decode")), + }; + let rent_epoch_text: std::string::String = match row.try_get("rent_epoch_text") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_state_decode")), + }; + let slot_text: std::string::String = match row.try_get("slot_text") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_state_decode")), + }; + let state_hash: std::vec::Vec = match row.try_get("state_hash") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_account_state_decode")), + }; + return std::result::Result::Ok(RawAccountStateDbRow { data, executable, lamports_text, owner, pubkey, rent_epoch_text, slot_text, state_hash }); } fn decode_raw_account_observation_row( diff --git a/crates/ksp-store-postgres-lib/tests/dependency_boundary.rs b/crates/ksp-store-postgres-lib/tests/dependency_boundary.rs index 30b7e85..263fc6b 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: 17 +// version: 18 #![warn(missing_docs)] #![deny(unreachable_pub)] @@ -127,7 +127,6 @@ 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() { - let crate_root = include_str!("../src/lib.rs"); let migration = include_str!("../src/migration.rs"); let schema = include_str!("../src/schema.rs"); let runtime = include_str!("../src/runtime.rs"); diff --git a/deltas/0.3.4/pre.004-fix.001.md b/deltas/0.3.4/pre.004-fix.001.md new file mode 100644 index 0000000..db072b8 --- /dev/null +++ b/deltas/0.3.4/pre.004-fix.001.md @@ -0,0 +1,119 @@ + + + +# Delta `0.3.4-pre.004-fix.001` — conformité Clippy du mapping PostgreSQL account + +## 1. Base requise + +```text +0.3.4-pre.4 +``` + +Le gate opérateur du 2026-08-30 confirme que `pre.004` compile et que les suites ciblées passent, mais `cargo clippy --workspace --all-targets` échoue sur le nouveau mapping `RawAccountState`. + +## 2. Anomalies corrigées + +Dans `src/raw_account.rs`, les deux helpers d'extraction physique utilisent 27 formes : + +```text +row.try_get(...).map_err(...)? +``` + +Le profil workspace nie `clippy::question-mark-used` et `clippy::implicit-return`. Chaque occurrence génère donc deux erreurs Clippy, soit 54 diagnostics. + +Le même gate signale un warning `unused_variables` pour `crate_root` dans `pre_003_v002_schema_is_complete_without_account_trait_or_write_dispatch`. + +## 3. Correctif + +Les 27 extractions sont dépliées en `match` typés : + +```text +Ok(value) -> value +Err(_) -> return Err(DataInvalid) +``` + +La logique et les phases d'erreur restent strictement identiques. Aucune macro, nouveau helper partagé ou nouvelle dépendance n'est introduit. + +La variable `crate_root` réellement inutilisée est supprimée du seul canari concerné. Le canari `pre_004_raw_account_read_sql_and_mapping_remain_backend_private_and_read_only` conserve sa propre lecture de `src/lib.rs`, qui reste utilisée. + +## 4. Version + +Deux fichiers Rust sont modifiés. Conformément à `VER-ID-007/010`, le workspace passe à : + +```text +0.3.4-pre.4.fix.1 +``` + +## 5. Scope inchangé + +Le fix ne modifie pas : + +```text +GET_ACCOUNT_STATE_SQL +GET_ACCOUNT_OBSERVATION_SQL +signatures get_raw_account_state/get_raw_account_observation +modèles ksp-store-api +write SQL account +pagination/cursor KSPA +impl RawAccount* +Store dispatch account +migrations V000/V001/V002 +``` + +Le checksum V002 final reste : + +```text +ff21605ed45f7ab4c0f92bbb692700b4118a9488b04d50a31d259ac59bdb550e +``` + +## 6. Fichiers modifiés + +```text +Cargo.toml +crates/ksp-store-postgres-lib/src/raw_account.rs +crates/ksp-store-postgres-lib/tests/dependency_boundary.rs +docs/plans/025-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT_PLAN.md +docs/validation/021-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT.md +``` + +## 7. Fichier ajouté + +```text +deltas/0.3.4/pre.004-fix.001.md +``` + +## 8. Validations exécutées dans l'environnement d'assemblage + +```text +python3 scripts/audit_rust_workspace_rules.py + General Rust rule audit: clean + Rust export completeness audit: 0 candidate(s) + KSP workspace Rust rule audit: clean + +python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.3.4 + PASS + +grep '?' crates/ksp-store-postgres-lib/src/raw_account.rs + aucune occurrence +``` + +`cargo`, `rustc` et `rustfmt` ne sont pas disponibles dans l'environnement d'assemblage ; aucun gate Cargo post-fix n'est revendiqué PASS ici. + +## 9. Gate opérateur à rejouer + +```bash +cargo fmt --all +python3 scripts/audit_rust_workspace_rules.py +python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.3.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 +``` + +## 10. Verdict + +`pre.004-fix.001` reste strictement dans la responsabilité de `pre.004` : il corrige uniquement la conformité Clippy et l'hygiène du canari du mapping/read slice déjà introduit, sans ajouter de fonctionnalité. 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 451ae60..f29be93 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 maintenant le mapping PostgreSQL privé et les deux lectures `get` account, sans write, pagination ni implémentation de capability. +`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. 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.4 -label = 0.3.4-pre.004 +workspace.package.version = 0.3.4-pre.4.fix.1 +label = 0.3.4-pre.004-fix.001 ``` 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. @@ -615,6 +615,14 @@ Le `RawAccountStateReference` reçu par `get` est vérifié contre le réseau mo La tranche reste strictement read-only : aucun `INSERT`, `UPDATE`, `DELETE`, `ON CONFLICT`, `FOR UPDATE`, cursor ou `list_raw_account_states`. Les quatre `impl RawAccount* for PostgresBackend` restent interdits jusqu'à `pre.008`; `pre.005` et `pre.006` possèdent encore les écritures. +#### `pre.004-fix.001` — conformité Clippy du mapping `Row` + +**Statut : réalisé ; gate opérateur complet à rejouer.** + +Le gate opérateur de `pre.004` confirme `cargo check --workspace`, les suites ciblées `ksp-store-api`, `ksp-store-lib`, `ksp-store-postgres-lib` (52 tests), `ksp-config-lib` et `ksp-store-lib --no-default-features`, tandis que `cargo clippy --workspace --all-targets` rejette les 27 extractions `Row::try_get(...).map_err(...)?` du nouveau module. Chaque occurrence produit conjointement `clippy::question-mark-used` et `clippy::implicit-return`, conformément aux interdictions `RUST-ERR-002/003`. Le même gate signale aussi un `crate_root` inutilisé dans le canari `pre_003_v002_schema_is_complete_without_account_trait_or_write_dispatch`. + +Le correctif déplie les extractions physiques en `match` typés avec retour `DataInvalid` explicite et supprime uniquement la variable de test inutilisée. Aucun SELECT, phase d'erreur, type physique, invariant de mapping, signature de bridge, test fonctionnel, migration ou checksum n'est modifié. La version Cargo devient `0.3.4-pre.4.fix.1` conformément à `VER-ID-007/010`, car deux fichiers Rust sont modifiés. + ### `pre.005` — Acquisition atomique et idempotence **Statut : planifié.** 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 819f3fc..7ed14e1 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. 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` 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. Base : @@ -274,7 +274,7 @@ Le test live account devra être opt-in/ignored, URI stdin, sans environnement n | pre.001 | audit, kbot3, threat model, V002 design, sizing, plan/validation | DONE | | pre.002 | V002 registry + deux tables + PK/FK de base, sans repository | PASS | | pre.003 | contraintes complètes, index, schema compatibility, checksum V002 | PASS | -| pre.004 | mapping privé state/observation + get reads + hostile rows | READY | +| pre.004 | mapping privé state/observation + get reads + hostile rows | RECHECK | | pre.005 | acquisition atomique state+observation + idempotence/conflict | PLANNED | | pre.006 | observation supplémentaire + races/cancellation unitaires | PLANNED | | pre.007 | list RawAccountStateQuery + keyset cursor V1 account | PLANNED | @@ -399,7 +399,7 @@ Gate opérateur de `pre.003-fix.001` du 2026-08-30 : **PASS complet** — audits ## 21. Verdict `pre.004` -Mapping PostgreSQL privé account : **PASS statique ; gate Cargo opérateur à rejouer**. +Mapping PostgreSQL privé account : **tests opérateur PASS ; Clippy `pre.004` FAIL ; `pre.004-fix.001` appliqué statiquement, gate complet à rejouer**. Surface matérialisée : @@ -432,5 +432,20 @@ impl RawAccountObservationRead/Write for PostgresBackend Store dispatch account ``` -Validations exécutées dans l'environnement d'assemblage : audit Rust général/export/workspace **PASS**. Le gate opérateur complet doit rejouer `cargo fmt`, audits, check, Clippy et suites ciblées après application du delta. +Le gate opérateur de `pre.004` du 2026-08-30 établit : `cargo check --workspace` PASS, suites `ksp-store-api`/`ksp-store-lib`/`ksp-store-postgres-lib`/`ksp-config-lib` PASS, `ksp-store-lib --no-default-features` PASS et audits Rust/Markdown PASS ; `cargo clippy --workspace --all-targets` FAIL uniquement sur les 27 usages de `?` du nouveau mapping, doublés par 27 retours implicites de closures, avec un warning séparé de variable `crate_root` inutilisée dans `dependency_boundary.rs`. + +### 21.1. Correctif `pre.004-fix.001` + +Le correctif remplace exclusivement les `map_err(...)?` de `raw_account_observation_db_row` et `raw_account_state_db_row` par des `match` typés avec retours explicites `DataInvalid`, conformément à `RUST-ERR-002/003`. Il retire également la déclaration `crate_root` inutilisée du canari `pre_003_v002_schema_is_complete_without_account_trait_or_write_dispatch`. + +Invariants du fix : + +- aucun SQL ou bind account modifié ; +- aucun invariant de mapping ou phase d'erreur modifié ; +- aucune migration V000/V001/V002 modifiée ; +- checksum V002 final inchangé : `ff21605ed45f7ab4c0f92bbb692700b4118a9488b04d50a31d259ac59bdb550e` ; +- aucun write, cursor, trait account ou dispatch Store ouvert ; +- `workspace.package.version = 0.3.4-pre.4.fix.1`. + +Validations exécutées dans l'environnement d'assemblage du fix : audit Rust général/export/workspace **PASS**, audit Markdown **PASS**, absence de `?` dans `src/raw_account.rs` confirmée. Le gate opérateur complet doit rejouer `cargo fmt`, audits, check, Clippy et suites ciblées après application du delta.