# Delta `0.3.8-pre.004` — inspection PostgreSQL RawTransaction et dispatch Store ## Base requise Base directe attendue : ```text 0.3.8-pre.003-fix.001 workspace.package.version = 0.3.8-pre.3.fix.1 ``` Le gate opérateur fourni sur cette base est entièrement vert : audits Rust/Markdown, `cargo check --workspace`, `cargo clippy --workspace --all-targets`, `ksp-store-api`, `ksp-store-lib`, `ksp-store-lib --no-default-features` et `ksp-app-store-desk`. La livraison est : ```text 0.3.8-pre.004 workspace.package.version = 0.3.8-pre.4 commit = v0.3.8-pre.004 tag = aucun ``` ## Objectif Implémenter le premier vertical slice physique de la primitive backend-neutral créée en `pre.003` : ```text RawTransactionInspectionRead ksp-store-postgres-lib -> ksp-store-lib::Store ``` `RawAccountStateInspectionRead` reste volontairement non implémenté jusqu'à `pre.005`. Aucun SQL de migration, aucune UI Store Desk et aucun branchement DataTables `serverSide` ne sont introduits. ## SQL d'inspection Deux statements privés existent, un par direction canonique. Chaque appel exécute une seule instruction SQL comprenant : ```text filtered_count conditional total_count LATERAL page ORDER BY slot, signature LIMIT/OFFSET ``` Cette forme garantit que counts et page sont issus du même snapshot de statement PostgreSQL. Lorsque le range slot est absent, `filtered_count` est réutilisé comme `total_count`; aucun second `COUNT(*)` n'est exécuté. Avec un filtre slot, `total_items` reste le total exact de la famille transaction dans le Store/network et `filtered_items` représente le sous-ensemble filtré. Les tombstones `Purged` sont inclus dans l'inspection afin que l'outil opérateur puisse voir l'état de rétention durable. Le listage cursor/keyset historique reste inchangé et continue d'exclure `Purged`. ## Pas de transfert payload / pas de N+1 Le statement de page ne sélectionne jamais : ```text transaction_row.payload archive_row.payload ``` Il projette seulement : ```text OCTET_LENGTH(transaction_row.payload) OCTET_LENGTH(archive_row.payload) hot_payload_present archive_payload_present ``` Le summary final est donc construit en une seule requête, sans chargement individuel par ligne et sans gros bytes IPC/runtime. Les marqueurs de présence permettent en outre de rejeter une forme PostgreSQL incohérente (`Full` avec archive résiduelle, `Archived` avec hot payload, `Purged` avec payload ou block time résiduel). ## Limites physiques et offset `RawPageLimit` reste sans plafond politique côté API. Le backend convertit avant I/O : ```text limit -> i64 PostgreSQL offset -> i64 PostgreSQL ``` Un limit non représentable renvoie `PageLimitUnsupported`; un offset non représentable renvoie `QueryInvalid`. Ces validations sont effectuées avant `pool.get()`. Une page vide causée par un offset supérieur à `filtered_items` reste valide et retourne les counts exacts avec `items = []`. ## Façade Store `PostgresBackend` et `Store` implémentent désormais `RawTransactionInspectionRead`. Le dispatch Store applique d'abord le même guard network que les autres opérations network-scoped. L'inventaire physique/facade passe donc temporairement de 10 à 11 capabilities RAW implémentées : ```text 10 historiques + RawTransactionInspectionRead = 11 ``` Les deux inventaires restent strictement symétriques et `RawAccountStateInspectionRead` est explicitement absent jusqu'à `pre.005`. ## Cursor/keyset préservé Les statements historiques `LIST_TRANSACTIONS_ASC_SQL` / `LIST_TRANSACTIONS_DESC_SQL` restent contrôlés séparément : ```text keyset (slot, signature) LIMIT + continuation probe aucun OFFSET retention_state <> 'purged' ``` L'exception `OFFSET` est donc locale au chemin inspection random-access et ne modifie pas le contrat machine/replay/backfill. ## Tests et canaris Le delta ajoute/actualise les preuves suivantes : - conversion limit/offset hostile avant I/O ; - décodage summary `Full` / `Archived` / `Purged` sans bytes ; - rejet des résidus physiques incompatibles avec l'état de rétention ; - canari SQL séparant explicitement keyset sans OFFSET et inspection avec OFFSET ; - preuve que l'inspection utilise counts, LATERAL page et `OCTET_LENGTH` sans sélectionner les payloads ; - inventaires exacts 11/11 côté backend et façade ; - live proof PostgreSQL opt-in : filtre slot, counts, asc/desc, offset et page vide profonde. ## Fichiers ajoutés ```text deltas/0.3.8/pre.004.md ``` ## Fichiers modifiés ```text Cargo.toml 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/error.rs crates/ksp-store-postgres-lib/src/lib.rs crates/ksp-store-postgres-lib/src/raw_transaction.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/postgres_raw_transaction_live.rs crates/ksp-store-postgres-lib/tests/public_api.rs crates/ksp-store-postgres-lib/unit_tests/raw_transaction.rs docs/plans/029-V0_3_8_STORE_DESK_PLAN.md docs/validation/025-V0_3_8_STORE_DESK.md ``` ## Fichiers supprimés ```text aucun ``` ## Validations d'assemblage Contrôles exécutés avant emballage : ```text General Rust rule audit: clean Rust export completeness audit: 0 candidate(s) KSP workspace Rust rule audit: clean Markdown table audit: clean (314 table(s), 696 file(s)) pre.004 structural contract audit: clean workspace.package.version: 0.3.8-pre.4 Store Raw capability implementations: 11 PostgreSQL Raw capability implementations: 11 RawAccountStateInspectionRead implementations: 0 RawTransaction keyset OFFSET occurrences: 0 RawTransaction inspection OFFSET occurrences: 2 delta scope: 1 ajout / 16 modifications / 0 suppression ``` Le sandbox d'assemblage ne dispose pas de `cargo`/`rustfmt`; aucun gate Cargo de `pre.004` n'est donc revendiqué localement. Le gate opérateur requis est : ```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 cargo check --workspace cargo clippy --workspace --all-targets cargo test -p ksp-store-postgres-lib cargo test -p ksp-store-lib cargo check -p ksp-store-lib --no-default-features cargo test -p ksp-store-api ``` Le live proof reste opt-in et doit être exécuté uniquement sur une base PostgreSQL dédiée vide si une preuve réelle est souhaitée : ```bash cargo test -p ksp-store-postgres-lib --test postgres_raw_transaction_live -- --ignored --nocapture ```