# Delta `0.3.4-pre.006` — observation account supplémentaire + contrat race/cancellation ## 1. Base requise ```text 0.3.4-pre.5 ``` Le gate opérateur fourni le 2026-08-30 pour `pre.005` est entièrement vert : ```text cargo fmt --all PASS audit Rust général / exports / workspace PASS audit Markdown PASS — 239 tables / 142 files cargo check --workspace PASS cargo clippy --workspace --all-targets PASS cargo test -p ksp-store-api PASS cargo test -p ksp-store-lib PASS cargo test -p ksp-store-postgres-lib PASS — 56 unit tests + canaris, live ignored cargo test -p ksp-config-lib PASS — 128 unit tests + ownership/public API cargo check -p ksp-store-lib --no-default-features PASS ``` L'acquisition atomique/idempotente de `pre.005` est donc considérée acquise. ## 2. Objectif Implémenter uniquement l'observation supplémentaire d'un `RawAccountState` déjà durable : ```text network guard avant I/O state reference obligatoire FOR KEY SHARE observation idempotente existante aucun state implicite aucun overwrite transaction unique ``` La pagination account et les quatre implémentations de capabilities restent hors tranche. ## 3. Version Le workspace passe à : ```text 0.3.4-pre.6 ``` ## 4. Observation supplémentaire `record_raw_account_observation` valide d'abord : ```text observation.account.network == backend.network ``` avant `pool.get()`. La transaction exécute ensuite : ```text SELECT 1 FROM ksp_raw_account_states WHERE pubkey = $1 AND slot = $2::TEXT::NUMERIC AND state_hash = $3 FOR KEY SHARE ``` Une référence absente retourne `ReferenceNotFound` avec phase statique `raw_account_observation_reference`. Aucun state n'est créé ou modifié. ## 5. Idempotence / conflit Après le lock de référence, la tranche réutilise `persist_account_observation_row` de `pre.005` : ```text INSERT ... ON CONFLICT (observation_key) DO NOTHING collision -> SELECT ... FOR UPDATE identique -> AlreadyPresent divergent -> Conflict ``` La comparaison porte sur le `RawAccountObservation` complet : référence, provenance et metadata optionnelles incluses. ## 6. Concurrence et cancellation unitaires Les canaris de `pre.006` figent l'ordre : ```text client.transaction() FOR KEY SHARE référence persist_account_observation_row sql_transaction.commit() ``` Le chemin ne détache aucune tâche (`tokio::spawn` absent), ne fuit pas le handle (`std::mem::forget` absent) et ne commit pas avant l'insertion/comparaison observation. Si la future est annulée avant le commit, le `Transaction` local est droppé et la transaction PostgreSQL non committée est rollback. La preuve concurrente réelle avec tâches simultanées et `JoinHandle::abort()` reste explicitement réservée au live `pre.009`. ## 7. Bridge backend `PostgresBackend` expose : ```text record_raw_account_observation( RawAccountObservation, ) -> RawObservationWriteOutcome ``` Le bridge reste construit uniquement avec des types `ksp-store-api`. `RawAccountObservationWrite` n'est pas encore implémenté ; l'ouverture des quatre traits account reste groupée en `pre.008`. ## 8. Scope négatif Restent absents : ```text list_raw_account_states cursor KSPA OFFSET UPDATE / DELETE account ON CONFLICT DO UPDATE impl RawAccountStateRead for PostgresBackend impl RawAccountStateWrite for PostgresBackend impl RawAccountObservationRead for PostgresBackend impl RawAccountObservationWrite for PostgresBackend Store dispatch account ``` Toutes les migrations V000/V001/V002 restent byte-inchangées. Le checksum V002 final reste : ```text ff21605ed45f7ab4c0f92bbb692700b4118a9488b04d50a31d259ac59bdb550e ``` ## 9. Validations d'assemblage ```text python3 scripts/audit_rust_workspace_rules.py PASS python3 scripts/audit_markdown_tables.py ... deltas/0.3.4 PASS migrations V000/V001/V002 byte-identiques à pre.005 PASS scope pre.007+ absent PASS ``` Le gate Cargo/Clippy/tests de `pre.006` doit être rejoué par l'opérateur.