# 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. Base : ```text v0.3.3 ``` Cible release : ```text PostgresBackend = 10/10 capabilities RAW Store = 10/10 capabilities RAW ``` ## 2. Gate base `pre.001` | Preuve | Attendu | État pre.001 | |------------------------------------|------------------|-------------------------------------------------| | workspace.package.version base | 0.3.3 | PASS audit | | V000 checksum | d29068b8…46a1450 | PASS recalcul indépendant | | V001 checksum | 31488cda…d72cc51 | PASS recalcul indépendant / 40 resources | | capabilities transaction physiques | 6 | PASS inventaire source | | capabilities account physiques | 0 | PASS inventaire source ; volontairement pending | | canaries anti-account 0.3.3 | présentes | PASS inventaire tests | | ksp-store-api change nécessaire | non | PASS audit | | SQL V002 créé en pre.001 | non | PASS scope | ## 3. Matrice capability finale | Capability | Base v0.3.3 | Cible 0.3.4 | Preuve finale | |--------------------------------|-------------|-------------|-------------------------------| | RawTransactionRead | PASS | PASS | régression suite 0.3.3 | | RawTransactionWrite | PASS | PASS | régression suite 0.3.3 | | RawTransactionObservationRead | PASS | PASS | régression suite 0.3.3 | | RawTransactionObservationWrite | PASS | PASS | régression suite 0.3.3 | | RawTransactionRetentionRead | PASS | PASS | régression suite 0.3.3 | | RawTransactionRetentionWrite | PASS | PASS | régression suite 0.3.3 | | RawAccountStateRead | PENDING | PASS | tests backend + façade + live | | RawAccountStateWrite | PENDING | PASS | tests backend + façade + live | | RawAccountObservationRead | PENDING | PASS | tests backend + façade + live | | RawAccountObservationWrite | PENDING | PASS | tests backend + façade + live | ### 3.1 Contrat opérationnel détaillé des quatre capabilities account | Capability | Input | Output | Guard réseau | SQL/transaction | Idempotence | Conflit | Absence/référence | Pagination | Error mapping | Preuve live | |----------------------------|---------------------|---------------------------------|------------------------------------------------------------------------------|--------------------------------------------------------|----------------------------------|----------------------------------------|-----------------------------------|---------------------|---------------------------------------------------------------------------|------------------------------------------------| | RawAccountStateRead | reference / query | Option / Page | reference/query network == backend | SELECT ; list keyset | n/a | n/a | get absent -> None | oui, cursor account | WrongNetwork, QueryInvalid, PageLimitUnsupported, DataInvalid, ReadFailed | get exact + list matrix | | RawAccountStateWrite | state + observation | RawAcquisitionWriteOutcome | state + observation networks + exact same reference | transaction state+observation | exact existing -> AlreadyPresent | same full ref divergent -> RawConflict | n/a | non | WrongNetwork, Conflict, DataInvalid, WriteFailed | atomicité + identical/divergent + cancellation | | RawAccountObservationRead | observation_key | Option | aucun input réseau ; backend fournit son network à la référence reconstruite | SELECT by PK | n/a | n/a | absent -> None | non | DataInvalid, ReadFailed | round-trip optional metadata | | RawAccountObservationWrite | observation | RawObservationWriteOutcome | observation.account.network == backend | transaction ; state FOR KEY SHARE + observation insert | exact existing -> AlreadyPresent | same key divergent -> RawConflict | state absent -> ReferenceNotFound | non | WrongNetwork, ReferenceNotFound, Conflict, DataInvalid, WriteFailed | missing ref + collision + concurrence | ## 4. Matrice `RawAccountState` | Champ | Représentation décidée | Validation requise | |------------|------------------------------------------|-----------------------------------------------------------------| | network | binding DB existant `ksp_store_identity` | pas de colonne par ligne ; guard exact avant I/O | | pubkey | BYTEA NOT NULL | `octet_length = 32` ; composant PK | | slot | NUMERIC(20,0) NOT NULL | 0..18446744073709551615 ; aucun narrowing | | state_hash | BYTEA NOT NULL | `octet_length = 32` ; composant PK ; jamais recalculé par Store | | lamports | NUMERIC(20,0) NOT NULL | domaine u64 exact | | owner | BYTEA NOT NULL | `octet_length = 32` | | executable | BOOLEAN NOT NULL | mapping direct | | rent_epoch | NUMERIC(20,0) NOT NULL | domaine u64 exact | | data | BYTEA NOT NULL | 0..=16 MiB ; vide autorisé ; aucun base64 | Points structurants : ```text identity = network API + (pubkey, slot, state_hash) network SQL = singleton ksp_store_identity, pas colonne row PK state = (pubkey, slot, state_hash) state_hash n'est pas une preuve suffisante d'idempotence : comparaison contenu exacte obligatoire empty data = valide ``` ## 5. Matrice observation | Champ | Représentation décidée | Validation requise | |---------------------------|------------------------|--------------------------------------------------| | observation_key | BYTEA NOT NULL | PK family-local ; collision = comparaison exacte | | account.pubkey | BYTEA NOT NULL | FK composite vers state | | account.slot | NUMERIC(20,0) NOT NULL | domaine u64 exact ; FK composite | | account.state_hash | BYTEA NOT NULL | FK composite | | provider | TEXT NOT NULL | 1..128 octets + alphabet logique stable V001 | | protocol | TEXT NOT NULL | idem V001 | | acquisition_method | TEXT NOT NULL | idem V001 | | origin | TEXT NOT NULL | backfill/import/live/repair/replay | | received_at | BIGINT NOT NULL | 0..253402300799999 | | capture_session_id | TEXT NULL | mêmes checks V001 | | commitment | TEXT NULL | mêmes checks V001 | | endpoint_id | TEXT NULL | mêmes checks V001 | | filter_id | TEXT NULL | mêmes checks V001 | | observed_at | BIGINT NULL | borne + `observed_at <= received_at` | | source_payload_hash | BYTEA NULL | NULL ou 32 bytes | | source_payload_size_bytes | BIGINT NULL | 0..=64 MiB ; borne API tient dans BIGINT | | is_startup | BOOLEAN NULL | Yellowstone metadata observation-only | | transaction_signature | BYTEA NULL | NULL ou 64 bytes ; aucune FK transaction | | write_version | NUMERIC(20,0) NULL | NULL ou domaine u64 exact | Invariants additionnels : ```text FK observation -> state composite record_raw_account_observation n'insère jamais le state transaction_signature observation-only, sans FK transaction write_version/is_startup observation-only observation_key family-local unique ``` ## 6. Query/pagination | Élément | Décision | Canari requis | |-----------------|------------------------------------------------|--------------------------------| | ordre ASC | slot, pubkey, state_hash ASC | multi-page sans duplicate/skip | | ordre DESC | slot, pubkey, state_hash DESC | symétrique ASC | | filtre pubkey | égalité optionnelle | cursor lié au filtre | | slot range | inclusive | cursor lié aux deux bornes | | cursor magic | KSPA | rejette KSPT transaction | | cursor version | 1 | rejette version hostile | | cursor longueur | 109 bytes | rejette taille hostile | | cursor domain | KSP/raw-account-state-cursor/v1 | digest family-bound | | cursor digest | network+pubkey filter+range+direction+last key | anti-replay query | | pagination | keyset seulement | aucun OFFSET | | limit | limite physique PostgreSQL uniquement | pas de batch policy | ## 7. Migration/schema V002 | Objet | Décision / état physique | État | |----------------------------------|--------------------------------------------------------------------------|---------------------| | V000 | bytes/checksum inchangés | PASS | | V001 | 40 resources + checksum inchangés | PASS | | V002 logical name | `raw_account_state` | PASS pre.002 | | table state | `ksp_raw_account_states` | PASS pre.002 | | table observation | `ksp_raw_account_observations` | PASS pre.002 | | PK state | `(pubkey,slot,state_hash)` | PASS pre.002 | | PK observation | `(observation_key)` | PASS pre.002 | | FK observation -> state | `(account_pubkey,account_slot,account_state_hash)` -> state composite PK | PASS pre.002 | | contraintes domaine/fixed-width | 26 CHECK + PK/FK, bornes API exactes | PASS pre.003 | | index métier | `(slot,pubkey,state_hash)` non unique / non filtré | PASS pre.003 | | index owner/provider/time/status | aucun prévu | PASS negative scope | | archive/tombstone account | aucune | PASS negative scope | | V002 checksum intermédiaire | `30ac8749…26311f55` sur les 5 resources pre.002 | PROVISIONAL | | V002 checksum final | `ff21605e…bdb550e` sur 32 resources | PASS pre.003 | ## 8. Idempotence et concurrence | Scénario | Résultat attendu | État | |------------------------------------------------------------|-------------------------------------------------------|-------------------| | 2 acquisitions identiques, même ref + même observation key | 1 Inserted/Inserted ; 1 AlreadyPresent/AlreadyPresent | PENDING impl/live | | 2 acquisitions même ref, contenu divergent | un gagnant durable ; autre `RAW_CONFLICT` | PENDING impl/live | | même pubkey+slot, state_hash différents | les deux states sont représentables | PENDING impl/live | | observation key identique, observations divergentes | une observation gagne ; autre `RAW_CONFLICT` | PENDING impl/live | | record observation identique concurrent | 1 Inserted ; 1 AlreadyPresent | PENDING impl/live | | record observation divergente concurrent | 1 Inserted ; 1 `RAW_CONFLICT` | PENDING impl/live | | annulation après insertion state avant observation | aucune moitié durable | PENDING impl/live | Le test de contenu divergent doit comparer les fields complets et ne jamais traiter l'égalité de `state_hash` comme preuve suffisante. ## 9. Threat model | Menace | Mitigation figée | Preuve | |---------------------------------------------------|-----------------------------------------------------------------------|----------------------------| | URI pointe vers DB d’un autre réseau | binding `ksp_store_identity` + guard réseau avant opération | unit + live | | pubkey/hash/signature DB de mauvaise largeur | CHECK SQL + mapping `DataInvalid` statique | unit hostile rows | | slot/lamports/rent_epoch/write_version > i64::MAX | NUMERIC(20,0) + conversion décimale fallible | unit limites u64 | | data account > 16 MiB | admission API + CHECK `octet_length <= 16777216` | unit + live boundary | | data account vide | aucune borne basse SQL ; vide explicitement valide | unit | | observation sans state | FK composite + record pré-vérifie state | unit + live | | échec observation après state insert | une transaction PostgreSQL commune | live cancellation/rollback | | même ref, contenu divergent | ON CONFLICT DO NOTHING + SELECT FOR UPDATE + comparaison exacte | unit + live concurrent | | même pubkey/slot, hashes distincts | PK `(pubkey,slot,state_hash)` | unit + live | | collision observation key | comparaison exacte de tous les champs ; conflict sinon | unit + live | | cursor transaction injecté dans account | magic/domain account distincts | unit cursor | | cursor account rejoué sur autre query | digest lie network/pubkey/range/direction/last key | unit cursor matrix | | ordre même slot ambigu | ordre total `(slot,pubkey,state_hash)` | unit + live pagination | | metadata Yellowstone promue au state | is_startup/transaction_signature/write_version uniquement observation | schema + mapping canary | | transaction_signature impose transaction row | aucune FK vers `ksp_raw_transactions` | schema canary | | SQL/server text fuit | projection PostgresBackendError statique | hardening | | V000/V001 modifiées | checksums figés et canaries exactes | migration tests | | index non justifié | un seul index métier V002 pour query unfiltered | schema completeness | ## 10. Audit historique kbot3 | Élément | Classement | Disposition | |----------------------------------------------------------|------------|-----------------------------------------------------------------------| | unicité observation_key et FK comme invariants physiques | REPRENDRE | garder le principe, avec clés KSP actuelles | | index account+slot pour navigation déterministe | REPRENDRE | réutiliser le principe seulement pour les queries réellement exposées | | NUMERIC(20,0) pour lamports/rent_epoch | REPRENDRE | étendre à slot/write_version pour le domaine u64 complet | | BIGSERIAL/id technique comme identité | REDESSINER | identité durable = `(pubkey, slot, state_hash)` | | pubkey/owner/hash en TEXT/base58 | REDESSINER | BYTEA fixed-width | | account data en base64 + `space` indépendant | REDESSINER | BYTEA complet ; longueur dérivée ; aucun double truth | | observation duplique l’état canonique | REDESSINER | observation référence un state complet séparé | | slot BIGINT/i64 | REDESSINER | NUMERIC(20,0) sans narrowing | | normalisation N1 -> CORE + processing ledger/version | REPORTER | future CORE/jobs/workers ; hors Store RAW 0.3.4 | | batch maximum 1000 / policy de sélection | REPORTER | policy worker/job ; pas pagination Store | | indexes owner/provider/method/time/status | REPORTER | aucune query publique actuelle ne les justifie | | status/error_message persistés dans observation RAW | REJETER | observation réussie uniquement ; erreurs sûres hors ligne métier | | sqlx et Store monolithique | REJETER | backend tokio-postgres séparé derrière ksp-store-lib | | maintenance CASCADE/TRUNCATE comme surface normale | REJETER | aucune destructive capability account | Le pipeline historique `k_sol_obs_account_observations -> k_sol_core_account_states` reste une source de comparaison, pas une architecture cible. La normalisation versionnée et ses caps de batch sont hors scope `0.3.4`. ## 11. Canaries `0.3.3` à faire évoluer À l'ouverture effective de la surface account : ```text ksp-store-lib/tests/dependency_boundary.rs remplacer interdiction RawAccount par exigence des 4 impl + absence types SQL ksp-store-lib/tests/hardening_completeness.rs remplacer inventaire 6 + RawAccount fermé par inventaire exact 10 ksp-store-postgres-lib/tests/dependency_boundary.rs ajouter module raw_account privé et exiger les 4 impl dans runtime bridge conserver V000 et health business-free ksp-store-postgres-lib/tests/hardening_completeness.rs remplacer interdiction RawAccount globale par inventaire exact 10 remplacer l'absence V002/account par checks de surface V002 minimale ``` Les tests historiques nommés `pre_XXX` peuvent conserver leurs noms si le contrat qu'ils vérifient reste valide ; seul leur contenu doit être élargi lorsqu'il fige intentionnellement le scope `0.3.3`. ## 12. Taxonomie d'erreur à prouver | Cas | Code/projection attendue | État | |---------------------------------------------------|------------------------------------------------------|--------------| | mauvais réseau opération | WrongNetwork -> `store.wrong_network` | PENDING impl | | référence absente pour observation supplémentaire | ReferenceNotFound -> `store.raw_reference_not_found` | PENDING impl | | contenu divergent / collision observation | Conflict -> `store_api.raw_conflict` | PENDING impl | | cursor/query invalide | QueryInvalid -> `store_api.raw_query_invalid` | PENDING impl | | page > limite physique PostgreSQL | PageLimitUnsupported | PENDING impl | | row DB incompatible/malformée | DataInvalid | PENDING impl | | échec SELECT | ReadFailed | PENDING impl | | échec INSERT/transaction | WriteFailed | PENDING impl | | V002 absente/incompatible/divergente | MigrationMismatch / MigrationFailed / SchemaNewer | PENDING impl | Aucune nouvelle variante backend n'est justifiée au gate `pre.001`. ## 13. Preuve live finale Le test live account devra être opt-in/ignored, URI stdin, sans environnement ni fuite de secret. Il doit couvrir V002 et rejouer un canari transaction dans la même base afin de prouver la coexistence cross-family. | Scénario live | Obligation | |-----------------------------------------|-------------| | schema update V001 -> V002 | obligatoire | | reopen idempotent | obligatoire | | u64 > i64::MAX / u64::MAX | obligatoire | | data empty / 16 MiB | obligatoire | | identical/divergent acquisition | obligatoire | | same pubkey+slot / distinct hash | obligatoire | | additional observation + missing ref | obligatoire | | collision observation | obligatoire | | keyset filtered/unfiltered ASC/DESC | obligatoire | | cursor hostile/cross-query/cross-family | obligatoire | | concurrency | obligatoire | | cancellation rollback | obligatoire | | RawTransaction regression canary | obligatoire | | cleanup isolation | obligatoire | ## 14. Sizing | Tranche | Objet | État | |---------|-------------------------------------------------------------------|---------| | 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 | PASS | | pre.005 | acquisition atomique state+observation + idempotence/conflict | PASS | | pre.006 | observation supplémentaire + races/cancellation unitaires | RECHECK | | pre.007 | list RawAccountStateQuery + keyset cursor V1 account | PLANNED | | pre.008 | 4 impl backend + 4 dispatch Store + conformance 10/10 | PLANNED | | 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 | | pre.012 | réconciliation docs finale sans rouvrir le code | PLANNED | | pre.013 | publication prep minimale + prompt 0.3.5 | PLANNED | | rel.001 | stabilisation/tag après gate opérateur | PLANNED | ## 15. Negative scope final | Surface | Doit rester absent en 0.3.4 | |--------------------------------------------|-----------------------------| | ksp-store-api redesign | oui | | account retention/archive/purge | oui | | CORE/STRUCTURAL persistence | oui | | workers/jobs/backfills | oui | | Store batch/priorité policy | oui | | owner/provider/time/status queries/indexes | oui | | Transport -> Store coupling | oui | | PostgreSQL types via ksp-store-lib | oui | | nouveau backend | oui | ## 16. Gate opérateur prévu pour clôture technique ```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 cargo test --workspace cargo tree -p ksp-store-lib --edges normal cargo tree -p ksp-store-lib -e features cargo tree -p ksp-store-postgres-lib --edges normal cargo tree --duplicates ``` Les preuves live restent séparées et explicitement `--ignored --nocapture`. ## 17. Verdict `pre.002` Fondation V002 minimale : **PASS statique**. Le registry logique V002 expose exactement cinq resources et aucune surface repository/runtime account. Gate opérateur du 2026-08-30 : audits Rust/Markdown, `cargo check --workspace`, Clippy all-targets, `ksp-store-api`, `ksp-store-lib`, `ksp-config-lib` et `ksp-store-lib --no-default-features` passent. `cargo test -p ksp-store-postgres-lib` exécute 43 tests unitaires et échoue uniquement sur deux canaris obsolètes : - `pre_003_fix_001_newer_history_is_rejected_without_down_migration` attendait `SchemaNewer` avec une fausse version future `2`, désormais occupée par V002 ; - `pre_002_v002_resources_are_exactly_two_tables_plus_base_pk_fk_without_indexes_or_domain_checks` comparait la FK à une chaîne sensible au saut de ligne avant `REFERENCES`. Ces deux défauts appartiennent strictement au couloir `pre.002` et sont corrigés par `pre.002-fix.001`. Ils ne remettent en cause ni les cinq resources V002 ni leur checksum. Contraintes complètes/index/schema compatibility/checksum final : **PASS `pre.003`**. Mapping/lectures account : **PENDING `pre.004`** à ce verdict historique ; writes/pagination/capabilities restent `pre.005+`. ## 18. Correctif `pre.002-fix.001` Le correctif applique deux recalibrages de tests seulement : 1. le canari d'historique construit désormais le préfixe valide V000/V001/V002 puis ajoute une version `3` inconnue, ce qui teste réellement le cas « schéma plus récent » ; 2. le canari de fondation V002 normalise les espaces ASCII du SQL embedded avant ses assertions sémantiques, sans relâcher l'inventaire exact des cinq resources ni les interdictions `CREATE INDEX`, `CHECK`, cascade, `BIGSERIAL`, `BIGINT` ou `TEXT` pour les données account. Invariants du correctif : - aucune ressource sous `migrations/v002_raw_account_state/` modifiée ; - checksum V002 intermédiaire inchangé : `30ac87496f1bb3805d816660891d7eab2127c599a636eb40c17ade5926311f55` ; - V000/V001 inchangées ; - aucun module repository account, dispatch Store ou implémentation `RawAccount*` ouvert ; - `workspace.package.version = 0.3.4-pre.2.fix.1` conformément à `VER-ID-007/010`, car des sources Rust de tests sont modifiées. Gate opérateur du correctif du 2026-08-30 : **PASS complet** — audits Rust/Markdown, workspace check, Clippy all-targets, Store API, Store façade, backend PostgreSQL, Config et `--no-default-features` sont verts. ## 19. Verdict `pre.003` V002 finale : **PASS fonctionnel opérateur ; fix de conformité Rust requis**. Inventaire final : ```text V002 resources = 32 tables = 2 constraints = 29 (2 PK + 1 FK + 26 CHECK) indexes = 1 final checksum = ff21605ed45f7ab4c0f92bbb692700b4118a9488b04d50a31d259ac59bdb550e provisional pre.002= 30ac87496f1bb3805d816660891d7eab2127c599a636eb40c17ade5926311f55 ``` Contraintes matérialisées : exactitude fixed-width des identités/hash/signature, domaines `u64` en `NUMERIC(20,0)`, data account `<= 16 MiB`, provenance logique 1..128 octets/alphabet sûr, origin fermé aux cinq variantes API, timestamps `0..MAX_RAW_UNIX_MILLIS`, `observed_at <= received_at`, hash source 32 bytes et source size `<= 64 MiB`. `is_startup` reste un booléen nullable sans contrainte artificielle. L'unique index métier V002 est non unique et non filtré : ```text (slot, pubkey, state_hash) ``` Aucun index owner/provider/method/time/status n'est ajouté. Compatibilité de prerelease : le checksum provisoire V002 de `pre.002` n'est accepté que lorsque `schema_update=update_if_needed`. Dans ce cas, les resources manquantes sont matérialisées et vérifiées, la compatibilité externe V002 est contrôlée, puis l'entrée d'historique est mise à jour atomiquement vers le checksum final. Avec `schema_update=disabled`, le checksum provisoire est rejeté ; tout checksum non explicitement connu reste `MigrationMismatch`. Le probe d'adoption couvre V001 + V002. V000/V001 et leurs checksums restent inchangés. Aucune surface repository/runtime account n'est ouverte : `src/raw_account.rs` absent, aucune implémentation `RawAccount*` et aucun dispatch Store. Gate opérateur de `pre.003` du 2026-08-30 : `cargo check --workspace`, Clippy all-targets, Store API, Store façade, backend PostgreSQL (45 tests), Config et `--no-default-features` sont **PASS**. L'audit Markdown est **PASS**. L'unique défaut est `RUST-FMT-104` dans `src/migration.rs` : le bloc `const` n'est pas intégralement trié alphabétiquement. ## 20. Correctif `pre.003-fix.001` Le correctif est strictement hygiénique : `HISTORY_LOAD_SQL` est déplacée avant `HISTORY_UPDATE_CHECKSUM_SQL`. Les valeurs, types et usages des constantes restent identiques. Invariants du fix : - aucune ressource V002 modifiée ; - checksum V002 final inchangé : `ff21605ed45f7ab4c0f92bbb692700b4118a9488b04d50a31d259ac59bdb550e` ; - checksum provisoire `pre.002` et bridge de migration inchangés ; - aucune modification de schéma, repository, dispatch ou capability ; - `workspace.package.version = 0.3.4-pre.3.fix.1` conformément à `VER-ID-007/010`. Gate opérateur de `pre.003-fix.001` du 2026-08-30 : **PASS complet** — audits Rust/Markdown, `cargo check --workspace`, Clippy all-targets, Store API, Store façade, backend PostgreSQL (45 tests), Config et `--no-default-features` sont verts. ## 21. Verdict `pre.004` Mapping PostgreSQL privé account : **PASS final via `pre.004-fix.001`**. Le gate initial `pre.004` avait échoué uniquement sur Clippy ; le gate complet du correctif est désormais vert. Surface matérialisée : ```text src/raw_account.rs privé PostgresBackend::get_raw_account_state public bridge backend PostgresBackend::get_raw_account_observation public bridge backend RawAccount* trait impl 0/4 — volontairement fermé write SQL account absent pagination/cursor account absent migrations V000/V001/V002 inchangées ``` Le mapping state couvre `slot`, `lamports` et `rent_epoch` sur tout le domaine `u64` via les projections `::text`, les largeurs exactes 32 bytes de pubkey/owner/state hash, le booléen `executable` et les bytes account complets. `RawAccountState::try_new` fournit la dernière admission backend-agnostic et transforme un payload > 16 MiB en `DataInvalid`; les comptes à data vide restent valides. Le mapping observation reconstruit l'identité account, toute la provenance commune et les metadata optionnelles `is_startup`, `transaction_signature` et `write_version`. `write_version` conserve tout le domaine `u64` via `NUMERIC(20,0)::text`; une signature présente doit faire exactement 64 bytes. L'absence de metadata optionnelle reste `None`, sans valeur inventée. Hostile-row guards déterministes couvrent notamment : largeur pubkey/hash/signature invalide, décimal négatif ou > `u64::MAX`, provenance/origin invalide, ordre temporel impossible et data oversized. Les erreurs restent `DataInvalid`/`ReadFailed`/`WrongNetwork` avec phases statiques et sans écho de valeur hostile. Le garde réseau de `get_raw_account_state` est exécuté avant acquisition d'une connexion. `get_raw_account_observation` reçoit seulement un `RawObservationKey`; la référence reconstruite utilise donc le réseau mono-backend déjà validé à l'ouverture, comme prévu par le contrat. Les canaris de scope prouvent encore l'absence de : ```text INSERT / UPDATE / DELETE account ON CONFLICT / FOR UPDATE account list_raw_account_states / cursor KSPA impl RawAccountStateRead/Write for PostgresBackend impl RawAccountObservationRead/Write for PostgresBackend Store dispatch account ``` 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. Gate opérateur du 2026-08-30 : **PASS complet** — audits Rust/Markdown, workspace check, Clippy all-targets, Store API, Store façade, backend PostgreSQL (52 tests), Config et `--no-default-features` sont verts. ## 22. Verdict `pre.005` Acquisition atomique account : **PASS complet opérateur**. Surface ajoutée : ```text PostgresBackend::persist_raw_account_acquisition INSERT state ON CONFLICT DO NOTHING SELECT state FOR UPDATE sur collision comparaison exacte du state canonique complet INSERT observation ON CONFLICT DO NOTHING SELECT observation FOR UPDATE sur collision comparaison exacte référence + provenance + metadata optionnelles COMMIT unique state + observation ``` Les préconditions `state.reference.network == backend.network`, `observation.account.network == backend.network` et `observation.account == state.reference` sont évaluées avant `pool.get()`. Une divergence de référence ou de contenu est classée `Conflict`; un échec physique d'écriture reste `WriteFailed`, sans texte PostgreSQL conservé. L'idempotence state ne fait aucune confiance exclusive à `state_hash` : un même `(pubkey, slot, state_hash)` n'est `AlreadyPresent` que si `lamports`, `owner`, `executable`, `rent_epoch` et `data` sont identiques. L'idempotence observation compare le modèle `RawAccountObservation` complet, y compris `is_startup`, `transaction_signature`, `write_version` et toute la provenance. La transaction commune garantit qu'une collision divergente sur l'observation après insertion d'un nouveau state ne peut pas laisser ce state durable isolément. Aucun `has_*`, `ON CONFLICT DO UPDATE`, `UPDATE` ou `DELETE` account n'est introduit. Canaries `pre.005` : guard réseau/référence, comparaison state complète, comparaison observation complète, encodage origin fermé, SQL `DO NOTHING + FOR UPDATE`, bridge backend-agnostic et absence des quatre implémentations `RawAccount*`. Le gate opérateur du 2026-08-30 est **PASS complet** : audits Rust/Markdown, `cargo check --workspace`, Clippy all-targets, Store API, Store façade, backend PostgreSQL (56 tests unitaires), Config et `ksp-store-lib --no-default-features` sont verts. Reste après `pre.005` : observation supplémentaire/concurrence ciblée (`pre.006`), pagination/cursor (`pre.007`) et implémentations/dispatch 10/10 (`pre.008`). Les migrations V000/V001/V002 restent inchangées et le checksum V002 final demeure `ff21605ed45f7ab4c0f92bbb692700b4118a9488b04d50a31d259ac59bdb550e`. ## 23. Verdict `pre.006` Observation supplémentaire account : **PASS statique ; gate opérateur complet à rejouer**. Surface ajoutée : ```text PostgresBackend::record_raw_account_observation network guard avant pool.get() SELECT state reference FOR KEY SHARE state absent -> ReferenceNotFound INSERT observation ON CONFLICT DO NOTHING collision -> SELECT observation FOR UPDATE + comparaison exacte COMMIT unique ``` Le state référencé doit être déjà durable : `record_raw_account_observation` ne crée jamais de `RawAccountState` et ne retransmet pas ses bytes. Le lock `FOR KEY SHARE` protège la référence pendant l'insertion observation/FK sans prendre un lock de contenu plus fort que nécessaire. L'idempotence et le conflit réutilisent le chemin `persist_account_observation_row` déjà validé en `pre.005`. Une même clé avec observation identique produit `AlreadyPresent`; une même clé avec contenu/provenance/metadata divergents produit `Conflict`. Le réseau de l'observation est rejeté avant toute acquisition de connexion lorsqu'il diffère du backend. La preuve de race/cancellation de cette tranche est volontairement unitaire/structurelle : les canaris imposent l'ordre `transaction -> FOR KEY SHARE -> persist/compare -> commit`, l'absence de commit anticipé et l'absence de tâche détachée ou de fuite volontaire du handle transactionnel. Une cancellation avant commit droppe donc le `Transaction` local et laisse le rollback à PostgreSQL. Les scénarios de vraies tâches concurrentes et `JoinHandle::abort()` restent au test live `pre.009`. 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`.