Files
2026-09-01 13:00:21 +02:00

12 KiB

Delta 0.3.6-pre.005 — fondation Backfill et découverte bornée

Base requise

0.3.6-pre.004 appliquée
workspace.package.version = 0.3.6-pre.4

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 / 146 fichiers)
cargo check --workspace                              PASS
cargo clippy --workspace --all-targets               PASS
cargo test -p ksp-onchain-transport-lib              PASS
  unitaires                                          385 PASS
  public_api                                          50 PASS
  release_completeness                               43 PASS
  doctests                                             4 PASS
cargo tree -p ksp-onchain-transport-lib --edges normal  exécuté
cargo tree -p ksp-onchain-transport-lib -e features     exécuté

Les smokes réseau restent opt-in et ignorés dans le gate normal, conformément à leur contrat.

Objectif

Matérialiser exclusivement la tranche pre.005 du plan 027 : créer ksp-job-backfill-lib et figer la requête, les scopes, l'identité des candidats, les bornes ainsi que la découverte paginée déterministe avant toute hydratation getTransaction, conversion RAW, persistance Store, checkpoint ou runtime de Job.

Cette tranche résout explicitement un invariant d'identité qui ne doit jamais dépendre de la topologie d'acquisition : une transaction/signature est identifiée logiquement par son réseau et sa signature, pas par le provider, l'endpoint ou le protocole qui l'a retrouvée.

Identité réseau contre provenance Transport

Le contrat de découverte introduit :

BackfillCandidateIdentity = (RawNetworkId, BackfillSignature)

RawNetworkId est un identifiant logique ouvert réexporté par ksp-store-lib. Des namespaces comme mainnet, devnet, testnet, localnet, synthetic ou un futur réseau nommé restent donc distincts sans imposer un enum fermé dans Job.

En conséquence :

  • la même signature observée sur deux réseaux différents représente deux identités distinctes ;
  • la même signature acquise plusieurs fois sur le même réseau via HTTP, WebSocket ou gRPC converge vers la même identité transactionnelle ;
  • rôle Transport, provider, endpoint et protocole décrivent la sélection ou la provenance d'acquisition ; ils ne participent jamais à l'identité transactionnelle ;
  • JobId et la future concurrence d'hydratation ne participent pas non plus au fingerprint sémantique d'un scope.

Le Store API possède déjà RawTransactionReference { network, signature }. Le backend PostgreSQL actuel lie en outre un Store physique à un seul RawNetworkId via ksp_store_identity; une clé physique locale basée sur la signature y reste donc dans un namespace réseau unique. Tout futur backend hébergeant plusieurs réseaux dans le même namespace physique devra préserver l'identité logique (network, signature) par sa clé, sa partition ou un mécanisme équivalent.

Crate ksp-job-backfill-lib

La nouvelle crate est une bibliothèque comportementale indépendante, sans exécutable ni Worker. Ses dépendances normales sont limitées à :

ksp-core-lib
ksp-job-api
ksp-logging-lib
ksp-onchain-transport-lib
ksp-store-lib (default-features = false)
sha2 (workspace)

Tokio est uniquement une dev-dependency avec les features de test nécessaires aux canaries async. Aucun backend Store n'est activé par défaut par Backfill.

La crate ne dépend pas directement de Config, Interface, Program, Wallet, Store API, Store PostgreSQL, reqwest, tonic ou d'une crate protocolaire Solana.

Requête bornée

BackfillRequest impose explicitement :

  • JobId ;
  • RawNetworkId ;
  • rôle HTTP Transport ;
  • engagement Confirmed ou Finalized ;
  • un des quatre scopes ;
  • page size 1..=1000 ;
  • max pages 1..=10000 ;
  • max candidates 1..=10000 ;
  • future concurrence d'hydratation 1..=64 ;
  • min_context_slot optionnel uniquement pour les scopes adresse.

Le rôle HTTP sert uniquement à la sélection Transport. Il est volontairement exclu du fingerprint du scope avec provider, endpoint et protocole.

Scopes

Quatre scopes sont matérialisés :

  • LatestAddress(address) : fenêtre courante depuis le plus récent ;
  • BeforeAddress(address, anchor) : historique plus ancien que l'ancre exclusive ;
  • AfterAddress(address, anchor) : fenêtre plus récente la plus proche d'une ancre exclusive ;
  • ExplicitSignatures(signatures) : liste explicite bornée sans découverte adresse.

Les signatures explicites sont dédupliquées selon la première occurrence et conservent leur ordre d'entrée.

BackfillSignature valide dans cette tranche uniquement la forme texte Base58 bornée compatible avec une signature Solana de 64 octets. Le décodage Base58 exact vers la signature RAW canonique de 64 octets reste volontairement réservé à pre.006; pre.005 ne prétend donc pas encore valider le contenu binaire canonique.

Fingerprint de scope

BackfillScopeFingerprint utilise SHA-256 avec séparation de domaine et couvre les paramètres qui changent réellement la sémantique de découverte :

  • réseau ;
  • commitment ;
  • type de scope ;
  • adresse ou liste explicite ;
  • direction/ancre ;
  • page size ;
  • max pages ;
  • max candidates ;
  • min_context_slot.

Il exclut intentionnellement :

  • JobId ;
  • rôle HTTP ;
  • provider ;
  • endpoint ;
  • protocole ;
  • concurrence d'hydratation.

Changer de source d'acquisition ne change donc pas l'identité du scope ni celle des transactions candidates.

Découverte paginée

La voie de production appelle uniquement le wrapper typé existant :

HttpTransportPool::get_signatures_for_address

Aucun client HTTP, retry loop, pacing, cooldown, sélection d'endpoint ou politique fournisseur n'entre dans Job.

Les règles figées sont :

  • Latest et Before utilisent le curseur before et conservent l'ordre RPC du plus récent au plus ancien ;
  • Before démarre avec l'ancre exclusive fournie ;
  • After utilise until = anchor, avance par before et ne conserve que les max_candidates les plus proches de l'ancre parmi les signatures plus récentes ;
  • la déduplication est stable entre pages ;
  • une page fournisseur plus grande que la limite demandée est rejetée ;
  • un curseur qui n'avance pas est rejeté ;
  • une page courte ou vide matérialise RpcBoundary ;
  • atteindre la limite de candidats matérialise CandidateLimit ;
  • épuiser max_pages en Latest/Before matérialise PageLimit et un résultat partiel ;
  • épuiser max_pages en After avant preuve de la frontière matérialise AfterAnchorNotReached et un résultat partiel.

La borne After est volontairement conservative : un Job ne prétend jamais avoir rejoint l'ancre si la limite de pages empêche de le prouver.

Tests matérialisés

La tranche ajoute 11 tests unitaires :

  • validation et redaction de signature ;
  • déduplication explicite stable ;
  • bornes exactes de requête ;
  • fingerprint indépendant de JobId/rôle/concurrence mais distinct par réseau ;
  • distinction sémantique Before/After ;
  • Latest paginé avec déduplication inter-pages ;
  • Before avec ancre exclusive et progression du curseur ;
  • After avec fenêtre la plus proche de l'ancre ;
  • After borné partiel ;
  • Latest borné partiel ;
  • explicite sans appel Transport et avec identité réseau.

Elle ajoute 6 canaries d'intégration :

  • deux canaries de dépendances/firewalls ;
  • deux canaries d'API publique, dont l'identité (network, signature) ;
  • deux canaries de complétude qui empêchent l'ouverture anticipée de RAW/persistance/checkpoint/runtime.

Les doubles de page sont privés aux unit tests ; aucune abstraction fake n'entre dans l'API publique.

Documentation courante réconciliée

Les documents d'architecture actifs sont alignés avec le nom désormais matérialisé ksp-job-backfill-lib et son graphe réel sans Config :

docs/architecture/004-COMPONENT_INVENTORY.md
docs/architecture/005-DEPENDENCY_GRAPH.md
docs/architecture/009-ACQUISITION_WORKERS_AND_JOBS.md

Les anciens plans historiques ne sont pas réécrits. README, USAGE, CHANGELOG et ROADMAP restent fermés jusqu'aux tranches prévues de réconciliation/fermeture.

Fichiers ajoutés

crates/ksp-job-backfill-lib/Cargo.toml
crates/ksp-job-backfill-lib/src/constants.rs
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/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
crates/ksp-job-backfill-lib/unit_tests/request.rs
deltas/0.3.6/pre.005.md

Fichiers modifiés

Cargo.toml
docs/architecture/004-COMPONENT_INVENTORY.md
docs/architecture/005-DEPENDENCY_GRAPH.md
docs/architecture/009-ACQUISITION_WORKERS_AND_JOBS.md
docs/plans/027-V0_3_6_JOB_API_BACKFILL_PLAN.md
docs/validation/023-V0_3_6_JOB_API_BACKFILL.md

Mécanique Cargo :

header version: 399 -> 400
workspace.package.version: 0.3.6-pre.4 -> 0.3.6-pre.5
workspace members: + crates/ksp-job-backfill-lib
workspace dependencies versionnées: inchangées

Fichiers supprimés

Aucun.

Frontières conservées

  • aucun code kbot3 n'est copié ; kbot3 reste uniquement une référence fonctionnelle ;
  • aucune hydratation getTransaction n'est encore implémentée dans Job ;
  • aucune conversion RAW v1 n'est ouverte ;
  • aucune écriture Store, observation d'acquisition ou politique ForceRehydrate n'est ouverte ;
  • aucun checkpoint, frontier contiguë, runtime Job ou notification concrète n'est ouvert ;
  • aucune lecture Config/env n'est introduite ;
  • aucun endpoint/provider/protocole ne devient une clé transactionnelle ;
  • aucun backend Store n'est imposé ;
  • aucun README/USAGE/CHANGELOG/ROADMAP n'est rouvert.

Validations exécutées dans l'environnement d'assemblage

python3 scripts/audit_rust_workspace_rules.py
-> General Rust rule audit: clean
-> Rust export completeness audit: 0 candidate(s)
-> KSP workspace Rust rule audit: clean

python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.3.6
-> Markdown table audit: clean (264 tables / 147 fichiers, présent delta inclus)

python3 -m unittest scripts/tests/test_audit_markdown_tables.py
-> 5 tests / OK

Le premier audit Markdown après réconciliation de l'inventaire a détecté uniquement l'élargissement mécanique requis par ksp-job-backfill-lib; la table a été réalignée sur le contenu le plus large avant le second passage propre.

L'environnement d'assemblage ne fournit ni cargo, ni rustc, ni rustfmt. Les 11 unitaires et 6 canaries Rust sont matérialisés mais ne sont 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

Le gate doit notamment confirmer :

  • 11 unitaires + 6 canaries ;
  • ksp-store-lib sans activation implicite du backend PostgreSQL ;
  • aucun accès direct à Store API/backend, Config ou dépendance protocolaire ;
  • pagination Latest/Before/After et explicite déterministe ;
  • résultat partiel conservateur aux bornes ;
  • identité candidate scellée par réseau + signature, indépendamment du rôle/provider/endpoint/protocole.

Questions ouvertes

Aucune question ne bloque pre.006 après un gate opérateur vert de pre.005.