# Delta `0.3.3-pre.003-fix.001` — Store/PostgreSQL schema compatibility hardening ## 1. Base et objectif Ce correctif s'applique **par-dessus le commit `0.3.3-pre.003` déjà réalisé**. Il ne réécrit ni ne remplace ce commit dans l'historique. Le gate opérateur de `pre.002` est vert. Le gate opérateur de `pre.003` n'a pas été fourni avant l'ouverture de ce fix ; aucune commande Cargo du fix n'est donc déclarée PASS dans ce document. Objectif du fix : conserver les bonnes garanties fonctionnelles historiques d'inspection/réconciliation du schéma PostgreSQL sans reprendre l'architecture kbot3, et corriger V001 avant l'ouverture du repository `RawTransaction` de `pre.004`. Version workspace après application : ```text 0.3.3-pre.3.fix.1 ``` Identifiant de livraison : ```text 0.3.3-pre.003-fix.001 ``` ## 2. Décisions corrigées ### 2.1 Migration logique vs ressources physiques V000 et V001 restent des **migrations logiques** versionnées. Les objets physiques ne deviennent pas chacun une version de migration. Arborescence retenue : ```text crates/ksp-store-postgres-lib/migrations/ v000_bootstrap/ tables/ 001_ksp_store_schema_migrations.sql v001_raw_transaction/ tables/ 001_ksp_store_identity.sql 002_ksp_raw_transactions.sql 003_ksp_raw_transaction_observations.sql 004_ksp_raw_transaction_archive_payloads.sql constraints/ 001_...sql -> 035_...sql indexes/ 001_ix_ksp_raw_transactions_slot_signature.sql ``` V001 contient exactement : ```text 4 tables 35 contraintes 1 index 40 ressources embedded ``` V000 est seulement déplacée : ses bytes restent strictement identiques. ### 2.2 Checksums V000 historique : ```text d29068b8c13b9dc0cc9ef6aaadd0fa12d41e0fe4c56541a1118c4bfc846a1450 ``` V001 multi-ressources : ```text 31488cda2f08f3f46c4cdbdbb6c18c243662fada02eac4487040c8735d72cc51 ``` Le checksum V001 est calculé sur le préfixe de format du registre puis, dans l'ordre embedded, sur l'identifiant relatif et les bytes exacts de chaque ressource. Le checksum monolithique `pre.003` n'est pas réutilisé. Une base ayant réellement enregistré l'ancien checksum V001 du commit `pre.003` n'est jamais réécrite silencieusement : elle doit être réinitialisée si elle est jetable ou réconciliée manuellement. ### 2.3 `IF NOT EXISTS` n'est pas une preuve de compatibilité Les ressources utilisent `CREATE ... IF NOT EXISTS`, `ADD COLUMN IF NOT EXISTS` ou des guards catalogue lorsque cela permet une création additive idempotente. Après et avant mutation, le backend introspecte néanmoins le catalogue PostgreSQL. Chaque ressource est classée : ```text Compatible Missing Incompatible ``` Un objet préexistant du bon nom mais de mauvaise définition est donc refusé. ### 2.4 Contrat physique vérifié Le module backend-private `schema` contrôle notamment : - tables gérées et nature `BASE TABLE` ; - colonnes obligatoires, type, nullabilité et absence d'identity/generated sur le contrat KSP ; - précision/scale `NUMERIC(20,0)` du slot ; - PK, FK et CHECK ; - définition canonique des contraintes à partir de leur propre ressource SQL ; - index partiel `(slot, signature)` hors `purged` ; - RLS ; - triggers utilisateur actifs ; - rewrite rules ; - contraintes/indexes uniques externes susceptibles de modifier la sémantique de write. Les marqueurs COMMENT ne servent pas de preuve de compatibilité et ne sont pas requis. ### 2.5 Extensions externes compatibles KSP ne requiert pas une égalité byte-for-byte du schéma complet avec son propre DDL. Les extensions externes qui sont prouvées non bloquantes restent admises. Le correctif tolère notamment : - une colonne externe nullable, sans default, identity ni generated expression ; - un index externe non unique ; - une contrainte attendue physiquement équivalente même si son nom diffère. Le correctif bloque conservativement les extensions susceptibles de modifier/contraindre les writes KSP lorsqu'il ne peut pas en prouver l'innocuité : mauvais type/nullabilité sur une colonne KSP, colonne externe write-blocking, contrainte non équivalente, unique index autonome, trigger actif, rule ou RLS. ### 2.6 Politiques `schema_autocreate` / `schema_autoupdate` `std.store` passe en format V2 et sépare : ```text schema_autocreate schema_autoupdate ``` `schema_autocreate` autorise l'initialisation ou l'adoption contrôlée d'une base sans metadata KSP. `schema_autoupdate` autorise les migrations pending et les réparations additives sûres de ressources manquantes sur un schéma KSP existant. Une incompatibilité exigeant une mutation destructive, ambiguë ou non prouvée sûre reste bloquante même avec `schema_autoupdate=true`; le backend émet uniquement une phase/resource sûre pour permettre l'intervention manuelle. Le format V1 reste lisible : `auto_migrate` est mappé vers les deux politiques. Les constructeurs Rust historiques restent également disponibles et mappent leur booléen vers les deux politiques. ### 2.7 Identité réseau Le binding V001 reste sous la même transaction et le même advisory lock. Une `ksp_store_identity` absente alors que V001 est déjà enregistrée n'est jamais reconstruite automatiquement, même avec `schema_autoupdate=true`, afin d'interdire tout rebind réseau silencieux. ## 3. Suppressions explicites après application de l'overlay L'extraction d'un zip n'efface pas les anciens fichiers. Après extraction du delta à la racine du dépôt, supprimer explicitement : ```bash rm crates/ksp-store-postgres-lib/migrations/V000__bootstrap.sql rm crates/ksp-store-postgres-lib/migrations/V001__raw_transaction.sql ``` Fichiers supprimés : - `crates/ksp-store-postgres-lib/migrations/V000__bootstrap.sql` - `crates/ksp-store-postgres-lib/migrations/V001__raw_transaction.sql` ## 4. Fichiers modifiés - `Cargo.toml` - `config/examples/std.store.example.json` - `config/schemas/std.store.schema.json` - `config/std.store.json` - `crates/ksp-config-lib/src/store.rs` - `crates/ksp-config-lib/unit_tests/fixtures/std.store.json` - `crates/ksp-config-lib/unit_tests/store.rs` - `crates/ksp-store-lib/src/settings.rs` - `crates/ksp-store-lib/src/store.rs` - `crates/ksp-store-lib/unit_tests/settings.rs` - `crates/ksp-store-postgres-lib/src/lib.rs` - `crates/ksp-store-postgres-lib/src/migration.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/unit_tests/migration.rs` - `docs/plans/024-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION_PLAN.md` - `docs/validation/020-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION.md` ## 5. Fichiers ajoutés - `crates/ksp-store-postgres-lib/migrations/v000_bootstrap/tables/001_ksp_store_schema_migrations.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/001_pk_ksp_store_identity.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/002_ck_ksp_store_identity_singleton.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/003_ck_ksp_store_identity_network.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/004_pk_ksp_raw_transactions.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/005_ck_ksp_raw_transactions_signature.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/006_ck_ksp_raw_transactions_slot.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/007_ck_ksp_raw_transactions_block_time.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/008_ck_ksp_raw_transactions_format_id.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/009_ck_ksp_raw_transactions_format_version.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/010_ck_ksp_raw_transactions_content_hash.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/011_ck_ksp_raw_transactions_payload.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/012_ck_ksp_raw_transactions_retention_state.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/013_ck_ksp_raw_transactions_payload_state.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/014_ck_ksp_raw_transactions_purged_block_time.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/015_pk_ksp_raw_transaction_observations.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/016_fk_ksp_raw_transaction_observations_transaction.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/017_ck_ksp_raw_transaction_observations_key.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/018_ck_ksp_raw_transaction_observations_signature.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/019_ck_ksp_raw_transaction_observations_provider.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/020_ck_ksp_raw_transaction_observations_protocol.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/021_ck_ksp_raw_transaction_observations_method.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/022_ck_ksp_raw_transaction_observations_origin.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/023_ck_ksp_raw_transaction_observations_received_at.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/024_ck_ksp_raw_transaction_observations_capture_session.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/025_ck_ksp_raw_transaction_observations_commitment.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/026_ck_ksp_raw_transaction_observations_endpoint.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/027_ck_ksp_raw_transaction_observations_filter.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/028_ck_ksp_raw_transaction_observations_observed_at.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/029_ck_ksp_raw_transaction_observations_time_order.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/030_ck_ksp_raw_transaction_observations_source_hash.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/031_ck_ksp_raw_transaction_observations_source_size.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/032_pk_ksp_raw_transaction_archive_payloads.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/033_fk_ksp_raw_transaction_archive_payloads_transaction.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/034_ck_ksp_raw_transaction_archive_payloads_signature.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/constraints/035_ck_ksp_raw_transaction_archive_payloads_payload.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/indexes/001_ix_ksp_raw_transactions_slot_signature.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/tables/001_ksp_store_identity.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/tables/002_ksp_raw_transactions.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/tables/003_ksp_raw_transaction_observations.sql` - `crates/ksp-store-postgres-lib/migrations/v001_raw_transaction/tables/004_ksp_raw_transaction_archive_payloads.sql` - `crates/ksp-store-postgres-lib/src/schema.rs` - `crates/ksp-store-postgres-lib/unit_tests/schema.rs` - `deltas/0.3.3/pre.003-fix.001.md` ## 6. Validation de génération Les validations réalisables dans l'environnement de génération doivent être propres avant publication de l'archive : ```text 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.3 validation JSON/JSON Schema std.store V1+V2 inventaire/checksum migration V000/V001 ``` L'environnement de génération ne possède pas `cargo`/`rustfmt`. Les gates Cargo ci-dessous restent donc **NON EXÉCUTÉS** jusqu'au passage opérateur. ## 7. Gate opérateur demandé Après extraction de l'overlay, suppression explicite des deux fichiers monolithiques puis application du formatage : ```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.3 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 ``` Canaris de suppression : ```bash test ! -e crates/ksp-store-postgres-lib/migrations/V000__bootstrap.sql test ! -e crates/ksp-store-postgres-lib/migrations/V001__raw_transaction.sql ``` Le vrai PostgreSQL live du contrat V001, des dérives et des réparations reste volontairement réservé au gate live prévu plus tard dans `0.3.3`; ce fix ne le présente pas comme PASS. ## 8. Suite Après gate opérateur vert de `pre.003-fix.001`, reprendre le séquencement avec : ```text 0.3.3-pre.004 — mapping SQL privé + lectures RawTransaction/observation/rétention/tombstone ```