Files
khadhroony-solana-project/docs/plans/022-V0_3_1_STORE_RAW_PLAN.md

60 KiB
Raw Blame History

Plan 0.3.1 — Store API RAW foundation

1. Statut et base

Ce plan est établi par 0.3.1-pre.001 à partir de la release stable v0.2.14 et de l'archive historique obligatoire khadhroony-bot3_v0.5.3-pre.005-fix010.zip.

La base KSP vérifiée à l'ouverture est :

workspace.package.version = 0.2.14
deltas/0.2.14/rel.001.md présent
prompts/020-V0_3_1_START_PROMPT.md présent
ksp-program-api présent
ksp-store-api absent
ksp-store-lib absent

L'archive opérateur ne contient pas de metadata Git exploitable ; le tag v0.2.14 ne peut donc pas être revérifié localement. La version Cargo, le delta rel.001, le prompt 020 et la surface Program API publiée concordent avec la base stable attendue.

Le journal opérateur fourni avec l'archive montre un baseline stable intégralement vert avant ouverture de 0.3.1 : audits Rust/Markdown, cargo check --workspace, Clippy et cargo test --workspace passent.

Après application de pre.001, la version Cargo cible est :

0.3.1-pre.1

2. Décision opérateur qui recalibre le prompt initial

Le brainstorming de pre.001 a séparé la création de l'API Store de son implémentation PostgreSQL.

La trajectoire devient :

0.3.1 = ksp-store-api uniquement
0.3.2 = ksp-store-lib + ksp-store-postgres-lib
        feature postgres par défaut
        PostgreSQL de référence via tokio-postgres

Cette décision remplace pour 0.3.1 la mission combinée ksp-store-api + ksp-store-lib décrite dans le prompt de démarrage. pre.001-fix.001 réconcilie immédiatement le ROADMAP.md afin que la trajectoire globale ne conserve pas une séquence désormais fausse.

Conséquences immédiates :

aucune dépendance PostgreSQL en 0.3.1
aucun tokio-postgres dans Cargo en 0.3.1
aucun pool
aucune table
aucun SQL
aucune migration
aucun std.store
aucun ksp-store-lib

0.3.1 doit stabiliser le contrat logique suffisamment pour que 0.3.2 puisse ensuite demander :

quelle représentation PostgreSQL satisfait le mieux ce contrat ?

et non :

comment exposer les tables déjà créées ?

3. Mission recalibrée

0.3.1 introduit ksp-store-api comme modèle logique commun et API fonctionnelle backend-agnostic du niveau N1 RAW/acquisition.

La crate doit posséder :

modèles objet/struct persistants communs
observations d'acquisition persistantes lorsque leur conservation est utile
références durables
outcomes d'écriture/idempotence
queries et pagination bornées
health/readiness communs utiles
contrats/capabilities backend extensibles
référence/format canonique de wake-up pour une donnée déjà persistée, conformément aux règles KSP-NOTIFY
cycle de rétention logique du RAW et tombstones anti-rebackfill

La release doit aussi auditer les autres données on-chain réellement utiles afin de distinguer explicitement :

modèle N1 persistant/replayable
modèle d'observation persistant
modèle event-only non persisté
DTO Transport seulement

La façade runtime concrète Store, la sélection d'un backend compilé et l'orchestration commune appartiendront à ksp-store-lib en 0.3.2. Les consumers ordinaires jobs/workers/apps dépendront alors uniquement de ksp-store-lib, qui réexportera la surface commune nécessaire de ksp-store-api.

ksp-store-api ne devient pas propriétaire de tous les messages inter-crates. Un modèle passif partagé qui ne représente aucune donnée persistée/rejouable et sert uniquement à transporter un événement entre acquisition et traitement relève préférentiellement de ksp-interface-lib. La frontière exacte doit être documentée avant création d'un tel type afin d'éviter deux structs concurrentes représentant le même fait.

ksp-store-api ne possède pas :

schéma physique
rows backend
SQL
migrations
pool/connection
transactions SQL
backend selection par lecture Config
tokio-postgres
PostgreSQL/MySQL/SQLite/RocksDB/ClickHouse types
Transport runtime
Program decoder
Materializer
scheduler/event bus/notification runtime

Le modèle public ksp-store-api est volontairement réutilisable par toute implémentation future. Un backend peut choisir une représentation physique radicalement différente sans modifier les DTO/entités et opérations logiques consommés par l'extérieur.

4. Modèle d'extensibilité backend

4.1 Graphe durable

Le graphe cible devient :

                              ksp-store-api
                         modèles + contrats communs
                                   |
                +------------------+------------------+
                |                                     |
                v                                     v
          ksp-store-lib                    ksp-store-postgres-lib
     façade/runtime commun                    impl PostgreSQL
       backend dispatch                         tokio-postgres
                |                                     ^
                | feature postgres (default) ----------+
                |
                +-- feature mysql ------> futur ksp-store-mysql-lib
                +-- autres features ----> futurs backends

Règles de dépendances :

ksp-store-lib              -> ksp-store-api
ksp-store-lib[postgres]    -> ksp-store-postgres-lib
ksp-store-postgres-lib     -> ksp-store-api
futur ksp-store-mysql-lib  -> ksp-store-api

ksp-store-postgres-lib -X-> ksp-store-lib
backend alternatif     -X-> ksp-store-lib

ksp-store-lib est donc le point de consommation normal du workspace, tandis que ksp-store-api reste le contrat d'implémentation partagé entre la façade et les backends.

4.2 Features de ksp-store-lib

0.3.2 introduira au minimum :

default = [postgres]
postgres -> dépendance optionnelle ksp-store-postgres-lib

Les futures features peuvent ajouter mysql, sqlite, rocksdb, clickhouse ou tout autre backend réellement implémenté. Plusieurs features backend peuvent être compilées simultanément ; la feature décide quels backends sont disponibles dans le binaire, jamais lequel est sélectionné au runtime.

La Config décide le backend actif parmi ceux compilés. Un backend KSP connu mais absent des features du binaire doit produire une erreur distincte et stable de type STORE_BACKEND_NOT_COMPILED, sans fallback silencieux vers PostgreSQL ou un autre backend.

4.3 Config, URI et secrets

ksp-store-api, ksp-store-lib et les crates backend ne lisent jamais directement .env ni les variables KSP/KSPB. ksp-config-lib reste propriétaire des documents, placeholders, .env, provenance et sensibilité.

La configuration future suit :

std.store / profil composite
        |
        v
ksp-config-lib
  résolution ${KSP_SECRET_*}
  + validation schema/sémantique
        |
        v
settings runtime ksp-store-lib
        |
        v
backend compilé sélectionné
        |
        v
crate backend privée

Règles fixées pour 0.3.2 et les futurs backends :

  • utiliser une URI/DSN lorsque le moteur possède une forme URI naturelle (postgresql://..., futur mysql://..., etc.) ;
  • permettre des options typées backend-specific uniquement lorsqu'elles sont réellement nécessaires et sans serde_json::Value opaque comme contrat runtime ;
  • considérer l'URI entière comme Secret dès qu'elle peut embarquer user/password/token ou autre credential ;
  • obtenir les secrets via des placeholders KSP_SECRET_* résolus par Config, avec inventaire dans .env.example au même delta que leur première utilisation ;
  • ne jamais versionner un vrai .env ni un URI/credential réel ;
  • ne jamais exposer URI, password, token, query secret ou chemin sensible dans Debug, logs, erreurs ou health ;
  • distinguer backend inconnu de backend KSP connu mais non compilé avant tentative de connexion ;
  • éviter tout fallback implicite vers PG*, .pgpass ou autre source d'environnement lue directement par le driver/backend lorsque Config a déjà fourni les settings effectifs.

La forme exacte de StoreSettings et du document std.store appartient au design de 0.3.2; 0.3.1 fixe seulement ces responsabilités et invariants.

4.4 Surface consumer

Les jobs/workers/apps ne dépendent pas directement de ksp-store-postgres-lib ni des futurs backends. Le chemin normal est :

ksp-job-* / ksp-worker-* / app
        -> ksp-store-lib
             -> réexporte les types API communs nécessaires
             -> expose Store/open/read/write/query communs
             -> dispatch vers le backend choisi par Config

Le consumer ne contient aucun #[cfg(feature = "postgres")] métier et ne branche pas sur postgres, mysql ou un nom de moteur pour lire/écrire les données.

4.5 Modèle public et modèle interne

Trois surfaces restent strictement distinctes :

ksp-store-api
    RawTransaction / observations persistantes / provenance / queries / outcomes
    = modèle objet et contrats communs à toutes les implémentations

ksp-store-lib
    Store / lifecycle / dispatch / settings runtime publics communs
    = façade de consommation normale

ksp-store-postgres-lib
    Postgres*Row / SQL / schema / migrations / pool / statements
    = implémentation privée au backend

Aucun type de row backend, handle de pool, statement préparé, migration ou nom d'objet SQL ne traverse ksp-store-lib vers ses consumers.

4.6 Frontière ksp-interface-lib / ksp-store-api

Les deux crates ne doivent pas devenir des définitions concurrentes du même modèle.

Règle de décision :

fait durable/replayable/queryable par Store
    -> ksp-store-api

observation durable attachée à un fait Store
    -> ksp-store-api

message passif event-only échangé entre composants
sans contrat de persistence
    -> ksp-interface-lib

signal canonique « donnée Store persistée disponible »
qui ne transporte qu'une référence durable
    -> ksp-store-api pour le format uniquement

shape spécifique à HTTP/WS/gRPC/provider
    -> Transport seulement

Lorsqu'un type Interface correspond exactement à une partie d'un modèle Store, ksp-store-api peut le réutiliser uniquement si cela ne crée ni dépendance de protocole ni sémantique incomplète. Sinon la composition effectue une conversion explicite. Aucun copier-coller de structs quasi identiques n'est accepté pour éviter une dérive silencieuse des deux contrats.

Cette règle sera réauditée au moment d'introduire les événements realtime (logsSubscribe, slot/vote/status) et les futurs types Interface nécessaires à l'acquisition.

5. Sources KSP relues

Le gate pre.001 a relu les sources prescrites par le prompt :

RULES.md
docs/000-README.md
docs/rules/RULES_GENERAL.md
docs/rules/RULES_KSP.md
docs/rules/RULES_RUST.md
docs/rules/RULES_DEPENDENCIES.md
docs/rules/RULES_DOCUMENTATION.md
docs/rules/FILE_CONTRACTS.md
docs/rules/VERSION_WORKFLOW.md
docs/rules/PROMPT_STRUCTURE.md

docs/architecture/000-README.md
docs/architecture/001-PROJECT_OBJECTIVES.md
docs/architecture/002-LAYERS_AND_DEPENDENCIES.md
docs/architecture/003-COMPONENT_CONTRACTS.md
docs/architecture/004-COMPONENT_INVENTORY.md
docs/architecture/005-DEPENDENCY_GRAPH.md
docs/architecture/006-WIRE_AND_PROGRAM.md
docs/architecture/007-EXECUTION_AND_POLICY.md
docs/architecture/008-DATA_MATERIALIZATION_AND_STORE.md
docs/architecture/009-ACQUISITION_WORKERS_AND_JOBS.md
docs/architecture/010-APPS_SERVICES_SCENARIOS_AND_CONTROL.md

Les règles directement structurantes sont :

KSP-API-001..007
KSP-CONFIG-001..018
KSP-NOTIFY-001..006
KSP-STORE-001..002
KSP-DATA-001..004
KSP-PIPE-001..007
DEP-KSP-001..005
DEP-CARGO-001..007
DEP-STORE-001..008
DEP-TRANSPORT-001..005
DEP-PIPE-001..008
DEP-WORKER-001..003
DEP-JOB-001..003

Le résultat normatif principal est :

Store API possède les contrats persistants
Store API -X-> Transport/Program/Materializer
Transport -X-> Store API
conversion explicite à la composition
persist -> commit -> notification
notification != backlog
at-least-once + persistence idempotente

6. N1 RAW : définition retenue

6.1 Rôle et critère d'admission

N1 est la couche acquisition / RAW replayable. Toutes les réponses Transport ne deviennent pas automatiquement des modèles Store.

Un type n'entre dans la taxonomie N1 que s'il apporte au moins une valeur durable au pipeline :

persistence / replay
future décomposition structurelle
future interprétation/décodage
état durable nécessaire au processing

Les données utiles uniquement comme déclencheur realtime peuvent avoir un modèle passif commun, mais ce modèle est event-only et ne reçoit pas automatiquement une capability Store.

Une même entité N1 ne peut agréger plusieurs sources que si chaque source candidate peut être convertie sans perte de la sémantique exigée par le contrat commun. Il est interdit de fabriquer un modèle universel rempli de Option<T> uniquement pour faire entrer des réponses incompatibles.

La règle d'admission est donc :

HTTP / WS / gRPC / provider response
        |
        v
satisfait intégralement le contrat canonique KSP ciblé ?
        |                         |
       oui                       non
        |                         |
        v                         v
même modèle N1             autre modèle / event
+ observation              ou DTO Transport seulement

Une matrice de compatibilité source -> modèle doit être produite avant de figer chaque nouvelle famille.

6.2 RawTransaction et observation

RawTransaction est la première famille persistante certaine de 0.3.1.

Les sources capables de fournir la transaction complète et la meta nécessaire au replay structurel doivent converger vers le même modèle, indépendamment de l'origine :

getTransaction
getBlock utilisé comme conteneur d'acquisition
transactionSubscribe lorsque la forme fournit le contenu complet
yellowstone Transaction
future source équivalente auditée

Le contrat sépare :

RawTransaction
    = fait RAW canonique source-independent et replayable

RawTransactionObservation
    = fait qu'une source donnée a observé/acquis cette transaction

Une réponse ne contenant qu'une signature, un status ou un sous-ensemble de champs ne produit jamais un RawTransaction artificiellement incomplet.

La même transaction acquise plusieurs fois converge vers une seule identité logique et plusieurs observations :

                 RawTransaction T
                        ^
                        |
        +---------------+---------------+
        |               |               |
 HTTP/provider obs  WS/provider obs  gRPC/provider obs

6.3 Logs de transaction vs notification de logs

Les logMessages contenus dans la meta d'une transaction appartiennent au RawTransaction. Ils ne constituent pas une seconde entité N1 RawLog.

Leur extraction individuelle appartient à la future décomposition STRUCTURAL :

RawTransaction
    -> STRUCTURAL
         -> TransactionLogLine / relation instructionnelle future

À l'inverse, logsSubscribe fournit un message realtime centré sur :

slot
signature
error/status
ordered log lines

Ce message peut déclencher un traitement ou l'hydratation de la transaction complète, mais il ne doit pas être confondu avec les logs extraits de RawTransaction.

Le candidat de travail est donc un modèle event-only tel que TransactionLogNotification/RawLogNotification, dont l'ownership sera tranché entre ksp-interface-lib et ksp-store-api selon la règle §4.6. Aucune persistence Store de ces notifications n'est requise par défaut.

Le pipeline realtime futur pourra faire :

logsSubscribe
    -> event passif
    -> worker/analyser
         -> éventuellement getTransaction(signature)
         -> persistence RawTransaction

Le Store n'envoie lui-même aucun événement.

6.4 RawAccountState et observation

Les réponses account complètes provenant de HTTP, WS ou Yellowstone peuvent potentiellement converger vers un même état canonique :

pubkey
slot/context utile
lamports
owner
executable
rent_epoch
data bytes exacts

Les détails propres à l'acquisition (write_version, signature source, startup, provider, transport, timing, etc.) appartiennent à RawAccountObservation lorsqu'ils sont utiles et disponibles.

RawAccountState/RawAccountObservation doivent être prévus dans ksp-store-api après validation de la matrice de compatibilité. Leur persistence concrète PostgreSQL peut rester non implémentée au début de 0.3.2.

Un compte demandé sous une forme déjà interprétée par le provider ne doit pas remplacer arbitrairement les bytes canoniques nécessaires à un futur decoder.

6.5 Transaction status

Les surfaces signature/status peuvent représenter un fait distinct d'une transaction complète :

signatureSubscribe
getSignatureStatuses
Yellowstone TransactionStatus
provider equivalent

Le candidat TransactionStatusObservation doit être étudié et, si la sémantique commune est démontrée, prévu dans l'API même si sa persistence n'est pas immédiatement implémentée.

Il peut plus tard servir à un worker/analyser pour détecter des transitions de commitment/status. Ce déclenchement n'est pas une responsabilité du Store.

6.6 Slot, vote, block et Yellowstone Entry

Classification actuelle :

slot/root/slotsUpdates
    -> event-only candidat ; persistence non justifiée actuellement

vote
    -> event-only candidat si la forme est suffisamment commune entre les ledgers/providers qui l'exposent

getBlock / Yellowstone Block
    -> source/conteneur d'acquisition de RawTransaction par défaut
       RawBlock persistant seulement si un besoin block-level non reconstructible est démontré

Yellowstone Entry
    -> examiné et non retenu actuellement : trop bas niveau et aucune destination replay/decomposition/event métier suffisante identifiée

RawBlock reste une IDEA, pas un modèle actif. Recréer le ledger bloc par bloc sans besoin supplémentaire irait à l'encontre de l'objectif KSP de transformer la donnée blockchain en unités directement exploitables.

6.7 Modèles et capabilities sont indépendants

La présence d'un modèle partagé n'implique pas une persistence.

Exemple :

TransactionLogNotification
    = peut exister comme contrat passif event-only

TransactionLogNotificationStore
    = ne doit pas exister sans besoin durable démontré

De même, RawAccountState peut être figé comme modèle commun avant que ksp-store-postgres-lib n'implémente sa capability.

Cette séparation permet d'inventorier correctement N1 sans forcer tous les backends à stocker toutes les familles.

7. Payload replayable

7.1 Décision de niveau API

ksp-store-api possède un payload de persistence RAW KSP, pas un payload réseau provider.

Le contrat candidat est un conteneur borné et versionné :

RawPayload
    format_id
    format_version
    bytes
    content_hash

format_id identifie un format de persistence KSP source-independent. Il ne vaut pas helius_json, yellowstone_proto ou un autre wire provider.

La conversion :

transport model
    -> conversion explicite
    -> RawPayload KSP

reste hors de ksp-store-api.

7.2 Pas de codec improvisé

0.3.1 ne choisit pas un codec réseau ou Solana concurrent. bincode reste interdit pour les codecs wire KSP.

Le premier format concret de transaction canonique doit pouvoir être produit par la future couche de conversion à partir des wires génériques KSP. Sa définition détaillée peut évoluer pendant 0.3.1 tant que les invariants publics restent :

source-independent
versionné
borné
lossless pour la normalisation générique couverte
Debug sans bytes
hashable/idempotent

Le Store ne prétend pas que des bytes arbitraires sans format connu sont replayables.

8. Future décomposition N1 -> niveau STRUCTURAL

Le nom de travail de N2 devient STRUCTURAL. CORE est abandonné dans le nouveau plan parce qu'il décrivait mal une opération qui consiste principalement à décomposer des données Solana brutes en sous-composants génériques.

La progression conceptuelle devient :

N1 RAW
    -> N2 STRUCTURAL lorsque le type est réellement décomposable
        -> N3 DECODED ultérieur
            -> N4 DOMAIN ultérieur

Cette progression n'est pas une chaîne obligatoire pour toutes les familles N1 :

RawTransaction     -> STRUCTURAL -> DECODED -> DOMAIN
RawAccountState    -> peut aller directement vers un decoder futur si aucune décomposition N2 utile n'existe
realtime event     -> worker/analyser, sans N2 nécessaire
status/slot/vote   -> peut rester un fait/event terminal

Le premier et seul cas N2 certain aujourd'hui est RawTransaction.

Le futur flux transactionnel doit permettre notamment :

RawTransaction
    -> StructuralTransaction/message
    -> account keys/références
    -> StructuralInstruction top-level #0
    -> StructuralInstruction top-level #1
    -> inner/CPI instruction #1.0
    -> inner/CPI instruction #1.1
    -> transaction logs/meta/balances/return data applicables

Chaque instruction/CPI doit posséder une identité/path stable et être traitable indépendamment. Un decoder absent ou défaillant pour une instruction ne bloque jamais les autres unités structurales de la transaction.

N2 n'est toutefois pas défini comme « transaction split » : si une autre famille N1 démontre plus tard une vraie décomposition structurelle utile, elle peut rejoindre ce niveau sans changer sa responsabilité.

N3 DECODED et N4 DOMAIN ne sont pas conçus dans 0.3.1. Le plan doit seulement garantir que le RAW transactionnel conserve tout ce qui sera nécessaire à une décomposition STRUCTURAL lossless pour les futurs decoders.

0.3.1 ne crée aucun type STRUCTURAL et ne dépend pas de ksp-program-api.

9. Provenance commune

La provenance candidate doit pouvoir représenter sans secret :

network/cluster
provider code
endpoint logical id optionnel
transport/protocol code
acquisition method
origin live/backfill/import/replay/repair
commitment optionnel
capture/session/filter logical ids optionnels
observed/received timestamp
source payload size/hash optionnels

Interdits :

endpoint URL
DSN
API key
token/password
payload source complet par simple diagnostic

Les codes provider/protocol/method restent ouverts et bornés ; aucun enum provider fermé n'est introduit dans Store API.

10. Identité et idempotence

10.1 Principes

L'API ne fournit pas de méthode has_* à appeler avant un write idempotent.

Le backend doit recevoir une opération unique avec ces sémantiques :

clé déjà absente
    -> Inserted

même clé + même contenu canonique
    -> AlreadyPresent

même clé + contenu différent
    -> Conflict

Cela évite un check-then-insert race.

10.2 Transactions

La clé logique d'un RawTransaction est dérivée au minimum du réseau et de la signature canonique. Un hash du contenu canonique protège contre une collision logique avec contenu divergent.

La signature ne doit pas être représentée comme un identifiant SQL i64 dans l'API.

10.3 Observations

Chaque observation possède une clé d'idempotence déterministe fournie par le producer/composition selon un contrat documenté. Deux acquisitions légitimes distinctes peuvent donc être conservées même si elles pointent vers le même RAW.

10.4 Événements et autres familles

Les événements realtime non persistés n'ont pas d'identité Store à inventer. Lorsqu'une nouvelle famille persistante est admise (RawAccountState, status durable, etc.), son identité d'idempotence doit être définie avec le modèle concret et ne jamais dépendre d'une primary key backend.

Aucune promesse exactly-once distribuée n'est faite.

11. API candidate

11.1 Contrats communs

ksp-store-api définit le modèle persistant et les opérations backend-agnostic qu'une implémentation doit satisfaire. 0.3.1 ne construit pas de backend, ne sélectionne aucun moteur et n'introduit pas encore la façade runtime concrète Store.

Le backend concret implémente des contrats publics d'extension, mais son modèle interne reste privé. En 0.3.2, ksp-store-lib::Store enveloppera ces contrats et deviendra la seule façade de consommation normale des jobs/workers/apps.

11.2 Object-safety et async

La future sélection runtime impose que les capabilities backend puissent être stockées derrière une abstraction dynamique sans connaître le moteur concret.

La stratégie candidate est :

capabilities Send + Sync
méthodes object-safe
futures boxed KSP-owned via un alias StoreFuture<'a, T>
future ksp-store-lib::Store compose les capabilities disponibles

Cette stratégie évite une dépendance async-trait uniquement pour masquer la transformation. Le coût d'une box de future est accepté au niveau Store, dominé par l'I/O de persistence, et doit rester mesurable si un chemin futur démontre le contraire.

11.3 Capabilities séparées

Éviter un trait monolithique exigeant tous les types de données à chaque backend.

La candidate est une composition fine :

StoreHealth
RawTransactionRead
RawTransactionWrite
RawTransactionObservationRead
RawTransactionObservationWrite

future RawAccountStateRead
future RawAccountStateWrite
future TransactionStatus* si persistence réellement retenue

Un modèle event-only ne crée aucune capability Store par défaut.

La future façade ksp-store-lib::Store peut exposer seulement les capabilities réellement compilées/supportées et produire une erreur stable lorsqu'une opération demandée n'est pas disponible.

11.4 Opérations atomiques métier

Aucun handle de transaction SQL/public n'est exposé.

Les invariants multi-écritures sont exprimés par des opérations logiques communes. Candidate initiale :

persist_transaction_acquisition(transaction, observation)
record_transaction_observation(observation)
get_raw_transaction(reference)
list_raw_transactions(query, page)
list_transaction_observations(query, page)

future persist_account_acquisition(state, observation)
future account reads/queries

health()

persist_transaction_acquisition signifie au contrat que le RAW et son observation réussissent atomiquement ou échouent ensemble. PostgreSQL réalisera cela avec une transaction privée en 0.3.2; un autre backend utilisera son mécanisme natif.

11.5 Outcomes

Le vocabulaire candidat :

RawWriteOutcome
    Inserted
    AlreadyPresent

Conflict
    = Error KSP stable, pas un succès silencieux

Pour une acquisition combinée, l'outcome doit distinguer au minimum l'état du RAW et de l'observation sans révéler de clé backend.

11.6 Pagination

Le concept historique PageRequest/PageSlice est repris avec :

default = 100
maximum = 500
cursor opaque et borné
ordre déterministe par query

Le cursor est un token d'implémentation retourné par la façade et réinjecté tel quel par le consumer. Son contenu ne devient pas API et ne doit pas être utilisé comme transport d'un SQL fragment.

12. Références, événements et cycle de vie N1

12.1 Références durables

ksp-store-api doit posséder les références compactes nécessaires aux lectures, au replay et aux relations entre couches, sans encoder une PK SQL.

Pour la première famille :

RawTransactionReference
    network
    signature

Les autres références sont ajoutées uniquement avec leur modèle réel.

12.2 Événements : ownership hors Store runtime

Le Store runtime persiste et lit. Il ne possède ni scheduler, ni event bus, ni mécanisme de publication et ne doit pas s'appuyer sur une notification native de base de données pour piloter le pipeline.

Les règles KSP-NOTIFY-001..006 restent applicables : ksp-store-api peut posséder le format canonique d'un wake-up signifiant qu'une donnée déjà commitée est disponible, sous forme de référence durable compacte. Cela ne signifie pas que la base ou ksp-store-lib publie elle-même le signal.

Le futur ordre d'orchestration est :

worker/acquisition
    -> Store.persist(...)
    -> succès/commit durable
    -> publisher worker/analyser/runtime
         -> notification canonique de référence si nécessaire

La notification persistée-data reste un wake-up et jamais la source de vérité du backlog. Les messages realtime d'acquisition (logsSubscribe, slot/vote, etc.) suivent la frontière §4.6 : ksp-interface-lib est le owner préféré lorsqu'ils ne représentent aucune donnée Store persistée.

12.3 Preuves de processing versionnées

Un simple champ :

processed = true

n'est pas une preuve durable suffisante.

L'audit kbot2/kbot3 confirme l'intérêt d'un ledger version-aware identifié au minimum par :

stage
processor identity/name
processor version
input identity
input hash/version
terminal status

Le contrat concret de processing sera introduit avec les jobs/couches concernés, pas dans la foundation RAW par anticipation. En revanche, les modèles N1 et leur rétention doivent rester compatibles avec ce futur ledger et avec le force replay.

12.4 Rétention, compression, archivage et purge

N1 ne doit pas imposer la conservation éternelle du payload RAW en stockage chaud.

Le cycle logique candidat, repris puis redessiné depuis kbot2, est :

Full
    -> Compacted (optionnel)
Full/Compacted
    -> Archived
        -> Purged

Ces états décrivent la disponibilité du payload, pas le succès des processors supérieurs.

La décision d'éligibilité appartient à un futur worker/job/maintenance policy qui vérifie les preuves de processing nécessaires. Le Store applique une transition bornée/atomique mais ne décide pas lui-même qu'une transaction est suffisamment décodée.

Une policy typique pourra exiger par exemple :

STRUCTURAL courant réussi pour le hash RAW courant
unités structurales dans un état terminal selon la coverage policy active
horizon de rétention minimal atteint
aucun traitement/replay explicitement en cours
archive durable confirmée si la policy exige une archive avant purge

L'expression « toutes les instructions sont décodées » ne peut pas être un absolu open-world : un nouveau decoder peut apparaître plus tard. Les états terminaux doivent donc être évalués par rapport à une coverage/version policy explicite. Une instruction marquée no_decoder/unsupported peut rester redécodable ultérieurement depuis N2 STRUCTURAL sans exiger le RAW si N2 a conservé les faits nécessaires.

La purge physique du payload ne supprime pas l'identité logique. Un tombstone minimal durable doit rester afin d'empêcher un backfill normal de réacquérir indéfiniment une transaction déjà traitée :

network
transaction signature / identité logique
slot lorsqu'il est connu
canonical format id/version
content hash
retention state
processing/retention provenance minimale nécessaire

Comportement attendu :

backfill normal + tombstone
    -> skip / AlreadyKnownPurged

backfill explicitement forcé
    -> réhydratation autorisée selon policy

Le tombstone ne doit pas prétendre remplacer le RAW pour replay. Si une nouvelle version STRUCTURAL a réellement besoin du document RAW purgé, une réhydratation forcée depuis une archive ou le réseau est nécessaire.

La policy est spécifique à chaque famille. RawAccountState, qui pourrait aller directement vers un decoder futur, ne peut pas réutiliser automatiquement la même politique de purge que RawTransaction.

0.3.1 doit préparer les contrats logiques de rétention/tombstone sans implémenter compression, stockage d'archive, cron/worker de purge ou maintenance PostgreSQL.

13. Health et diagnostics

La façade peut exposer un health commun minimal :

Unknown
Healthy
Degraded
Unhealthy

avec un backend code sûr et un code diagnostic borné si nécessaire.

Ne pas exposer dans l'API commune :

migration SQL state
pool handles
connection count détaillé par driver
server error brut
DSN
SQLSTATE arbitraire présenté comme contrat portable

Les diagnostics spécifiques à PostgreSQL appartiennent à ksp-store-postgres-lib. ksp-store-lib ne projette vers le contrat commun que les états réellement portables et sanitised.

14. Dependency graph 0.3.1

Graphe maximal par défaut :

ksp-store-api
└── ksp-core-lib

Aucune dépendance externe n'est nécessaire pour la foundation candidate :

pas de serde
pas de chrono
pas de tokio
pas de async-trait
pas de futures-util
pas de ksp-interface-lib tant qu'un type Interface réel n'est pas requis

Les timestamps persistants peuvent être représentés par un newtype KSP en unités Unix explicites plutôt que d'ajouter chrono par réflexe. Le choix exact est prouvé dans la tranche de modèle.

15. Audit historique kbot3

15.1 Surface auditée

L'archive historique a été extraite et les surfaces prescrites ont été relues, notamment :

ks-store/Cargo.toml
ks-store/README.md
ks-store/USAGE.md
ks-store/TODO.md
ks-store/src/lib.rs
ks-store/src/store.rs
ks-store/src/contracts/**
ks-store/src/postgres/**
ks-store/migrations/postgres/**
config/store.config.json
config/schemas/store.config.schema.json
ks-config/src/store.rs
docs/architecture/STORAGE_ARCHITECTURE.md
docs/guides/POSTGRES_STORAGE.md
docs/plans/V0_5_3_KS_STORE_NORMALIZATION_PLAN.md
olddocs/archivekbot2/docs/RAW_STORAGE_LIFECYCLE.md
olddocs/archivekbot2/docs/CORE_EXTRACTION_SELECTION_AND_REPLAY.md
olddocs/archivekbot2/docs/CORE_EXTRACTION_CONTRACTS.md

Le Store historique couvre 16 tables N1-N3, 240 ressources SQL atomiques et 79 index attendus. Cette surface confirme l'intérêt de la séparation façade/backend, de l'idempotence et des observations, mais elle est beaucoup trop large pour 0.3.1.

L'archive contient aussi les documents kbot2 historiques. Ils avaient déjà formalisé deux idées utiles : un cycle RAW full/compacted/archived/purged et un processing ledger versionné pour décider skip/replay. KSP reprend les concepts mais rejette le couplage à un simple processing_state global et redessine la rétention comme une policy externe au Store.

15.2 Matrice d'héritage

Concept historique Observation kbot2/kbot3 Décision Application KSP
séparation façade Store / PostgreSQL PostgreSQL et driver privés REPRENDRE API commune séparée ; façade + backend PostgreSQL reportés à 0.3.2
StoreOpenOptions / backend selection backend + options historiques partiellement opaques REDESSINER Config produit les settings ; ksp-store-lib sélectionne un backend compilé
health/readiness contrat backend-neutral présent REPRENDRE health minimal portable dans ksp-store-api
transaction canonique source-independent HTTP/WS/gRPC convergent vers une transaction canonique REPRENDRE RawTransaction commun seulement si la source satisfait le contrat complet
acquisition observations transaction + account observations séparées du RAW REPRENDRE concept N1 central ; transaction d'abord, account prévu après matrice de compatibilité
ancienne raw WS notification kbot2 l'a supprimée de la baseline persistante REPRENDRE logsSubscribe/events restent event-only par défaut ; pas de table de notification brute
logs transactionnels conservés puis extraits dans la couche Core historique REDESSINER restent dans RawTransaction, puis deviennent des unités N2 STRUCTURAL ; pas de RawLog persistant séparé
account observations/states N1 account observation + N2 account state existaient REDESSINER prévoir RawAccountState/observation ; N2 seulement si une décomposition réellement utile est démontrée
repository traits capabilities async séparées REPRENDRE capabilities read/write fines, aucune obligation de supporter toutes les familles
pagination 100 par défaut, 500 max, cursor opaque REPRENDRE mêmes bornes candidates, cursor toujours opaque
replay contracts sélection + force replay version-aware REPRENDRE futur replay piloté par processor/version/input hash, jamais par simple bool
processing_state RAW received/core_extracted/failed facilitait la queue REDESSINER peut servir d'index de travail futur mais ne constitue jamais la preuve durable unique de traitement
processing ledger stage + processor/version + input/hash + status REPRENDRE future preuve durable versionnée ; reportée aux couches/jobs concernés
retention lifecycle full/compacted/archived/purged déjà envisagé dans kbot2 REDESSINER états logiques backend-agnostic + policy externe + tombstone anti-rebackfill
CORE tables transaction, keys, instructions, CPI, logs, balances, return data REDESSINER future couche N2 renommée STRUCTURAL ; aucune surface créée en 0.3.1
DECODE coverage/events états decoded/ignored/unsupported/failed versionnés REPORTER future N3 ; utile à la policy de rétention mais pas défini dans la foundation RAW
materialization journal sorties génériques versionnées REPORTER future N3/N4
raw transaction table table physique unique par signature REPORTER modèle commun maintenant ; table privée à ksp-store-postgres-lib en 0.3.2
schema validation / migrations / indexes forte surface PostgreSQL REPORTER responsabilité ksp-store-postgres-lib
maintenance truncate/drop scripts destructifs dédiés REJETER aucune API runtime générique destructive ; rétention contrôlée distincte
Config Store backend/options historiques REDESSINER Config reste owner ; URI/secrets adaptés vers ksp-store-lib en 0.3.2
modèles partagés hors Store kbot3 recommandait de ne pas dupliquer les modèles communs REPRENDRE frontière explicite ksp-interface-lib / ksp-store-api, conversion explicite lorsque les sémantiques diffèrent

16. Audit PostgreSQL et driver de référence futur

16.1 Décision

Le choix opérateur est fixé pour la version suivante :

ksp-store-lib
    façade/runtime commun
    feature postgres = feature par défaut
    feature postgres -> ksp-store-postgres-lib

ksp-store-postgres-lib
    backend officiel de référence = PostgreSQL
    driver interne = tokio-postgres

Ce choix n'ajoute aucune dependency à 0.3.1.

16.2 État externe audité au 28 août 2026

Sources primaires actuelles consultées : documentation PostgreSQL et documentation/package tokio-postgres.

Résultats :

PostgreSQL stable courant : 18.6, publié le 13 août 2026
tokio-postgres courant : 0.7.18, publié le 12 juin 2026
edition tokio-postgres : Rust 2024
MSRV tokio-postgres : Rust 1.85
runtime async : Tokio
prepared statements : supportés
transactions / isolation : supportées
COPY in/out : supporté
pipelining : supporté
TLS : connector externe, à choisir explicitement en 0.3.2

tokio-postgres::Config peut être construit et renseigné programmaticalement. 0.3.2 devra éviter les chemins de configuration implicites : l'URI/les options effectives viennent de ksp-config-lib via les settings publics de ksp-store-lib, puis sont converties vers le backend PostgreSQL sans lecture directe de .env, PG* ou .pgpass.

16.3 SQLx

SQLx 0.9.0 a été réaudité comme alternative mais n'est plus retenu après décision opérateur.

La comparaison a néanmoins confirmé l'intérêt d'éviter dans KSP les defaults de connexion influencés par PG*/.pgpass lorsque le backend peut être construit explicitement depuis Config.

La performance ne constitue pas à elle seule la raison du choix : 0.3.2 devra optimiser les vrais chemins KSP avec prepared statements, batching/COPY lorsque justifié, transactions et indexes adaptés.

17. Contrat attendu de 0.3.2 sans le concevoir physiquement ici

0.3.2 introduira deux crates dans la même release :

ksp-store-lib
ksp-store-postgres-lib

ksp-store-lib devra :

dépendre de ksp-store-api
réexporter la surface API commune nécessaire aux consumers
exposer la façade Store et son lifecycle
posséder la sélection/dispatch des backends compilés
activer postgres par défaut
importer ksp-store-postgres-lib seulement sous feature postgres
accepter les settings effectifs fournis par Config
rejeter distinctement un backend connu mais non compilé
ne jamais exposer les types internes d'un backend

ksp-store-postgres-lib devra implémenter le même modèle objet et les mêmes contrats de ksp-store-api et pourra créer librement :

PostgresStoreBackend
Postgres*Row privés
migrations
schema version
prepared statements
indexes
pool
transaction handles privés
mapping Rust API <-> PostgreSQL
tokio-postgres

Les consumers jobs/workers/apps dépendront uniquement de ksp-store-lib, avec la feature par défaut ou une sélection explicite de features. Les crates backend alternatives dépendront de ksp-store-api, pas de ksp-store-lib.

La compatibilité PostgreSQL devra être testée par round-trip des modèles API et par introspection/validation du schema réel.

18. Sorties PostgreSQL du prompt initial explicitement déplacées

Le prompt de démarrage demandait encore à pre.001 un schéma PostgreSQL candidat, une migration policy et un gate PostgreSQL live parce que 0.3.1 devait initialement contenir API et implémentation. La décision de split rend ces sorties prématurées.

Elles sont donc classées ainsi :

Sortie initialement demandée Décision pre.001 Release propriétaire
tables/colonnes/PK/FK/indexes RAW REPORTER 0.3.2 ksp-store-postgres-lib
mapping u64/bytes/timestamps vers PostgreSQL REPORTER 0.3.2
migration layout/version/checksum REPORTER 0.3.2
schema introspection/conformance REPORTER 0.3.2
pool/TLS/timeouts REPORTER 0.3.2
PostgreSQL live gate REPORTER 0.3.2
SQL injection/statement policy exécutable REPORTER 0.3.2

Les invariants API qui contraignent déjà ce futur schéma restent fixés maintenant :

identités logiques non dépendantes d'une PK SQL
idempotence Inserted/AlreadyPresent/Conflict
atomicité RAW + observation
pagination bornée
payload versionné et borné
provenance sans secret
round-trip exact du modèle objet commun

Aucun nom de table candidat n'est donc produit par 0.3.1-pre.001. C'est une conséquence volontaire du split, pas une omission du gate.

La trajectoire roadmap corrigée est :

0.3.1  ksp-store-api RAW/observations
0.3.2  ksp-store-lib + ksp-store-postgres-lib
0.3.3  wires Interface nécessaires à l'acquisition/normalisation future
0.3.4  ksp-job-api + premier backfill
0.3.5  application backfill/inspection RAW

19. Threat model 0.3.1

Menace Réponse de design
payload hostile trop grand bornes à la construction des contrats RAW avant persistence
raw payload leak via Debug Debug manuel/redacted pour conteneurs sensibles
secret provider/endpoint provenance = ids/codes sûrs, jamais URL/credential
source partielle ne produit pas un modèle canonique incomplet ; autre model/event ou Transport only
mismatch HTTP/WS/gRPC matrice d'admission source -> modèle avant stabilisation de chaque famille
duplicate race write idempotent atomique, jamais has_* comme précondition
même clé / contenu divergent conflit explicite, pas d'overwrite silencieux
partial RAW + observation opération persist_*_acquisition atomique au contrat
cursor hostile taille/limit bornées ; cursor opaque non interprété par consumer
query non bornée aucune liste publique sans PageRequest borné
backend leak aucun type driver/row/table/SQL dans la façade
event persistence creep event-only != capability Store ; owner Interface préféré si non persistant
stale processed = true future preuve stage+processor/version+input hash, pas un bool comme source de vérité
purge prématurée policy externe + preuves versionnées + transition atomique
re-backfill après purge tombstone minimal durable ; réhydratation uniquement en mode forcé
purge casse nouveau structural archive/réseau forcé nécessaire si nouvelle version N2 exige le RAW
closed-world backend contrat implémentable hors ksp-store-lib
closed-world RAW family modèles/capabilities séparés, ajoutables sans JSON fourre-tout
scope creep N2/N3/N4 aucun type STRUCTURAL/DECODED/DOMAIN implémenté dans 0.3.1

Les menaces DSN/SQL injection/schema drift/migrations restent documentées pour 0.3.2, où elles deviennent exécutables. Elles ne justifient pas des types SQL dans l'API.

20. Stratégie de tests 0.3.1

Prévoir :

unit tests module-local
public API canaries crate-root
external backend canary
object-safe backend contract canary
manifest/dependency firewall
exact public export inventory
exact production module inventory
bounds payload/provenance/cursor
Debug/redaction tests
idempotence outcome semantics
transaction identity validation
source-to-model admission matrix canaries
transaction logs remain owned by RawTransaction before STRUCTURAL extraction
account/status model compatibility canaries lorsqu'introduits
event-only model does not imply Store capability
atomic-operation contract fake backend
pagination bounds
retention state/tombstone transition invariants
normal backfill skips purged tombstone; forced rehydrate remains explicit
processing proof is not represented by a lone boolean
release completeness
cargo tree -p ksp-store-api --edges normal
cargo tree --duplicates

Le backend externe canary doit implémenter le contrat avec une petite implémentation mémoire de test située hors des modules de production. Il prouve :

aucune dépendance à ksp-store-lib
aucune connaissance PostgreSQL
implémentation des mêmes capabilities API
round-trip via le contrat backend commun

Aucun PostgreSQL live test n'appartient à 0.3.1 puisque le backend PostgreSQL n'existe pas encore.

21. Ownership

Concept Owner candidat/fixé Visibilité Introduction Raison
RawTransaction ksp-store-api public 0.3.1 modèle persistant/replayable commun
RawTransactionObservation ksp-store-api public 0.3.1 acquisition durable distincte du RAW
transaction log messages RAW dans RawTransaction public via modèle RAW 0.3.1 font partie de la transaction avant décomposition
structural transaction log lines future couche N2 hors 0.3.1 série STRUCTURAL unités extraites après décomposition
RawAccountState ksp-store-api public si audit PASS prévu 0.3.1, capability progressive état canonique commun HTTP/WS/gRPC à confirmer
RawAccountObservation ksp-store-api public si audit PASS prévu 0.3.1, capability progressive provenance/account acquisition
TransactionStatusObservation à confirmer ksp-store-api public si persistence utile exploration 0.3.1 petit état durable/event potentiel
log/status/slot/vote event-only préférer ksp-interface-lib public passif future tranche Interface si retenue échange inter-composants sans persistence Store
RawRetentionState / tombstone ksp-store-api public logique à stabiliser en 0.3.1 cycle de vie RAW backend-agnostic
processing ledger versionné future contrat Store commun hors surface initiale RAW avec jobs/N2/N3 preuve durable stage/version/input, pas bool
RAW references ksp-store-api public 0.3.1 reads/replay/lineage
write outcomes ksp-store-api public 0.3.1 sémantique idempotente commune
query/page ksp-store-api public 0.3.1 backlog/replay backend-agnostic
health portable ksp-store-api public 0.3.1 consumer commun
backend capabilities ksp-store-api public 0.3.1 implémentations externes
Store facade ksp-store-lib public 0.3.2 point de consommation commun
backend dispatch/settings ksp-store-lib public/privé selon contrat 0.3.2 features disponibles + sélection Config
PostgreSQL rows/pool/SQL/migrations ksp-store-postgres-lib privé/opérateur 0.3.2 détails physiques
transport -> RAW conversion composition/pipeline futur privé/réutilisable release acquisition conversion explicite, jamais dépendance inverse
RAW -> STRUCTURAL future pipeline générique hors 0.3.1 série suivante aucune dépendance Program
event runtime / scheduler / analyzer worker/analyser/runtime futur hors Store ultérieur Store ne notifie pas lui-même
retention policy/maintenance worker job/worker/opérateur futur hors Store API décisionnel ultérieur décide l'éligibilité ; Store applique la transition

22. Hors scope strict 0.3.1

ksp-store-lib
PostgreSQL runtime
tokio-postgres dependency
pool/TLS PostgreSQL
SQL/tables/indexes/migrations
std.store Config
backend MySQL/SQLite/Oracle/RocksDB/ClickHouse
Transport acquisition runtime
conversion Transport -> RAW fonctionnelle
N2 STRUCTURAL structs et persistence
RAW -> STRUCTURAL processor
N3 DECODED
N4 DOMAIN/materialization
processing ledger concret
worker/job/backfill
application Store/RAW
event bus/scheduler/analyser runtime
compression algorithm concret
archive backend concret
retention cron/worker automatique
truncate/drop/maintenance API générique

Les contrats logiques de rétention/tombstone N1 et la compatibilité avec un futur processing ledger peuvent en revanche être définis dans ksp-store-api sans implémenter ces mécanismes.

23. Prévision souple recalibrée 0.3.1

Chaque tranche vise environ 1520 minutes de travail effectif.

pre.001 — Audit, taxonomie N1 et split API/backend

Base, règles, architecture, audit kbot2/kbot3, audit PostgreSQL/driver futur, frontière Interface/Store, classification persistence/event/Transport-only, rétention/tombstones, ownership, threat model, tests et sizing.

pre.002 — Scaffold ksp-store-api + taxonomie

Créer uniquement la crate API, manifest Core-only, façade crate-root et modules de modèle/capability sans dépendance backend. Verrouiller le vocabulaire RAW/STRUCTURAL et la séparation model/capability.

pre.003 — Primitives RAW communes + transaction

Introduire payload/reference/provenance/idempotence/timestamps bornés puis RawTransaction et son observation. Les logs inclus restent dans la transaction.

pre.004 — Matrice cross-source + familles N1 prévues

Auditer HTTP/WS/gRPC pour RawAccountState/observation et TransactionStatusObservation; introduire les modèles communs seulement lorsque la sémantique converge. Classer explicitement logsSubscribe/slot/vote/block/Entry en event, IDEA ou rejet.

pre.005 — Capabilities backend extensibles

Implémenter les contracts read/write object-safe et l'external backend canary, sans façade runtime Store et sans runtime DB. Un modèle prévu n'oblige pas chaque backend à implémenter sa capability.

pre.006 — Queries, outcomes et lifecycle RAW

Finaliser reads/list/backlog bornés, atomic acquisition contract, RawRetentionState, tombstone minimal et sémantiques normal-skip/force-rehydrate sans implémenter compression/archive physique.

pre.007 — Boundary/adversarial hardening + completeness

Payload/cursor/provenance hostile, Interface/Store ownership canaries, admission matrix, retention races, exact exports/modules, external backend, dependency firewall et scope négatif N2/N3/N4.

pre.008 — Gate technique final

Workspace complet, tests Store API, graphes Cargo. Aucun développement fonctionnel nouveau.

pre.009 — Réconciliation documentaire finale

README/USAGE, plan, validation et références durables réellement concernées. Aucun CHANGELOG.md, ROADMAP.md ni prompt suivant.

pre.010 — Préparation de publication minimale

Uniquement :

Cargo.toml
CHANGELOG.md
ROADMAP.md
prompts/021-V0_3_2_START_PROMPT.md
delta pre.010

Le prompt 0.3.2 ouvre ensemble ksp-store-lib et ksp-store-postgres-lib, avec feature postgres par défaut et tokio-postgres strictement dans la crate backend.

rel.001 — Publication stable

Mécanique de publication uniquement.

La release reste estimée clôturable dans une session si les tranches conservent cette granularité et si aucun processor STRUCTURAL/decoder n'est tiré dans le scope.

24. Trajectoire 0.3.x résultante

La prévision durable devient, sous réserve des gates de chaque release :

0.3.1  ksp-store-api / N1 RAW + observations + lifecycle logique
0.3.2  ksp-store-lib + ksp-store-postgres-lib / PostgreSQL reference via tokio-postgres
0.3.3  ksp-interface-lib additions nécessaires aux événements/acquisitions partagés
0.3.4  ksp-job-api + premier backfill RAW concret
0.3.5  application backfill/inspection RAW
ensuite worker/service live RAW avant ouverture N2 STRUCTURAL

Les événements realtime non persistés et les modèles Interface peuvent être avancés ou retardés selon le premier consumer réel ; ils ne doivent pas être artificiellement absorbés par Store.

La renumérotation 0.3.1..0.3.5 issue de pre.001-fix.001 reste valide.

25. Critères de fermeture de 0.3.1

La stable 0.3.1 est prête seulement si :

ksp-store-api existe seule dans le domaine Store
RawTransaction + observations stables
aucune source partielle ne fabrique un RawTransaction incomplet
logs transactionnels restent dans RawTransaction jusqu'à N2 STRUCTURAL
RawAccountState/observation audités et modèle prévu si compatibilité cross-source prouvée
TransactionStatusObservation classé explicitement
logsSubscribe/slot/vote classés event-only/Interface, reportés ou rejetés explicitement
RawBlock reste IDEA sans persistence tant qu'un besoin block-level n'est pas démontré
Yellowstone Entry reste explicitement hors taxonomie active
frontière ksp-interface-lib / ksp-store-api documentée et sans modèles concurrents
models et capabilities séparés
contrats backend communs stables
backend externe implémentable sans ksp-store-lib
aucun type backend/SQL public
queries/pagination bornées
idempotence/conflict semantics explicites
atomic acquisition contract explicite
références durables stables
RawRetentionState/tombstone empêchent le rebackfill normal après purge
force rehydrate reste explicitement distinct
aucun simple processed bool n'est présenté comme preuve suffisante de traitement
future preuve processing versionnée stage/processor/input reste possible
aucune dépendance PostgreSQL
aucune surface N2 STRUCTURAL/N3 DECODED/N4 DOMAIN implémentée
workspace et graphes entièrement verts
prompt 0.3.2 cohérent avec ksp-store-lib + ksp-store-postgres-lib + tokio-postgres