42 KiB
Plan 0.3.16 -> 0.3.18 — résilience RAW, variantes, conflits et récupération
1. Statut du document
Ce document ferme le gate d'architecture et de sizing de 0.3.16-pre.001.
Base autorisée :
v0.3.15
commit 7328be6997399c513306f2f4c8cdf00bd8b22367
pre.001 reste volontairement documentaire. Il ne crée aucune migration finale, aucune table SQL, aucun contrat Rust définitif et aucune UI de conflit. Les décisions ci-dessous fixent les invariants à respecter par les tranches d'implémentation suivantes.
Le fix documentaire 0.3.16-pre.001-fix.001 conserve toutes ces décisions techniques mais corrige le sizing : le programme est réparti sur trois releases stables, 0.3.16, 0.3.17 et 0.3.18, afin qu'aucune version ne nécessite plus d'une session de développement normale. Ce fix ne modifie pas workspace.package.version et ne change aucun contrat Rust ou SQL.
2. Contraintes normatives confirmées
Les règles KSP restent cumulatives. 0.3.16 ne crée aucune exception implicite.
Contraintes bloquantes pour cette release :
- Rust 2024 ;
unsafe,unwrap,expectetpanicrestent interdits conformément aux règles KSP ; - tous les éléments partagés
pub/pub(crate)restent réexportés par lelib.rsde la crate et consommés viacrate::Item; ksp-store-libreste l'unique façade Store des Worker, Job et Desk ;ksp-store-postgres-libreste le propriétaire du schéma, du SQL, des transactions et des verrous PostgreSQL ;ksp-config-libreste le propriétaire de la configuration projet et de sa validation ;- le Worker live et le Job Backfill restent indépendants ;
- aucune application Desk ne reçoit de SQL, de backend PostgreSQL direct ni de secret d'infrastructure ;
- aucune nouvelle dépendance protocolaire Solana directe n'est introduite pour contourner les couches existantes ;
- les migrations V000/V001/V002 restent byte-identiques et leurs checksums ne sont jamais réécrits ;
- toute évolution physique se fait par une migration additive nouvelle ;
- aucune queue non bornée n'est introduite ;
- aucune provenance provider n'est une autorité canonique ;
- aucune divergence RAW conservable ne doit être convertie par défaut en panne terminale d'acquisition ;
- une validation n'est déclarée PASS que si elle a réellement été exécutée.
3. État stable 0.3.15 audité
3.1 Fermeture de la release précédente
Le delta stable 0.3.15-rel.001 documente un gate opérateur pre.018 propre, puis les couloirs de fermeture pre.016 technique/live, pre.017 documentaire et pre.018 publication.
La fermeture stable a notamment validé :
- les cinq familles de routes live ;
- plusieurs Worker indépendants partageant le même Store RAW ;
- Yellowstone + HTTP Block Polling en parallèle environ dix-neuf minutes ;
- absence de
grpc_backpressure_overflowpendant ce live de référence ; - Stop final
Stopped/Healthypour les deux routes ; - acceptation du seul cas de convergence asymétrique prouvé en
0.3.15: canonique complet + entrant dontlogMessagesest strictement tronqué et compatible.
Le sens canonique tronqué -> entrant complet est explicitement réservé à 0.3.16.
3.2 Store API et outcomes actuels
Le Store RAW 0.3.15 expose un modèle centré sur une identité logique (network, signature) et un canonique unique par signature dans le backend PostgreSQL d'une base déjà liée à un réseau.
Les outcomes d'écriture actuels distinguent notamment :
Inserted
AlreadyPresent
Rehydrated
SkippedPurged
Les observations distinguent notamment :
Inserted
AlreadyPresent
NotRecorded
Le contrat ne possède pas encore d'outcome durable représentant :
variant inserted
compatible less complete
canonical promoted
conflict quarantined
conflict resolved
Ces notions doivent donc être ajoutées explicitement au contrat backend-neutral plutôt que détournées vers des erreurs.
3.3 Persistance PostgreSQL actuelle
Le chemin principal 0.3.15 est transactionnel :
BEGIN
tentative d'insertion du canonique
si collision d'identité : verrouillage de la ligne existante
comparaison avec le canonique courant
insertion/idempotence de l'observation
COMMIT
Le verrou de collision repose sur la ligne canonique et permet de sérialiser deux écritures concurrentes de la même signature.
Le cas étroit ActiveIncomingTruncatedLogs permet déjà de traiter l'entrant tronqué comme observation sans remplacer le canonique complet. Toute divergence non reconnue reste un content_conflict.
La rétention actuelle porte sur le canonique et distingue :
Full
Archived
Purged
ForceRehydrate
Les migrations enregistrées sont V000, V001 et V002. Leur registre, leurs ressources et leurs checksums sont stables et intouchables.
3.4 Chemin Worker actuel lors d'un conflit
Le Worker 0.3.15 possède une convergence run-local avant et autour de la persistance. Une divergence peut être rejetée avant que le Store ne dispose d'un mécanisme durable permettant de conserver la variante.
Si le Store remonte encore un content_conflict, le Worker le transforme en faute de persistance. Une tâche de persistance qui termine en faute provoque ensuite l'arrêt de la source concernée et un état terminal de route.
Cette chaîne est précisément ce que 0.3.16 doit modifier :
content divergence conservable
-> Store arbitre sous verrou
-> variante durable
-> conflit durable si nécessaire
-> outcome non terminal
-> Worker continue
Le cache de convergence run-local ne doit plus pouvoir prendre une décision plus forte que le Store durable. Il pourra rester un accélérateur uniquement s'il ne supprime jamais l'arbitrage durable nécessaire.
3.5 Erreurs Store temporaires et terminales
0.3.15 ne possède pas encore de politique Worker dédiée de retry Store avec classification backend-neutral complète.
Un défaut PostgreSQL remonté comme faute de persistance tend donc à devenir terminal pour la route, même lorsqu'il correspond à une indisponibilité temporaire potentiellement récupérable.
Cette responsabilité doit être séparée du content_conflict et de la reconnexion Transport.
3.6 Retry et reconnexion Transport actuels
L'audit confirme des mécanismes déjà présents :
- HTTP possède son propre retry de requête borné dans le Transport ;
- WebSocket possède
max_retries, un délai initial et un délai maximum avec backoff exponentiel ; - Yellowstone possède également une boucle de reconnexion/reprise bornée et un backoff ;
- Config sait déjà projeter des réglages Transport typés vers ces composants.
0.3.16 ne doit donc pas créer un second moteur concurrent. Le travail attendu est une extension/cohérence des contrats déjà propriétaires de la reconnexion réseau.
3.7 Store Desk actuelle
ksp-app-store-desk reste une application d'inspection via ksp-store-lib, sans SQL direct ni dépendance PostgreSQL directe.
L'inspection actuelle sait présenter les surfaces RAW existantes mais ne possède pas encore de DTO/query/action pour :
variants
open conflicts
resolution history
canonical transitions
manual resolution
synthetic merge lineage
Ces contrats doivent apparaître d'abord dans Store API/lib, puis seulement dans le bridge Tauri et le frontend.
4. Réaudit externe du 19 septembre 2026
Sources réauditées :
- Solana
getTransaction; - Solana
getBlock; - Solana RPC JSON Structures ;
TransactionStatusMeta/UiTransactionStatusMetaactuels ;- collecteur de logs SVM/Agave actuel ;
- documentation PostgreSQL actuelle pour
INSERT ... ON CONFLICT,SELECT ... FOR UPDATEet isolation transactionnelle.
4.1 logMessages : seule relation de complétude automatiquement admise
Le collecteur SVM actuel conserve une limite de 10 000 octets. Lorsqu'une nouvelle ligne ferait atteindre ou dépasser la limite, il ajoute une seule ligne exacte :
Log truncated
puis n'ajoute plus les lignes suivantes.
La relation KSP sûre est donc limitée au cas suivant :
A = [l0, l1, ..., ln, "Log truncated"]
B = [l0, l1, ..., ln, ln+1, ...]
avec égalité exacte de toutes les autres composantes canoniques pertinentes.
Alors :
A < B
au sens de la complétude des logs seulement.
Cette preuve permet les deux sens :
- canonique complet + entrant tronqué ->
CompatibleLessComplete, sans promotion ; - canonique tronqué + entrant complet ->
CompatibleMoreComplete, avec promotion atomique.
Ne sont pas des preuves de troncature :
- une liste simplement plus courte sans marqueur ;
- une divergence avant le marqueur ;
- plusieurs listes différentes sans relation de préfixe stricte ;
- un provider connu pour tronquer ;
- la seule longueur en octets ou en nombre de lignes.
4.2 innerInstructions
La réponse RPC officielle distingue selon la forme de réponse des tableaux, null, ou une omission dans certains modes.
Aucune relation générale null < [] < valeur n'est prouvée.
Politique 0.3.16 :
valeurs identiques -> Exact sur ce champ
valeurs différentes -> Conflict/Incomparable
absence/null/[] -> aucune dominance automatique
4.3 loadedAddresses
Les structures RPC officielles indiquent que loadedAddresses est présent comme objet dans les réponses JSON/full, mais volontairement omis avec certains encodages/modes comme jsonParsed ou accounts.
L'omission peut donc être structurelle et dépendre de la sérialisation, pas de la qualité intrinsèque d'une observation.
Politique : aucune promotion automatique fondée sur présence/absence de ce champ.
4.4 returnData, computeUnitsConsumed et costUnits
Ces champs sont optionnels dans les structures UI et peuvent être omis selon le mode de réponse. La documentation RPC montre également des cas où returnData apparaît à null dans des exemples alors que son type documentaire principal est optionnel.
Politique :
même valeur présente -> Exact sur le champ
valeurs présentes différentes -> Conflict
absent/null/présent -> Incomparable tant qu'un contrat plus précis n'est pas prouvé
4.5 Token balances
preTokenBalances et postTokenBalances sont optionnels dans la représentation de statut et la documentation RPC distingue tableaux et null.
Une liste vide est une valeur métier possible et ne doit jamais être traitée comme une troncature.
Politique : aucune dominance automatique hors égalité exacte.
4.6 Rewards
rewards peut être tableau, null ou omis selon le mode et showRewards/rewards demandé. KSP utilise déjà certaines routes avec rewards désactivées.
Politique : l'absence de rewards n'est pas une preuve d'une variante moins complète dans le comparateur générique. Toute règle future devra intégrer explicitement le contrat d'acquisition qui a produit la représentation.
4.7 maxSupportedTransactionVersion
Ce paramètre contrôle quelles versions de transaction le client déclare pouvoir traiter. Il ne constitue pas un score de qualité entre deux payloads déjà acquis.
Une différence d'acquisition causée par une limite de version doit être diagnostiquée au niveau Transport/acquisition, pas transformée en règle de promotion canonique.
4.8 Conclusion champ par champ
Pour 0.3.16, la seule dominance automatique de contenu autorisée au départ est :
relation de troncature logMessages strictement prouvée
Tous les autres champs restent fail-closed : égalité exacte ou Conflict/Incomparable.
Cette politique pourra être étendue dans une future tranche uniquement après preuve normative et canari KSP dédié.
5. Modèles physiques étudiés
5.1 Modèle A — modifier fortement la ligne canonique V001
Principe : transformer ksp_raw_transactions en identité portant directement un pointeur de variante, ajouter les variantes puis déplacer progressivement le payload hors de la ligne historique.
Avantages :
- modèle relationnel final compact ;
- un seul point d'identité logique.
Inconvénients :
- forte modification du schéma stable V001 ;
- risque élevé sur les lecteurs/rétention/tests existants ;
- migration volumique plus intrusive ;
- rollback de release plus difficile ;
- davantage de surfaces changées dans une seule étape.
Décision : non retenu pour 0.3.16.
5.2 Modèle B — ledger de variantes + sélecteur sidecar + projection V001
Principe :
ksp_raw_transactions
= projection canonique compatible V001
variant ledger V003
= toutes les représentations conservées à partir de 0.3.16
canonical selector V003
= variant_id actuellement sélectionné
conflict/resolution journal V003
= historique immuable des décisions
Une promotion effectue dans une même transaction :
lock identité V001
verify expected canonical/revision
persist incoming variant if new
update canonical selector
update V001 canonical projection
append canonical transition / resolution event
attach observation to exact variant
commit
Avantages :
- V000/V001/V002 inchangées ;
- anciens lecteurs restent compatibles ;
- rollback conceptuel vers une ancienne variante sans Internet ;
- migration additive et progressive ;
- verrou existant de l'identité réutilisable ;
- séparation nette identité / variante / historique.
Inconvénients :
- duplication contrôlée entre la projection V001 et la variante canonique ;
- invariants de cohérence à tester entre projection et sélecteur ;
- rétention plus complexe ;
- nouvelles écritures doivent maintenir plusieurs structures atomiquement.
Décision : modèle retenu.
5.3 Modèle C — variante adressée uniquement par content_hash
Principe : utiliser le hash comme clé physique principale et considérer deux hashes identiques comme même variante.
Avantages :
- déduplication simple ;
- index de recherche efficace.
Inconvénients :
- confond hash cryptographique et preuve d'égalité ;
- rend la sémantique de collision implicite ;
- complique le cas tombstone/payload absent ;
- impose une hypothèse plus forte que nécessaire au contrat de domaine.
Décision : rejeté.
6. Identité d'une variante
La variante utilise un identifiant surrogate stable :
variant_id
content_hash reste stocké et indexable, mais sert à :
- accélérer la recherche de candidats égaux ;
- vérifier l'intégrité ;
- fournir un diagnostic redacted ;
- aider les tombstones de rétention.
Il ne suffit jamais seul à conclure Exact lorsque les deux payloads sont disponibles.
Pour une identité verrouillée :
same hash
-> candidate equality
-> exact compare of canonical representation
-> Exact only after exact comparison
Aucune contrainte d'unicité ne doit transformer automatiquement (signature, content_hash) en preuve métier. La sérialisation par verrou de l'identité permet de prévenir les doubles insertions concurrentes après comparaison exacte.
7. Structures V003 conceptuelles retenues
Les noms SQL exacts seront figés dans la tranche de migration, mais les rôles sont désormais décidés.
7.1 Variant ledger
Chaque variante conserve au minimum :
variant_id
transaction identity/signature
origin kind = native | synthetic
canonical raw format id/version
slot
block_time
content_hash
payload ou état de rétention
created_at
Une variante native représente exactement un payload effectivement reçu.
Une variante synthétique doit être explicitement marquée et posséder une lignée de parents.
7.2 Canonical selector
Une structure sidecar contient au minimum :
transaction identity
canonical_variant_id
canonical_revision
updated_at
canonical_revision sert de garde optimiste aux actions Desk afin d'éviter une résolution sur une vue périmée.
7.3 Variant observations
À partir de V003, chaque observation nouvelle est durablement liée à la variante réellement reçue.
L'écriture reste atomique avec la variante et l'outcome de persistance.
Point historique important : les observations créées avant 0.3.16 ne contiennent pas toujours le payload reçu. Le cas logMessages tronqué accepté en 0.3.15 conserve l'observation mais pas une variante complète de l'entrant.
Il est donc interdit de prétendre reconstruire après coup l'association exacte de toutes les observations historiques.
Le bootstrap V003 doit les exposer comme observations héritées dont la variante exacte est unknown/legacy, ou comme association explicitement qualifiée et non comme preuve native.
7.4 Conflict case
Une identité peut posséder un dossier de conflit durable unique et réouvrable.
Le dossier porte :
conflict identity
status = Open | Resolved
revision
current canonical variant
latest classification/reason
created_at
updated_at
Les variantes participantes sont conservées séparément.
7.5 Journal de transitions et résolutions
Le journal est append-only.
Types conceptuels :
AutoPromoteMoreComplete
ManualPromoteVariant
KeepCurrentAndResolve
RestorePreviousCanonical
ReopenConflict
CreateSyntheticMerge
Chaque événement garde :
action id
conflict id optionnel
from variant id optionnel
to variant id
expected revision
origin = system | operator
result
created_at
Aucun payload arbitraire ni secret ne doit être requis dans les logs sûrs.
7.6 Parents de variante synthétique
Une fusion assistée crée une nouvelle variante :
origin = synthetic
parents = [variant A, variant B, ...]
Elle ne réécrit jamais A ou B et ne se présente jamais comme une observation provider native.
8. Migration strategy
8.1 Invariant principal
V000/V001/V002 restent byte-identiques.
V003 ajoute des ressources nouvelles et, si nécessaire, des index/contraintes nouvelles via une migration indépendante enregistrée avec son propre checksum.
8.2 Bootstrap des identités existantes
La migration ne doit pas inventer des variantes historiques perdues.
Pour chaque identité déjà présente, le système peut créer une variante bootstrap représentant l'état canonique encore réellement disponible :
Full: payload canonique actuel copié comme variante bootstrap ;Archived: payload exact récupéré depuis l'archive locale existante et porté comme variante archivée ;Purged: variante tombstone seulement, sans prétendre disposer encore des bytes perdus.
Les observations historiques restent qualifiées legacy lorsqu'aucune preuve ne permet de les rattacher à la variante exacte reçue.
8.3 Migration volumique
La création de schéma et le bootstrap de données doivent être séparables si le volume réel rend un backfill monolithique trop coûteux.
La stratégie préférée est :
- créer V003 de façon additive ;
- permettre un bootstrap paresseux d'une identité sous son verrou lorsque sa projection V003 manque ;
- fournir un backfill borné/idempotent de préparation si nécessaire pour réduire le coût au premier accès ;
- ne jamais bloquer la migration de schéma sur une reconstruction externe.
Cette stratégie sera testée sur une copie PostgreSQL réaliste avant gate final.
9. Atomicité et concurrence
9.1 Même identité, deux entrants concurrents
L'identité V001 reste le mutex transactionnel naturel.
Séquence cible :
BEGIN
SELECT canonical identity FOR UPDATE
ensure V003 bootstrap
reload canonical selected variant
compare incoming against locked canonical
persist or reuse exact incoming variant
attach incoming observation
apply promotion/conflict transition if required
update compatibility projection if canonical changes
append event
COMMIT
Deux transactions ne peuvent donc pas promouvoir simultanément à partir du même canonical_revision sans que la seconde réévalue l'état après attente/verrou.
9.2 Action Desk concurrente avec ingestion
Toute action manuelle contient :
expected canonical_variant_id
expected conflict revision
Si l'état a changé, l'action échoue comme stale action et l'UI recharge. Elle ne force jamais silencieusement un ancien choix.
9.3 Hash collision
Le hash réduit l'espace de recherche mais l'égalité exacte des représentations disponibles tranche l'idempotence.
Si deux payloads différents ont le même hash, ils restent deux variantes différentes et le diagnostic doit le signaler comme anomalie de hash, pas les fusionner.
10. Relation de qualité
Le comparateur interne utilise cinq relations :
Exact
CompatibleLessComplete
CompatibleMoreComplete
Conflict
Incomparable
Incomparable est nécessaire pour représenter une relation partielle avant projection vers le comportement durable. Dans 0.3.16, un Incomparable est conservé comme conflit durable fail-closed.
10.1 Exact
Tous les éléments du contrat canonique sont égaux.
Conséquence : observation supplémentaire/idempotente sur la même variante.
10.2 CompatibleLessComplete
Seul un manque explicitement prouvé existe et le canonique domine l'entrant.
En première version, seul le cas de troncature logMessages prouvée est admis.
Conséquence : conserver le canonique ; conserver la variante reçue si elle n'existe pas ; rattacher son observation ; ne pas ouvrir de conflit nécessitant action humaine.
10.3 CompatibleMoreComplete
L'entrant domine le canonique selon la même preuve stricte.
Conséquence : persister l'entrant puis promotion atomique ; conserver l'ancien canonique ; ajouter un événement automatique réversible.
10.4 Conflict
Une contradiction prouvée existe.
Conséquence : conserver les deux variantes, conserver le canonique courant, ouvrir/compléter le conflict case, Worker non terminal.
10.5 Incomparable
Aucune contradiction simple n'est nécessairement démontrée, mais aucune dominance sûre n'est prouvée.
Conséquence 0.3.16 : même traitement durable prudent qu'un conflit, avec reason code distinct.
11. Ownership du comparateur
La décision de convergence doit être backend-neutral et réutilisable sous verrou par le backend.
Le contrat de relation appartient à la surface Store RAW commune ; il ne doit pas être une heuristique privée du Worker.
Décision d'ownership :
ksp-store-apiporte les types de relation, reason codes, données backend-neutral et le comparateur pur de convergence RAW ;- le comparateur appartient au contrat de domaine Store RAW parce qu'il doit être identique pour tous les backends et directement appelable par
ksp-store-postgres-libsous verrou sans dépendance inverse versksp-store-lib; ksp-store-postgres-libapplique cette relation dans la transaction durable et reste propriétaire de la sérialisation PostgreSQL, des verrous et des mutations atomiques ;ksp-store-libexpose uniquement la façade commune et les opérations d'inspection/résolution ; il ne devient pas une dépendance du backend ;ksp-raw-transaction-libreste l'autorité sur la canonicalisation RAW v1 et fournit les golden/canaris utiles, sans devenir propriétaire de la politique Store de convergence ;- le Worker peut utiliser la relation à titre de métrique/optimisation, mais le Store verrouillé reste l'autorité de la décision durable.
La tranche API devra encore vérifier le graphe Cargo avant de fixer le fichier/module Rust exact, mais la frontière de crate est fermée : le comparateur backend-neutral appartient à ksp-store-api. Aucune nouvelle dépendance circulaire n'est admise.
12. Rétention, archive et rehydration
12.1 Règle générale
Une promotion réversible exige de conserver les bytes nécessaires au retour arrière.
Une variante :
- actuellement canonique ;
- participante à un conflit ouvert ;
- ancien canonique référencé par un événement de restauration ;
- parent requis d'une variante synthétique ;
doit pouvoir être archivée mais ne doit pas être purgée de manière irréversible tant que le contrat de restauration dépend d'elle.
12.2 Full -> Archived
Autorisé si les bytes exacts sont conservés dans la surface d'archive V003 et que la restauration locale est possible.
12.3 Purged
Purged reste possible pour des variantes non épinglées par les invariants ci-dessus, selon politique de rétention explicite.
Un ancien canonique nécessaire à RestorePreviousCanonical est épinglé contre la purge.
12.4 ForceRehydrate
ForceRehydrate ne réécrit jamais arbitrairement une variante existante :
- si les bytes exacts sont encore disponibles, égalité exacte normale ;
- si seul un tombstone legacy subsiste, le hash peut vérifier une attente d'intégrité mais n'est pas présenté comme preuve d'égalité historique ;
- un payload réellement différent devient une nouvelle variante ;
- une représentation plus complète peut ensuite être promue selon le comparateur.
13. Machine d'état du conflict case
États :
Open
Resolved
Transitions :
new Conflict/Incomparable -> Open
Open + KeepCurrent -> Resolved
Open + PromoteVariant -> Resolved
Open + SyntheticMerge -> Resolved
Resolved + new unresolved variant -> Open
Resolved + explicit Reopen -> Open
Resolved + RestorePreviousCanonical -> Resolved with new journal event
Le statut est volontairement petit. Le détail de la décision vit dans le journal append-only et non dans une multiplication d'états terminaux.
14. Outcomes Store cibles
Les noms Rust exacts seront figés dans pre.002, mais les catégories suivantes sont requises :
InsertedCanonical
ObservedExact
ObservedCompatibleLessComplete
PromotedCompatibleMoreComplete
QuarantinedConflict
Rehydrated
SkippedPurged
QuarantinedConflict n'est pas une erreur de transport ni une erreur terminale Store. C'est un succès de durabilité avec réconciliation canonique encore ouverte.
15. Store retry — ownership et sémantique
15.1 Classification
ksp-store-api expose une classification backend-neutral :
Transient
Terminal
ksp-store-postgres-lib mappe les erreurs PostgreSQL réelles vers cette classification.
Exemples transients à couvrir par tests :
- connexion interrompue ;
- timeout/pool temporairement indisponible ;
- serialization failure lorsque le niveau choisi peut la produire ;
- deadlock détecté et transaction entièrement rejouable.
Exemples terminaux :
- checksum/migration/schema incompatible ;
- contrat modèle invalide ;
- payload invalide ;
- violation d'invariant non récupérable ;
- politique de retry épuisée.
Un conflit de contenu conservé durablement n'est plus dans cette classification d'erreur.
15.2 Orchestration
Le Worker possède l'orchestration de retry de persistance, car il possède :
- l'admission ;
- la concurrence de persistance ;
- le lifecycle ;
- le backpressure ;
- le health ;
- le Stop/drain.
Le backend Store reste responsable de l'atomicité d'une tentative et de la classification de sa faute ; il ne masque pas une boucle de retry indéfinie au Worker.
15.3 Paramètres de politique
Le type Worker doit accepter :
initial_delay
max_delay
backoff_multiplier
max_attempts = bounded | explicitly unbounded
jitter bounded
reset after successful durable persistence
Les valeurs numériques par défaut seront calibrées par tests ciblés avant la tranche Worker retry ; leur ownership et leur sémantique sont cependant fermés ici.
15.4 Backpressure
Pendant une indisponibilité Store :
pending durable write retained
retry according to policy
persistence concurrency remains bounded
admission queue remains bounded
pressure propagates upstream when capacity fills
no success counter before durable commit
no silent drop
Si max_attempts est épuisé, la route peut devenir terminale.
Si un mode explicitement illimité est configuré, la route reste Blocked/non terminale jusqu'à recovery ou Stop, tout en conservant des files bornées.
16. Transport reconnect — ownership et extension
La reconnexion réseau reste propriété de ksp-onchain-transport-lib.
Le moteur existant WebSocket/Yellowstone doit être étendu plutôt que dupliqué avec une politique typée comprenant :
initial_delay
max_delay
backoff_multiplier
max_attempts
reset_after_stable_duration
bounded_jitter
Les defaults de compatibilité doivent conserver le comportement 0.3.15 lorsqu'un nouveau réglage n'est pas fourni.
HTTP conserve sa sémantique de retry de requête ; il ne doit pas être forcé dans une abstraction de « reconnect » si cette abstraction dégrade son contrat.
ksp-config-lib reste le propriétaire de la représentation de configuration et projette les valeurs validées vers Transport.
Une reconnexion réussie ne prouve jamais que la coverage est continue. Gaps/replay/repair restent des mécanismes séparés.
17. Health et métriques source-neutral
Les compteurs cibles sont source-neutral et redacted :
raw_variant_created_count
raw_exact_observation_count
raw_less_complete_observation_count
raw_canonical_promotion_count
raw_unresolved_conflict_count
raw_auto_resolved_count
store_retry_attempt_count
store_retry_exhausted_count
store_blocked_duration_ms
Les noms pourront être harmonisés avec les conventions exactes du Worker API, mais aucune dimension provider ne doit décider de la santé canonique.
Le lifecycle peut rester Running pendant :
unresolved conflict -> health Degraded
transient Store retry -> health Degraded
Store pressure saturating bounded capacity -> health Blocked
La route devient terminale seulement lorsqu'une erreur structurelle l'exige ou lorsque la politique de récupération est épuisée.
Deux notions de complétude doivent rester distinctes :
acquisition_complete
canonical_complete
18. Store Desk — scope retenu
18.1 Lecture
Ajouter via ksp-store-lib seulement :
RAW Conflicts
RAW Conflict History / Resolution History
variant detail
canonical transition history
observation/provenance per variant
Les listes doivent être paginées/bornées ; DataTables serverSide est privilégié lorsque le volume le justifie.
18.2 Actions
Actions structurées :
Promote variant
Keep current canonical + Resolve
Restore previous canonical
Reopen conflict
Assisted merge, seulement si les règles prouvées l'autorisent
Chaque action envoie des identifiants typés et la revision attendue ; pas d'éditeur JSON libre comme mécanisme principal.
18.3 Architecture Desk
Conserver :
lib.rs = exports only
tauri.rs = bridge IPC
modules Rust spécialisés
Vite + TypeScript
tracing frontend des actions sans valeurs sensibles
ksp-store-lib seule façade Store
cargo tauri dev/build pour les gates Desk
19. Risques identifiés
19.1 Divergence projection V001 / sélecteur V003
Mitigation : mise à jour atomique sous le même verrou, tests de canari et vérification d'invariant lors des lectures sensibles.
19.2 Double variante concurrente
Mitigation : verrou identité, recherche hash comme préfiltre puis comparaison exacte avant insertion.
19.3 Stale action Store Desk
Mitigation : canonical_revision + conflict revision attendues.
19.4 Rétention détruisant le rollback
Mitigation : pin explicite des variantes nécessaires à l'historique/restauration ; archive locale admise, purge interdite tant que l'invariant l'exige.
19.5 Migration volumique
Mitigation : V003 additive, bootstrap idempotent/paresseux et éventuel backfill borné plutôt qu'une reconstruction externe ou une migration monolithique obligatoire.
19.6 Retry Store créant une file infinie
Mitigation : réutiliser les capacités bornées existantes, backpressure upstream et aucun buffer latéral non borné.
19.7 Reconnect masquant des gaps
Mitigation : reconnect et coverage restent deux compteurs/contrats séparés.
19.8 Faux ordre de qualité hors logs
Mitigation : Incomparable fail-closed pour tous les champs sans preuve normative.
19.9 Historique legacy incomplet
Mitigation : ne jamais prétendre reconstruire une variante pré-V003 non stockée ; qualifier explicitement l'observation comme legacy/unknown.
20. Découpage recalibré du programme 0.3.16 -> 0.3.18
Le découpage initial de pre.001 jusqu'à pre.022 est abandonné. Même si chaque tranche restait petite isolément, plus de vingt prereleases auxquelles s'ajouteraient les fix.* ne constituent pas une cible réaliste pour la règle KSP « une version = au maximum une session ».
Le programme conserve les mêmes invariants techniques, mais il est réparti en trois vertical slices stables. La cible est de rester autour de sept à huit prereleases par version, avec une marge réelle pour les fixes. Une release ne doit pas être artificiellement maintenue ouverte pour absorber le scope de la suivante.
20.1 0.3.16 — fondations RAW multi-variantes et ingestion non terminale
Objectif de fermeture : disposer d'une persistance durable des variantes et conflits, d'une promotion automatique strictement prouvée, et permettre au Worker live de continuer lorsqu'une divergence conservable est enregistrée.
0.3.16-pre.001
Gate d'audit, architecture, sizing, plan et validation. Cette tranche est déjà appliquée.
0.3.16-pre.002
Contrats Store API backend-neutral : variant identity, relation de qualité, outcomes de persistance, reason codes et contrats minimaux nécessaires au conflit durable.
0.3.16-pre.003
Migration V003 : registre, resources et schéma multi-variantes/selector/conflict minimal ; canaris garantissant l'immuabilité byte/checksum de V000/V001/V002.
0.3.16-pre.004
Backend PostgreSQL : bootstrap V003, variant ledger, rattachement exact des nouvelles observations à leur variante, idempotence et concurrence d'insertion.
0.3.16-pre.005
Comparateur partagé : Exact, CompatibleLessComplete, CompatibleMoreComplete, Conflict, Incomparable ; canaris logMessages bidirectionnels et politique fail-closed sur les autres champs.
0.3.16-pre.006
Sélecteur canonique et promotion atomique : revision, projection V001 cohérente, conservation de l'ancien canonique et journal minimal des transitions.
0.3.16-pre.007
Conflit durable minimal + intégration Worker : variante conservée, case ouverte, outcome non terminal, suppression/restriction de l'arbitrage run-local plus fort que le Store et health Degraded lorsque requis.
0.3.16-pre.008
Hardening ciblé de la vertical slice : concurrence, rollback transactionnel, régression Job Backfill/Store, gates workspace, validation documentaire et préparation de 0.3.16-rel.001.
0.3.16-rel.001
Publication stable mécanique après gates propres. Aucun Store Desk ni retry Store complet n'est requis pour fermer cette release.
20.2 0.3.17 — résilience opérationnelle et cycle de vie des variantes
Objectif de fermeture : rendre l'acquisition résiliente aux indisponibilités Store/Transport et fournir les contrats backend-neutral d'inspection/résolution nécessaires aux opérateurs et à la future UI.
0.3.17-pre.001
Contrats Store API d'inspection/résolution, revisions attendues et classification backend-neutral des erreurs Store Transient/Terminal.
0.3.17-pre.002
Cycle de vie complet des conflict cases : participants, résolution, reopen, historique append-only et races ingestion/résolution.
0.3.17-pre.003
Rétention des variantes : archive, pins, purge guards, rollback local et ForceRehydrate exact/différent.
0.3.17-pre.004
ksp-store-lib : inspection, historique et actions backend-neutral ; maintien explicite de la compatibilité du Job Backfill existant.
0.3.17-pre.005
Worker : Store retry/backpressure/Blocked, exhaustion explicite, cancellation et drain pendant backoff.
0.3.17-pre.006
Transport/Config : reconnexion WebSocket/Yellowstone configurable en étendant les mécanismes propriétaires existants, sans confondre reconnexion et coverage et sans réimplémenter le retry HTTP.
0.3.17-pre.007
Hardening cross-layer et preuves live ciblées : conflits concurrents, outage/recovery Store, retry, reconnect et invariants de queue bornée.
0.3.17-pre.008
Gate technique final, réconciliation documentaire et préparation de 0.3.17-rel.001 ainsi que du prompt 0.3.18.
0.3.17-rel.001
Publication stable mécanique après gates propres.
20.3 0.3.18 — inspection et résolution opérateur Store Desk
Objectif de fermeture : exposer les variantes/conflits/résolutions déjà stabilisés par Store API/lib dans une UI Desk sûre, paginée, traçable et sans dépendance backend directe.
0.3.18-pre.001
Store Desk backend/Tauri : queries typées, pagination server-side et actions via ksp-store-lib uniquement.
0.3.18-pre.002
Frontend Vite/TypeScript : listes de conflits, historique, détail de variantes et transitions canoniques.
0.3.18-pre.003
Actions opérateur promote/keep/restore/reopen avec revision attendue, stale-action explicite et rafraîchissement cohérent.
0.3.18-pre.004
Fusion assistée/synthétique strictement bornée uniquement si les règles de fusion sont réellement prouvées ; sinon fermeture documentaire explicite du non-support, sans heuristique libre.
0.3.18-pre.005
Hardening UI/backend : redaction, pagination bornée, concurrence, cancellation, dependency boundaries et régressions Store Desk.
0.3.18-pre.006
Preuves end-to-end et gate technique/live final : PostgreSQL/Store/Worker/Desk, Tauri build/smokes pertinents et graphes Cargo lorsque requis par les règles.
0.3.18-pre.007
Réconciliation documentaire, CHANGELOG.md, ROADMAP.md, prompt de reprise suivant et préparation de 0.3.18-rel.001.
0.3.18-rel.001
Publication stable mécanique après gates propres.
20.4 Règles de sizing pour les trois releases
Le nombre exact de prereleases reste souple, mais les règles suivantes sont désormais normatives pour ce programme :
- une version ne doit pas dépasser une session normale ;
- une tranche peut être subdivisée si sa validation devient trop lourde ;
- un
fix.*corrige la tranche concernée sans obliger à compresser la suite ; - un scope non indispensable à la vertical slice de la release est reporté à la release suivante ;
- aucune fusion artificielle de migration, concurrence, Worker, Transport et UI n'est autorisée pour « économiser » un numéro de prerelease ;
- la fermeture stable de chaque release doit laisser le workspace dans un état cohérent et exploitable indépendamment du développement de la suivante.
21. Hors périmètre confirmé et déplacement des scopes réservés
Le programme 0.3.16 -> 0.3.18 n'implémente pas :
- multi-route/multi-stratégie du Job Backfill, désormais reporté après
0.3.18et ciblé par défaut sur0.3.19; - adaptation Backfill Desk correspondante, ciblée par défaut sur
0.3.20; - RAW -> STRUCTURAL ;
- persistence STRUCTURAL ;
- DECODED/DOMAIN ;
- majority voting provider ;
- provider priority ;
- fusion JSON libre ;
- reconstruction Internet comme rollback normal ;
- second pipeline d'acquisition ;
- dépendance Worker <-> Job Backfill.
Les anciennes réservations 0.3.17 = Backfill multi-route et 0.3.18 = Backfill Desk sont donc explicitement annulées par 0.3.16-pre.001-fix.001. Ce déplacement évite deux scopes concurrents sur les mêmes numéros de version.
Le Job Backfill actuel doit seulement rester compatible avec les contrats Store communs pendant 0.3.16 -> 0.3.18. Les numéros 0.3.19/0.3.20 restent des cibles de planning et pourront être réévalués par le gate d'ouverture correspondant sans réouvrir le programme RAW résilience déjà fermé.
22. Questions non bloquantes reportées aux tranches d'implémentation
Les décisions suivantes ne changent pas l'architecture du gate et peuvent être finalisées au moment où des mesures/tests réels existent :
- noms SQL définitifs des ressources V003 ;
- valeurs numériques par défaut de Store retry ;
- valeur par défaut du jitter et de
reset_after_stable_durationTransport ; - colonnes exactes des DataTables Store Desk ;
- stratégie de backfill eager ou lazy optimale après mesure du volume PostgreSQL réel.
Toute réponse à ces questions doit respecter les invariants fixés ci-dessus ; elle ne peut pas réouvrir un provider ranking ou une perte silencieuse.
23. Sources externes réauditées
- https://solana.com/docs/rpc/http/gettransaction
- https://solana.com/docs/rpc/http/getblock
- https://solana.com/docs/rpc/json-structures
- https://docs.rs/solana-transaction-status/latest/solana_transaction_status/struct.TransactionStatusMeta.html
- https://docs.rs/solana-transaction-status/latest/solana_transaction_status/struct.UiTransactionStatusMeta.html
- https://docs.rs/solana-svm-log-collector/latest/src/solana_svm_log_collector/lib.rs.html
- https://github.com/anza-xyz/agave/blob/master/svm/doc/spec.md
- https://www.postgresql.org/docs/current/sql-insert.html
- https://www.postgresql.org/docs/current/explicit-locking.html
- https://www.postgresql.org/docs/current/transaction-iso.html
24. Gate pre.001
Le gate est architecturalement fermé lorsque :
- le modèle sidecar/variant ledger est accepté comme base de développement ;
- aucune migration V000/V001/V002 n'est modifiée ;
- le surrogate
variant_idest l'identité physique et le hash n'est pas une preuve d'égalité ; - les observations futures sont rattachées à leur variante réelle ;
- l'historique legacy non reconstructible est explicitement qualifié ;
logMessagesest la seule dominance automatique admise ;- tous les autres champs restent fail-closed ;
- Store retry appartient à l'orchestration Worker avec classification backend-neutral fournie par Store ;
- Transport reconnect reste propriété Transport ;
- Store Desk passe exclusivement par Store-lib ;
- les risques de concurrence, rétention, stale action et migration volumique ont un mécanisme de contrôle explicite ;
- le découpage multi-version
0.3.16->0.3.18remplace la prévision initiale0.3.16-pre.001->pre.022trop longue pour une seule session.