Files
khadhroony-solana-project/deltas/0.3.6/pre.008.md
2026-09-01 15:14:57 +02:00

307 lines
11 KiB
Markdown

<!-- file: deltas/0.3.6/pre.008.md -->
<!-- version: 1 -->
# Delta `0.3.6-pre.008` — concurrence bornée, frontier contiguë et checkpoint Backfill
## Base requise
```text
0.3.6-pre.007 appliquée
workspace.package.version = 0.3.6-pre.7
```
Le gate opérateur fourni pour cette base confirme :
```text
cargo fmt --all PASS
python3 scripts/audit_rust_workspace_rules.py PASS / clean
python3 scripts/audit_markdown_tables.py ... PASS / clean (264 tables / 152 fichiers)
cargo check --workspace PASS
cargo clippy --workspace --all-targets PASS / aucun warning
cargo test -p ksp-job-backfill-lib PASS
unitaires 29 PASS
dependency_boundary 2 PASS
public_api 4 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.008` du plan 027 : exécuter les candidats découverts avec une concurrence métier bornée, réconcilier les fins hors ordre par une frontier contiguë et produire un checkpoint sûr pour reprise contrôlée.
La tranche n'ouvre pas encore l'annulation coopérative, le snapshot latest-value concret, les états terminaux complets ou les courses d'annulation de `pre.009`.
## Checkpoint caller-owned
La tranche ajoute le type public opaque :
```text
BackfillCheckpoint
```
Il contient uniquement :
```text
JobId
BackfillScopeFingerprint
completed_prefix
resume_before privé si scope BeforeAddress
```
Il ne contient ni payload RAW, ni URL, ni provider, ni endpoint, ni credential, ni liste de signatures.
`BackfillRequest::with_checkpoint` valide obligatoirement :
```text
checkpoint.job_id == request.job_id
checkpoint.scope_fingerprint == request.scope_fingerprint
```
Un checkpoint incompatible est rejeté par :
```text
ERROR_CODE_BACKFILL_CHECKPOINT_INVALID
```
Aucune table Store, migration ou persistance durable de Job n'est créée. Le checkpoint est caller-owned et qualifie uniquement une reprise contrôlée ; la v0.3.6 ne revendique pas une reprise crash-safe multi-processus.
## Sémantique de reprise par scope
### LatestAddress
La reprise repart volontairement sans curseur RPC depuis la vue latest courante. L'idempotence Store absorbe les candidats déjà durables. Le préfixe historique du checkpoint n'est jamais utilisé comme offset de la nouvelle vue latest.
### BeforeAddress
Le checkpoint conserve comme curseur exclusif le dernier candidat du préfixe contigu durable. Si aucun candidat nouveau n'est durable, le curseur précédent — ou l'ancre initiale — est conservé. Le compteur `completed_prefix` est cumulatif entre reprises contrôlées.
### AfterAddress
Le scope borné est rejoué et seul le préfixe contigu validé est ignoré. Si la découverte s'arrête sur :
```text
AfterAnchorNotReached
```
aucun progrès nouveau de checkpoint n'est publié, même si certaines hydrations observées ont fini durablement ; le checkpoint précédent est conservé lorsqu'il existe.
### ExplicitSignatures
La liste dédupliquée et fingerprintée est rejouée et seul son préfixe contigu déjà validé est ignoré.
## Frontier contiguë
Chaque candidat conserve son index stable de découverte. Une bitmap privée bornée à `max_candidates <= 10_000` suit les résultats durables.
La frontier avance uniquement pour :
```text
Inserted
AlreadyPresent
SkippedPurged
```
Les résultats suivants créent un trou et ne sont jamais marqués durables :
```text
Missing
Conflict
erreur Transport
conversion invalide
erreur Store
```
Une completion hors ordre ne saute jamais un index incomplet. Les tests reproduisent explicitement :
```text
index 1 durable -> frontier 0
index 0 durable -> frontier 2
index 3 durable -> frontier 2
index 2 durable -> frontier 4
```
## Exécution concurrente bornée
La tranche ajoute :
```text
execute_backfill_discovery
BackfillExecutionBatch
```
L'implémentation utilise un `FuturesUnordered` privé et ne garde jamais plus de :
```text
request.hydration_concurrency()
```
futures candidates en vol.
Aucun `tokio::spawn`, timer Job, retry Job, pacing Job ou choix d'endpoint n'est introduit. Transport reste propriétaire de sa politique réseau et Store de sa persistance.
Le batch expose uniquement des faits bornés utiles à la preuve de frontier : candidats, admis, terminés, durables, trous, maximum en vol, préfixe contigu local, checkpoint et premier code d'échec fatal.
## Missing, conflit et erreurs
`Missing` est un trou non fatal : les admissions suivantes continuent, mais la frontier reste bloquée tant que ce trou existe.
Un `Conflict` ou toute erreur Transport/conversion/Store finale :
1. devient le premier `failure_code` stable du batch ;
2. arrête immédiatement les nouvelles admissions ;
3. laisse finir toutes les futures déjà admises ;
4. produit ensuite le checkpoint correspondant au dernier préfixe réellement durable.
Cette phase ne publie pas encore l'état Job terminal `Failed`/`Partial`; cette projection appartient au snapshot/runtime de `pre.009`.
## Horodatage d'acquisition
Le chemin public d'exécution produit le `RawTimestamp` de réception à partir de `SystemTime` immédiatement avant l'hydratation. Une horloge avant epoch ou un timestamp non représentable devient une erreur structurée :
```text
ERROR_CODE_BACKFILL_EXECUTION_INVALID
```
Aucun clock/provider type n'entre dans l'API publique.
## Dépendances
La crate ajoute comme dépendance normale :
```text
futures-util = { workspace = true, features = ["std"] }
```
Cette dépendance existe déjà dans `[workspace.dependencies]`; aucune nouvelle version externe n'est introduite.
Tokio reste exclusivement en `dev-dependencies` pour les tests async et aucun type Tokio/Futures n'est exposé par la crate root.
## Tests matérialisés
La tranche ajoute **10 tests unitaires**, portant le total à **39 unitaires** :
- frontier hors ordre strictement contiguë ;
- checkpoint rejeté sur mauvais `JobId` ou fingerprint ;
- checkpoint Before cumulatif avec curseur du dernier candidat contigu ;
- After sans ancre atteinte n'avance jamais le checkpoint ;
- Before reprend au curseur checkpoint plutôt qu'à l'ancre initiale ;
- Latest reprend sans curseur depuis la vue courante ;
- concurrence observée strictement bornée malgré fins hors ordre ;
- Missing non fatal mais bloquant pour la frontier ;
- conflit arrêtant les admissions et drainant le travail déjà en vol ;
- reprise Explicit sautant uniquement le préfixe checkpointé.
Une canarie publique supplémentaire porte les canaries d'intégration à **9** :
```text
dependency_boundary 2
public_api 5
release_completeness 2
```
Les canaries verrouillent également l'inventaire exact des modules, `futures-util` comme détail privé normal, l'absence de Tokio normal, l'absence de retry/pacing/endpoint selection Job et la fermeture des surfaces `BackfillSnapshot`/`BackfillJobHandle` de `pre.009`.
## Fichiers ajoutés
```text
crates/ksp-job-backfill-lib/src/checkpoint.rs
crates/ksp-job-backfill-lib/src/execution.rs
crates/ksp-job-backfill-lib/unit_tests/checkpoint.rs
crates/ksp-job-backfill-lib/unit_tests/execution.rs
deltas/0.3.6/pre.008.md
```
## Fichiers modifiés
```text
Cargo.toml
crates/ksp-job-backfill-lib/Cargo.toml
crates/ksp-job-backfill-lib/src/discovery.rs
crates/ksp-job-backfill-lib/src/error.rs
crates/ksp-job-backfill-lib/src/lib.rs
crates/ksp-job-backfill-lib/src/persistence.rs
crates/ksp-job-backfill-lib/src/request.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
crates/ksp-job-backfill-lib/unit_tests/discovery.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é :
```text
Cargo.toml 405 -> 406
crates/ksp-job-backfill-lib/Cargo.toml 2 -> 3
crates/ksp-job-backfill-lib/src/discovery.rs 2 -> 3
crates/ksp-job-backfill-lib/src/error.rs 3 -> 4
crates/ksp-job-backfill-lib/src/lib.rs 3 -> 4
crates/ksp-job-backfill-lib/src/persistence.rs 1 -> 2
crates/ksp-job-backfill-lib/src/request.rs 2 -> 3
crates/ksp-job-backfill-lib/tests/dependency_boundary.rs 3 -> 4
crates/ksp-job-backfill-lib/tests/public_api.rs 3 -> 4
crates/ksp-job-backfill-lib/tests/release_completeness.rs 3 -> 4
crates/ksp-job-backfill-lib/unit_tests/discovery.rs 1 -> 2
```
Les quatre nouveaux fichiers Rust commencent à `version: 1`. Le plan 027 et la validation 023 passent de `13` à `14`. Le présent delta commence à `version: 1`.
## Version workspace
La tranche modifie du Rust :
```text
workspace.package.version = 0.3.6-pre.8
delivery = 0.3.6-pre.008
commit = v0.3.6-pre.008
```
Aucun tag prerelease.
## Fichiers supprimés
Aucun.
## Frontières conservées
- aucune persistence durable de checkpoint dans Store ;
- aucune table ou migration Job ;
- aucun retry/pacing/timer Job ;
- aucune sélection provider/endpoint Job ;
- aucun `ForceRehydrate` ;
- aucun `tokio::spawn` ;
- aucun type Tokio/Futures public ;
- aucune annulation coopérative de `pre.009` ;
- aucun `BackfillSnapshot` latest-value concret ;
- aucune application ou Worker ;
- aucun changement README/USAGE/CHANGELOG/ROADMAP/prompt.
## Validations exécutées dans l'environnement d'assemblage
```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.6
```
L'environnement d'assemblage ne fournit ni `cargo`, ni `rustc`, ni `rustfmt`; aucun résultat Rust de `pre.008` n'est donc revendiqué localement.
## Gate opérateur demandé
```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.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 après gate : **39 unitaires + 9 canaries**, sans warning. Un échec reste dans la lane `pre.008-fix.001`; un gate vert ouvre `pre.009`.