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

8.2 KiB

Delta 0.3.16-pre.008 — hardening technique de la vertical slice variantes/conflits

Base requise

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 :

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

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 :

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 :

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 :

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 :

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 :

entity = AlreadyPresent
observation = AlreadyPresent
variant = QuarantinedConflict

La projection doit rester :

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 :

première identité -> QuarantinedConflict
seconde identité  -> QuarantinedConflict

Les deux acquisitions doivent atteindre le Store. Après les deux résultats :

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 :

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

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é

deltas/0.3.16/pre.008.md

Fichiers supprimés

aucun

Validations exécutées lors de la génération

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é

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 :

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.