# 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` à `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 ; `pre.008-fix.001` corrige son unique canari mal réconcilié et son gate opérateur complet est PASS. `pre.009` matérialise la preuve PostgreSQL live account, la concurrence/cancellation réelle et la coexistence RawTransaction sans modifier les migrations ; son gate standard et son live account sur PostgreSQL 17 sont PASS. `pre.010` ferme les canaris cross-family sans modifier le runtime et son gate opérateur complet est PASS. `pre.011` ouvre le gate technique final, le replay live ciblé et les graphes Cargo sans nouveau développement. 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 | PASS | | pre.007 | list RawAccountStateQuery + keyset cursor V1 account | PASS | | pre.008 | 4 impl backend + 4 dispatch Store + conformance 10/10 | PASS | | pre.009 | preuve PostgreSQL live account + coexistence RawTransaction | CURRENT | | 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 complet opérateur**. 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`. ## 24. Verdict `pre.007` 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é : ```text ASC (slot ASC, pubkey ASC, state_hash ASC) DESC (slot DESC, pubkey DESC, state_hash DESC) ``` Deux familles de SELECT sont utilisées pour chaque direction : sans filtre pubkey, l'index V002 `(slot, pubkey, state_hash)` porte la navigation ; avec filtre pubkey, la PK `(pubkey, slot, state_hash)` porte le préfixe account puis le même ordre total. Les predicates de continuation utilisent exclusivement les comparaisons tuple `>` / `<`. Aucun `OFFSET`, batch maximum, priorité, clamp ou policy worker n'est introduit. Le cursor account V1 est fixe à 109 bytes : ```text 0..4 magic KSPA 4 version 1 5..13 last_slot u64 big-endian 13..45 last_pubkey 32 bytes 45..77 last_state_hash 32 bytes 77..109 SHA-256 binding digest ``` Le digest utilise le domaine `KSP/raw-account-state-cursor/v1` et lie le réseau, le filtre pubkey optionnel, la direction, les bornes de slot et la dernière clé complète. Les canaris couvrent round-trip, taille/magic/version/digest hostiles, replay réseau/pubkey/direction/plage et replay cross-family depuis `KSPT`. La page conserve la sémantique Store existante : `LIMIT + 1` détecte la continuation, le backend n'impose aucun maximum fonctionnel KSP et expose seulement la borne PostgreSQL `9_223_372_036_854_775_806` nécessaire pour convertir `requested + 1` en `i64`. Les rows de liste revalident pubkey/hash à 32 bytes et `slot` via decimal exact `u64`, sans narrowing. Surface runtime ajoutée : ```text PostgresBackend::list_raw_account_states( &RawAccountStateQuery, ) -> RawPage ``` Les quatre `impl RawAccount* for PostgresBackend` et tout dispatch account dans `ksp-store-lib::Store` restent absents jusqu'à `pre.008`. Les migrations V000/V001/V002 restent byte-inchangées et le checksum V002 demeure `ff21605ed45f7ab4c0f92bbb692700b4118a9488b04d50a31d259ac59bdb550e`. ## 25. Verdict `pre.007-fix.001` 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 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 initial en échec ciblé sur le canari `dependency_boundary`**. 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`. ## 27. Verdict `pre.008-fix.001` Le gate opérateur de `pre.008` confirme que les audits Rust/Markdown, le workspace check, `ksp-store-api`, `ksp-store-lib`, `ksp-config-lib` et `--no-default-features` passent. L'unique blocage est la compilation de `ksp-store-postgres-lib/tests/dependency_boundary.rs` : `runtime` est référencé dans le canari d'ownership `pre.005` sans y être déclaré, tandis qu'une déclaration devenue inutilisée subsiste dans le canari V002 `pre.003`. Le fix déplace uniquement : ```text let runtime = include_str!("../src/runtime.rs"); ``` vers le canari qui le consomme effectivement. Les assertions de placement 10/10 restent inchangées : les implémentations account demeurent uniques dans `runtime.rs`, absentes de `raw_account.rs`, et les couches migration/schema restent sans capability métier. Aucun fichier de production, SQL runtime ou migration V000/V001/V002 n'est modifié. Le checksum V002 reste `ff21605ed45f7ab4c0f92bbb692700b4118a9488b04d50a31d259ac59bdb550e`. Le gate opérateur complet du 2026-08-30 sur `0.3.4-pre.8.fix.1` est **PASS** : audits Rust/Markdown, workspace check, Clippy all-targets, Store API, Store façade, backend PostgreSQL (63 tests unitaires + 13 dependency + 7 hardening + 13 public), Config et `--no-default-features` sont verts. ## 28. Verdict `pre.009` Preuve PostgreSQL live `RawAccountState` : **PASS complet opérateur**. Nouveau test : ```text crates/ksp-store-postgres-lib/tests/postgres_raw_account_live.rs #[ignore = "opt-in real PostgreSQL RawAccountState proof; reads one dedicated URI from stdin"] ``` Invariants d'exploitation : ```text URI dédiée lue depuis stdin uniquement aucune lecture d'environnement aucun affichage de l'URI refus si une table KSP managée existe déjà PostgreSQL >= 15 cleanup uniquement après preuve d'absence initiale reopen health migration_version = 2 ``` Matrice matérialisée : | Scénario | Preuve `pre.009` | |--------------------------------------------|--------------------------------------| | bootstrap/reopen V002 | oui | | schema_autoupdate index account | oui | | slot/lamports/rent_epoch/write_version u64 | oui, `u64::MAX` | | data account vide | oui | | data account 16 MiB exact | oui | | acquisition insert/idempotence | oui | | même référence / contenu divergent | `Conflict` | | même pubkey+slot / hashes distincts | oui | | collision observation + rollback state | oui | | observation supplémentaire identique | `AlreadyPresent` | | observation supplémentaire divergente | `Conflict` | | référence state absente | `ReferenceNotFound` | | metadata Yellowstone | round-trip exact | | pagination ASC/DESC | oui | | filtre pubkey | oui | | cursor cross-query / digest hostile | `QueryInvalid` | | cursor cross-family `KSPT` | `QueryInvalid` | | concurrence identique | Inserted + AlreadyPresent | | concurrence divergente | Inserted + Conflict | | cancellation réelle | `JoinHandle::abort()` + state absent | | coexistence RawTransaction | oui | | cleanup isolation | oui | Le test `postgres_raw_transaction_live.rs` est ajusté uniquement sur son inventaire de schéma managé : il probe et supprime désormais aussi `ksp_raw_account_states` et `ksp_raw_account_observations`, nécessaires depuis V002. Aucun scénario métier RawTransaction n'est modifié. Le gate standard du 2026-08-30 est PASS : audits Rust/Markdown, workspace check, Clippy all-targets, Store API, Store façade, backend PostgreSQL, Config et `--no-default-features` sont verts. Le live account est ensuite PASS sur PostgreSQL 17 en 2,72 s, avec URI dédiée lue depuis stdin sans écho. Les migrations V000/V001/V002 restent byte-inchangées ; le checksum V002 reste : ```text ff21605ed45f7ab4c0f92bbb692700b4118a9488b04d50a31d259ac59bdb550e ``` ## 29. Verdict `pre.010` Hardening et complétude cross-family : **PASS opérateur complet**. Aucun fichier de production n'est modifié. Les nouveaux canaris backend vérifient que le SQL account reste limité aux lectures, inserts idempotents, locks et pagination keyset ; ils interdisent `UPDATE`, `DELETE`, `TRUNCATE`, `OFFSET`, `ON CONFLICT DO UPDATE` et toute policy worker. Le cursor account reste `KSPA`, domaine `KSP/raw-account-state-cursor/v1`, 109 bytes, distinct de `KSPT`. Le registre V002 est figé à exactement 32 resources : 2 tables, 2 PK, 1 FK observation->state, 26 CHECK et 1 index `(slot,pubkey,state_hash)` non filtré. Les canaris imposent l'absence de `ksp_raw_transactions` et `ksp_raw_transaction_observations` dans V002. Le CHECK de `transaction_signature` impose seulement 64 bytes lorsqu'elle est présente et aucune FK transaction. La façade compare directement son inventaire de traits RAW avec celui de `PostgresBackend` : les deux ensembles doivent être strictement identiques et contenir exactement dix capabilities. Aucune capability account de retention, delete ou compaction n'est admise. Les fichiers `ksp-store-api`, `ksp-store-lib/src`, `ksp-store-postgres-lib/src`, les tests live et les migrations V000/V001/V002 restent inchangés. Le checksum V002 demeure : ```text ff21605ed45f7ab4c0f92bbb692700b4118a9488b04d50a31d259ac59bdb550e ``` Le gate opérateur du 2026-08-30 est PASS : audits Rust/Markdown, workspace check, Clippy all-targets, Store API, Store façade, backend PostgreSQL, Config et `--no-default-features` sont verts. Les canaris supplémentaires de `pre.010` passent : inventaire RAW exact 10/10 des deux côtés, V002 exact à 32 resources, SQL account non destructif/keyset-only et séparation cross-family maintenue. ## 30. Verdict `pre.011` Gate technique final : **PASS**. Cette tranche ne modifie aucun code de production, test fonctionnel, SQL, migration, Config runtime, manifest de crate, dépendance runtime, README/USAGE, CHANGELOG/ROADMAP ou prompt suivant. Elle synchronise uniquement la version workspace et documente le gate final. Matrice opérateur attendue : ```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-lib --no-default-features 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 ``` Replay PostgreSQL réel ciblé : ```bash read -rsp "Dedicated PostgreSQL URI: " KSP_PG_TEST_URI; echo printf '%s\n' "$KSP_PG_TEST_URI" | cargo test -p ksp-store-postgres-lib --test postgres_raw_account_live -- --ignored --nocapture --test-threads=1 unset KSP_PG_TEST_URI ``` Le live account est la preuve finale ciblée : il couvre bootstrap/reopen V002, bornes u64/data, idempotence/conflits, pagination/cursors, concurrence/cancellation et coexistence `RawTransaction`. Les lives foundation et RawTransaction séparés ne sont pas requis par `pre.011`. Le replay opérateur de clôture confirme : ```text cargo test -p ksp-store-lib --no-default-features PASS cargo test --workspace PASS cargo tree façade/backend/features/duplicates exécutés postgres_raw_account_live PASS — PostgreSQL 17, 3.00 s ``` Les graphes ne révèlent pas de reverse edge Store ni de dépendance PostgreSQL lorsque la feature façade correspondante est désactivée. Les doublons Cargo observés appartiennent au workspace transverse et ne matérialisent aucune duplication propre à la verticale Store nécessitant un correctif. Le live final lit l'URI dédiée depuis stdin sans l'écho. Le couloir technique est donc fermé ; aucun `pre.011-fix.NNN` n'est requis. ## 31. Verdict `pre.012` Réconciliation documentaire finale : **matérialisée ; gate documentaire opérateur à exécuter**. Documents durables réconciliés : ```text crates/ksp-store-lib/README.md crates/ksp-store-lib/USAGE.md crates/ksp-store-postgres-lib/README.md crates/ksp-store-postgres-lib/USAGE.md docs/plans/025-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT_PLAN.md docs/validation/021-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT.md ``` La documentation décrit maintenant la surface réellement validée : dix capabilities RAW exactes, quatre capabilities `RawAccount*`, acquisition account atomique/idempotente, observation supplémentaire reference-guarded, pagination keyset `(slot,pubkey,state_hash)` avec cursor `KSPA`, séparation stricte avec `RawTransaction` et absence de rétention/destruction account. Relus et volontairement inchangés : ```text README.md racine docs/architecture/003-COMPONENT_CONTRACTS.md docs/architecture/004-COMPONENT_INVENTORY.md docs/architecture/005-DEPENDENCY_GRAPH.md docs/architecture/008-DATA_MATERIALIZATION_AND_STORE.md ksp-config-lib README/USAGE ksp-store-api — aucun README/USAGE à créer artificiellement ``` `CHANGELOG.md`, `ROADMAP.md` et le prompt de démarrage `0.3.5` restent hors de cette tranche et appartiennent à `pre.013`. Aucun `src/**`, test, Config, schema, migration ou dépendance n'est modifié.