9.0 KiB
Delta 0.3.6-pre.007 — persistance Store atomique et idempotence Backfill
Base requise
0.3.6-pre.006-fix.002 appliquée
workspace.package.version = 0.3.6-pre.6.fix.2
Le gate opérateur fourni pour cette base confirme :
cargo fmt --all PASS
python3 scripts/audit_rust_workspace_rules.py PASS / clean
python3 scripts/audit_markdown_tables.py ... PASS / clean (264 tables / 151 fichiers)
cargo check --workspace PASS
cargo clippy --workspace --all-targets PASS / aucun warning
cargo test -p ksp-job-backfill-lib PASS
unitaires 20 PASS
dependency_boundary 2 PASS
public_api 3 PASS
release_completeness 2 PASS
cargo tree -p ksp-job-backfill-lib --edges normal exécuté
cargo tree -p ksp-job-backfill-lib -e features exécuté
Objectif
Matérialiser exclusivement pre.007 du plan 027 : composer la persistance normale d'un résultat d'hydratation Backfill avec les contrats Store déjà existants, sans prélecture, sans second chemin d'écriture et sans ouvrir la concurrence, la frontier ou le checkpoint de pre.008.
L'identité canonique reste :
RawTransactionReference = (RawNetworkId, RawTransactionSignature)
Provider, endpoint et protocole ne participent toujours qu'à la provenance de l'observation.
Contrat public Backfill
La tranche ajoute :
persist_backfill_hydration
BackfillPersistenceOutcome
BackfillEntityPersistence
BackfillObservationPersistence
ERROR_CODE_BACKFILL_PERSISTENCE_INVALID
BackfillPersistenceOutcome conserve la référence réseau + signature et projette séparément l'état de l'entité canonique et celui de l'observation. Cette projection évite de réduire un conflit, une purge ou une nouvelle observation à un compteur skip ambigu.
Dispositions entité :
Inserted
AlreadyPresent
SkippedPurged
Missing
Conflict
Dispositions observation :
Inserted
AlreadyPresent
NotRecorded
NotApplicable
Composition Store atomique
Pour BackfillHydrationOutcome::Available, la bibliothèque consomme l'acquisition en mémoire puis appelle exclusivement :
ksp_store_lib::RawTransactionWrite::persist_raw_transaction_acquisition
avec :
RawTransactionAcquisitionMode::Normal
La transaction et son observation restent donc une seule opération logique atomique possédée par Store. La tranche n'appelle jamais record_raw_transaction_observation séparément et n'introduit aucune prélecture get_raw_transaction avant écriture.
ForceRehydrate n'est pas exposé par le chemin Backfill normal.
Réseau
Avant toute écriture, le réseau logique exposé par le snapshot du Store doit être identique au RawNetworkId de la référence hydratée. Un mismatch est rejeté par :
ERROR_CODE_BACKFILL_PERSISTENCE_INVALID
field = store.network
Ce contrôle n'utilise ni endpoint, ni provider, ni protocole comme identité.
Une incohérence interne entre la référence annoncée, la transaction et l'observation est également rejetée avant soumission Store.
Outcomes Store admis
En mode normal, les combinaisons Store suivantes sont acceptées et projetées explicitement :
RawEntityWriteOutcome::Inserted
+ RawObservationWriteOutcome::Inserted
-> Inserted / Inserted
RawEntityWriteOutcome::AlreadyPresent
+ RawObservationWriteOutcome::Inserted
-> AlreadyPresent / Inserted
RawEntityWriteOutcome::AlreadyPresent
+ RawObservationWriteOutcome::AlreadyPresent
-> AlreadyPresent / AlreadyPresent
RawEntityWriteOutcome::SkippedPurged
+ RawObservationWriteOutcome::NotRecorded
-> SkippedPurged / NotRecorded
RawEntityWriteOutcome::Rehydrated est impossible dans le chemin normal et est rejeté, comme toute autre combinaison incohérente.
Missing
BackfillHydrationOutcome::Missing(reference) devient :
Missing / NotApplicable
sans aucune écriture Store. La tranche ne persiste toujours ni observation missing, ni observation d'échec fabriquée ; ces contrats n'existent pas dans le Store RAW transaction actuel.
Conflit et erreurs
Une erreur Store dont le code stable est :
ERROR_CODE_RAW_CONFLICT
est projetée en :
Conflict / NotRecorded
Elle reste donc visible et distincte de l'idempotence. Les tranches de frontier/snapshot pourront ensuite la traiter comme trou/échec sans parsing de message d'erreur.
Toutes les autres erreurs Store sont propagées telles quelles.
Idempotence
La même acquisition déterministe peut produire successivement :
Inserted / Inserted
AlreadyPresent / AlreadyPresent
sans doublon logique. Une entité déjà présente peut aussi recevoir une nouvelle observation légitime :
AlreadyPresent / Inserted
La distinction repose entièrement sur les outcomes Store officiels ; Job ne reconstruit aucune logique d'idempotence physique.
Tests matérialisés
La tranche ajoute 9 tests unitaires, portant le total de ksp-job-backfill-lib à 29 unitaires :
Missingne soumet aucune écriture ;- mismatch réseau rejeté avant Store ;
- insert atomique
Inserted/Inserted; - entité existante + nouvelle observation ;
- relance idempotente
AlreadyPresent/AlreadyPresent; - purge normale
SkippedPurged/NotRecorded; - conflit Store explicite et non confondu avec idempotence ;
- erreur Store non conflit propagée ;
- combinaisons impossibles et mismatch transaction/observation rejetés.
Une canarie publique supplémentaire porte les canaries d'intégration à 8 :
dependency_boundary 2
public_api 4
release_completeness 2
Les canaries statiques verrouillent l'usage de l'écriture atomique, du mode Normal, l'absence d'écriture d'observation séparée, l'absence de ForceRehydrate, l'absence de backend Store direct et la fermeture du checkpoint/runtime de pre.008.
Fichiers ajoutés
crates/ksp-job-backfill-lib/src/persistence.rs
crates/ksp-job-backfill-lib/unit_tests/persistence.rs
deltas/0.3.6/pre.007.md
Fichiers modifiés
Cargo.toml
crates/ksp-job-backfill-lib/src/error.rs
crates/ksp-job-backfill-lib/src/lib.rs
crates/ksp-job-backfill-lib/tests/dependency_boundary.rs
crates/ksp-job-backfill-lib/tests/public_api.rs
crates/ksp-job-backfill-lib/tests/release_completeness.rs
docs/plans/027-V0_3_6_JOB_API_BACKFILL_PLAN.md
docs/validation/023-V0_3_6_JOB_API_BACKFILL.md
Versions d'en-tête
Les fichiers Rust/TOML modifiés incrémentent tous leur version d'en-tête exactement d'une unité :
Cargo.toml 404 -> 405
crates/ksp-job-backfill-lib/src/error.rs 2 -> 3
crates/ksp-job-backfill-lib/src/lib.rs 2 -> 3
crates/ksp-job-backfill-lib/tests/dependency_boundary.rs 2 -> 3
crates/ksp-job-backfill-lib/tests/public_api.rs 2 -> 3
crates/ksp-job-backfill-lib/tests/release_completeness.rs 2 -> 3
Les deux nouveaux fichiers Rust commencent à version: 1. Le plan 027 et la validation 023 passent de 12 à 13. Le présent delta commence à version: 1.
Version workspace
La tranche modifie du Rust :
workspace.package.version = 0.3.6-pre.7
delivery = 0.3.6-pre.007
commit = v0.3.6-pre.007
Aucun tag prerelease.
Fichiers supprimés
Aucun.
Frontières conservées
- aucune prélecture Store avant hydratation/persistance ;
- aucun backend Store direct ;
- aucun
ForceRehydrate; - aucune persistence de missing/failed artificielle ;
- aucune concurrence d'hydratation concrète ;
- aucune frontier contiguë ;
- aucun checkpoint/reprise runtime ;
- aucun snapshot Backfill concret ;
- aucune nouvelle dépendance ou feature ;
- aucune modification Transport, Store, Job API, Config, README, USAGE, CHANGELOG ou ROADMAP ;
- aucun code kbot3 copié.
Validations dans l'environnement d'assemblage
L'environnement d'assemblage exécute les audits statiques et contrôles d'archive mais ne possède ni cargo, ni rustc, ni rustfmt. Les tests Rust matérialisés ne sont donc pas annoncés comme exécutés localement.
Gate opérateur demandé
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.6
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test -p ksp-job-backfill-lib
cargo tree -p ksp-job-backfill-lib --edges normal
cargo tree -p ksp-job-backfill-lib -e features
Résultat attendu : 29 tests unitaires + 8 canaries d'intégration, aucun warning Clippy, et dépendance Store toujours limitée à ksp-store-lib avec default-features = false.
Questions ouvertes
Aucune pour pre.007. pre.008 reste fermé tant que ce gate n'est pas vert.