Files
khadhroony-solana-project/deltas/0.3.16/pre.008.md
2026-09-22 07:40:23 +02:00

237 lines
8.2 KiB
Markdown

<!-- file: deltas/0.3.16/pre.008.md -->
<!-- version: 1 -->
# Delta `0.3.16-pre.008` — hardening technique de la vertical slice variantes/conflits
## Base requise
```text
0.3.16-pre.007-fix.003 appliqué
workspace.package.version = 0.3.16-pre.7.fix.3
```
Le gate opérateur de `pre.007-fix.003` est entièrement propre : format, audits Rust/Markdown, `cargo check --workspace`, Clippy workspace/all-targets/all-features, Store API, Store façade, PostgreSQL, Job Backfill et l'intégralité des suites ciblées du Worker passent. `pre.007` est donc fermé avant ouverture de cette tranche.
## Objectif
`pre.008` est la tranche de hardening technique finale de la vertical slice `0.3.16` :
```text
concurrence même identité
rollback transactionnel promotion/conflit
cohérence projection V001 / selector V003
conservation du canonique en conflit
régressions Job Backfill / Worker
preuves live PostgreSQL opt-in actualisées
gate workspace
```
Aucune nouvelle capacité fonctionnelle, résolution opérateur, retry Store, reconnexion Transport ou Store Desk n'est introduite.
## Version
```text
workspace.package.version = 0.3.16-pre.8
```
## Hardening PostgreSQL statique
Les canaris V003 verrouillent désormais explicitement deux invariants supplémentaires :
1. la branche `ActiveConflict` persiste/réutilise la variante entrante et ouvre/complète le conflict case, mais n'appelle jamais la promotion canonique et ne touche ni `UPDATE_CANONICAL_TRANSACTION_PROJECTION_SQL` ni `UPDATE_TRANSACTION_VARIANT_SELECTOR_SQL` ;
2. les helpers de promotion et de conflict case reçoivent la transaction PostgreSQL de l'acquisition et ne peuvent jamais effectuer leur propre `COMMIT`.
Les gardes existantes restent exigées :
```text
identity row -> FOR UPDATE
selector promotion -> expected canonical_variant_id + expected canonical_revision
conflict update -> expected revision
```
Ainsi, selector, projection V001, ledger V003, conflict case, observation et mapping restent bornés par la transaction d'acquisition externe.
## Preuve live PostgreSQL actualisée
Le test opt-in `postgres_raw_transaction_live.rs` est réaligné sur la sémantique `pre.007`.
Le scénario de divergence concurrente d'une même identité attend désormais :
```text
1 x InsertedCanonical
1 x QuarantinedConflict
2 variantes durables
2 observations durables
2 mappings observation -> variante
1 selector canonique
1 conflict case Open revision 1
projection V001 == variante désignée par le selector
```
L'ordre d'arrivée n'est pas supposé : l'une ou l'autre acquisition peut devenir le premier canonique, mais la seconde doit être quarantinée sans écraser ce canonique.
### Rollback après ouverture réelle d'un conflit
Un nouveau scénario live force une erreur après les écritures de divergence mais avant le commit :
```text
canonique existant
-> incoming divergent
-> variante entrante créée
-> conflict case créé
-> collision volontaire d'observation avec une autre identité
-> erreur avant COMMIT
-> rollback implicite de la transaction
```
Après l'échec, la preuve exige exactement l'état antérieur :
```text
1 seule variante pour l'identité
1 selector
0 conflict case
1 observation de seed
1 mapping de seed
projection V001 toujours cohérente avec le selector
```
Cela couvre le risque qu'un conflict case ou une variante orpheline survive à un échec tardif de l'acquisition.
Le test live reste `#[ignore]`, lit uniquement un URI PostgreSQL dédié depuis stdin, refuse un schéma KSP préexistant et ne devient pas un gate automatique sans ressource opérateur explicite.
## Régression Job Backfill
Le Job Backfill ajoute un canari sur une réobservation déjà idempotente d'un conflit durable :
```text
entity = AlreadyPresent
observation = AlreadyPresent
variant = QuarantinedConflict
```
La projection doit rester :
```text
BackfillEntityPersistence::Conflict
BackfillObservationPersistence::AlreadyPresent
```
Le conflit durable n'est donc jamais aplati en simple `AlreadyPresent`, même lorsque l'observation est elle-même idempotente.
## Régression Worker
Le Worker ajoute une preuve runtime avec deux identités successives alors que le Store retourne `QuarantinedConflict` :
```text
première identité -> QuarantinedConflict
seconde identité -> QuarantinedConflict
```
Les deux acquisitions doivent atteindre le Store. Après les deux résultats :
```text
state = Running
health = Degraded
persisted_total = 2
content_conflict_total = 2
store_failure_total = 0
```
Le Stop explicite doit ensuite terminer en `Stopped`. Cette preuve verrouille directement l'invariant « un conflit durable n'empêche pas la progression d'une autre identité ».
## Recalibrage du lifecycle de fermeture
Le plan 038 est ajusté pour respecter les règles `VER-LIFECYCLE` actuelles, qui sont plus strictes que le découpage historique `pre.008 -> rel.001` :
```text
pre.008 = hardening / gate technique
pre.009 = réconciliation documentaire finale
pre.010 = préparation minimale de publication
rel.001 = mécanique de publication stable
```
`pre.008` ne modifie donc ni `CHANGELOG.md`, ni `ROADMAP.md`, ni le prompt de démarrage suivant, et ne fait pas la réconciliation README/USAGE finale.
## Fichiers modifiés
```text
Cargo.toml
crates/ksp-job-backfill-lib/unit_tests/persistence.rs
crates/ksp-store-postgres-lib/tests/hardening_completeness.rs
crates/ksp-store-postgres-lib/tests/postgres_raw_transaction_live.rs
crates/ksp-store-postgres-lib/tests/v003_variant_persistence.rs
crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/runtime.rs
docs/plans/038-V0_3_16_RAW_RESILIENCE_CONFLICT_PLAN.md
```
## Fichier ajouté
```text
deltas/0.3.16/pre.008.md
```
## Fichiers supprimés
```text
aucun
```
## Validations exécutées lors de la génération
```bash
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
```
Le toolchain Rust n'est pas installé dans l'environnement de génération. `cargo fmt`, `cargo check`, Clippy et les tests Cargo ne sont donc pas déclarés exécutés ici.
La preuve PostgreSQL live reste volontairement opt-in et n'est pas déclarée exécutée sans URI isolé fourni par l'opérateur.
## Gate opérateur demandé
```bash
cargo fmt --all
cargo fmt --all -- --check
python3 scripts/audit_rust_workspace_rules.py
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas
cargo check --workspace
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test -p ksp-store-api --all-targets --all-features
cargo test -p ksp-store-lib --all-targets --all-features
cargo test -p ksp-store-postgres-lib --all-targets --all-features
cargo test -p ksp-job-backfill-lib --all-targets --all-features
cargo test -p ksp-worker-raw-transaction-ingest-lib --all-targets --all-features
```
Le live PostgreSQL peut être exécuté séparément sur une base dédiée vide :
```bash
cargo test -p ksp-store-postgres-lib --test postgres_raw_transaction_live -- --ignored --nocapture
```
Il ne doit jamais être lancé contre une base KSP contenant des données utiles.
## Décisions prises
- aucune modification runtime n'est nécessaire pour fermer le hardening observé ;
- le conflit durable concurrent est un succès quarantiné, pas une erreur backend ;
- les deux observations concurrentes divergentes doivent être conservées ;
- un échec tardif avant commit doit annuler variante et conflict case nouvellement créés ;
- le canonique ne change jamais dans la branche de conflit ;
- Backfill conserve explicitement la distinction conflit / observation idempotente ;
- le Worker doit continuer à traiter les identités suivantes après une quarantaine ;
- le lifecycle final est séparé en `pre.008`, `pre.009`, `pre.010`, puis `rel.001` conformément aux règles actuelles.
## Questions ouvertes
Aucune question fonctionnelle bloquante pour `0.3.16`.
Le test PostgreSQL live reste conditionné à la disponibilité d'un PostgreSQL isolé ; son absence n'autorise pas à prétendre qu'il a été exécuté.
## Suite
Après gate technique propre : `0.3.16-pre.009` — réconciliation documentaire finale de `0.3.16`, sans nouveau comportement runtime.