# 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 : ```text 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 : ```text 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 : ```text 0.3.1 = ksp-store-api uniquement 0.3.2 = ksp-store-lib, backend PostgreSQL officiel 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 et encore visible dans le `ROADMAP.md` stable `v0.2.14`. Le `ROADMAP.md` n'est pas modifié dans `pre.001` conformément aux lanes documentaires KSP. Il devra être réconcilié dans la lane de préparation de publication de `0.3.1`, avec le prompt de démarrage `0.3.2`. Conséquences immédiates : ```text 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 / D1 RAW**. La crate doit posséder : ```text modèles objet/struct persistants communs références durables provenance d'acquisition outcomes d'écriture/idempotence queries et pagination bornées health/readiness communs utiles contrats backend extensibles façade Store commune consommée par workers/jobs/apps format canonique de notification de disponibilité si la référence RAW est stabilisée ``` Elle ne possède pas : ```text 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 Program decoder Materializer ``` 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 : ```text ksp-store-api | modèles + opérations | +-----------+-----------+ | | ksp-store-lib ksp-store-mysql-lib PostgreSQL officiel exemple futur tokio-postgres impl indépendante ``` `ksp-store-mysql-lib` est uniquement un exemple de future implémentation. `0.3.1` et `0.3.2` n'implémentent aucun autre backend que PostgreSQL. Une future implémentation alternative doit dépendre de `ksp-store-api`, pas de `ksp-store-lib`. ### 4.2 Sélection par Config et composition `ksp-store-api` ne lit pas Config. `ksp-store-lib` ne lit pas Config non plus. La composition future suit : ```text ksp-config-lib -> settings backend typés composition/application -> choisit l'implémentation disponible -> construit ksp_store_api::Store consumer -> appelle toujours la même façade Store ``` Lorsque plusieurs implémentations seront effectivement liées à un même host, changer de backend doit pouvoir se limiter au choix Config/composition. Un consumer logique ne branche pas sur `postgres`, `mysql`, `rocksdb` ou un autre moteur. La Config ne peut évidemment sélectionner qu'une implémentation réellement liée au binaire concerné ; `0.3.2` n'en fournira qu'une, PostgreSQL. ### 4.3 Modèle public et modèle interne Deux modèles restent strictement distincts : ```text ksp-store-api RawTransaction / RawLog / provenance / queries / outcomes = public et commun backend PostgresRawTransactionRow / SQL columns / statements / indexes = privés à l'implémentation ``` Aucun type de row backend, handle de pool, statement préparé ou nom d'objet SQL ne traverse la façade publique. ## 5. Sources KSP relues Le gate `pre.001` a relu les sources prescrites par le prompt : ```text 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 : ```text 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 : ```text 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 / D1 RAW : définition retenue ### 6.1 Rôle N1 est la couche **acquisition persistée / RAW replayable**. Elle ne se limite pas à une table `raw_transactions`. Elle regroupe les faits d'acquisition nécessaires pour rejouer ultérieurement la normalisation générique Solana sans redemander arbitrairement la donnée au provider. La première surface concrète `0.3.1` retient : ```text RawTransaction RawTransactionObservation RawLog RawLogObservation provenance commune références durables communes ``` Les familles suivantes sont explicitement prévues mais reportées tant que leur acquisition/replay exact n'est pas suffisamment auditée : ```text RawAccount / account observation RawBlock RawSlot / slot update si un besoin durable distinct existe autres familles d'acquisition réellement nécessaires ``` Aucun enum fermé ne doit empêcher leur ajout. ### 6.2 Transport n'est pas une famille RAW Les mécanismes suivants sont des **origines d'acquisition**, pas des modèles persistants distincts : ```text HTTP JSON-RPC WebSocket JSON-RPC Yellowstone gRPC provider extension import backfill ``` Interdit : ```text RawHttpTransaction RawWsTransaction RawGrpcTransaction ``` La même transaction logique acquise par HTTP, WS et gRPC doit converger vers un `RawTransaction` commun, avec plusieurs observations/provenances si nécessaire. ### 6.3 Transaction canonique et observation Le contrat sépare : ```text RawTransaction = fait RAW canonique source-independent et replayable RawTransactionObservation = fait qu'une source donnée a observé/acquis cette transaction ``` Exemple : ```text RawTransaction T ^ | +---------------+---------------+ | | | HTTP/Helius obs WS/Helius obs gRPC/PublicNode obs ``` La duplication d'observation ne duplique pas automatiquement la transaction canonique. ### 6.4 Raw logs `RawLog` est une famille N1 distincte et n'est pas confondue avec la future décomposition CORE des logs. Elle doit préserver l'observation de logs suffisamment fidèlement pour : ```text conserver l'ordre des lignes conserver slot/signature/identité applicable conserver le statut source nécessaire permettre replay/inspection permettre de relier un log à une transaction lorsqu'elle est connue ``` Une observation `logsSubscribe` peut également servir de déclencheur pour acquérir ensuite la transaction complète. Le modèle de provenance ne doit pas empêcher une lineage future : ```text RawLogObservation -> déclenche acquisition transaction RawTransactionObservation -> RawTransaction ``` Cette relation causale n'est pas un orchestrateur et n'est pas obligatoirement implémentée dans `0.3.1`. ### 6.5 Autres RAW Le design doit permettre d'ajouter plus tard des familles distinctes sans créer un `RawAnything { kind, json }` universel. Le partage se fait par composition de primitives communes : ```text RawAcquisitionProvenance RawPayload RawContentHash / idempotence key RawDataReference PageRequest / Page ``` et non par effacement des sémantiques propres à chaque famille. ## 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é : ```text 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 : ```text 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 : ```text 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 générique Solana La frontière architecturale actuelle reste nommée : ```text RAW -> CORE -> DECODE -> SPECIALIZED ``` Le nom `CORE` n'est pas considéré comme irréversible. Une revue de nomenclature pourra être faite avant l'ouverture effective de D2 sans modifier les responsabilités déjà fixées. Le rôle de D2 reste en revanche clair : décomposer/normaliser une transaction RAW en faits Solana génériques indépendants des decoders Program. Le futur flux doit permettre notamment : ```text RawTransaction -> transaction/message générique -> account keys -> instruction top-level #0 -> instruction top-level #1 -> CPI/inner instruction #1.0 -> CPI/inner instruction #1.1 -> logs/meta/balances/return data applicables ``` Chaque instruction/CPI doit ensuite pouvoir être traitée indépendamment : ```text instruction générique -> decoder disponible ? oui -> decode non -> Unsupported/NotApplicable durable sans bloquer le reste ``` `0.3.1` ne crée aucun type D2/CORE et ne dépend pas de `ksp-program-api`. ## 9. Provenance commune La provenance candidate doit pouvoir représenter sans secret : ```text 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 : ```text 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 : ```text 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 Logs L'identité exacte d'un `RawLog` sera stabilisée avec son modèle concret ; elle doit être déterministe et ne doit pas dépendre d'un primary key backend. Aucune promesse exactly-once distribuée n'est faite. ## 11. API candidate ### 11.1 Façade commune Le consumer normal doit recevoir une façade commune : ```text Store ``` et appeler des opérations backend-agnostic. Le backend concret implémente un contrat public d'extension, mais son modèle interne reste privé. ### 11.2 Object-safety et async La sélection de backend par Config/composition est un besoin runtime réel. L'API doit donc permettre de construire une même façade `Store` à partir d'une implémentation externe différente. La stratégie candidate est : ```text StoreBackend: Send + Sync méthodes object-safe futures boxed KSP-owned via un alias StoreFuture<'a, T> Store possède Arc ``` 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 Éviter un trait de 150 méthodes. La candidate est une composition de capabilities N1 : ```text StoreHealth RawTransactionStore RawLogStore ``` Un contrat composite `StoreBackend` peut agréger les capabilities exigées par la façade `Store` de cette release. Les futures couches D2/D3/D4 ne sont pas ajoutées par anticipation. ### 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 : ```text persist_transaction_acquisition(transaction, observation) record_transaction_observation(observation) get_raw_transaction(reference) list_raw_transactions(query, page) list_transaction_observations(query, page) persist_log_acquisition(raw_log, observation) record_log_observation(observation) get_raw_log(reference) list_raw_logs(query, page) list_log_observations(query, page) health() ``` `persist_*_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 : ```text 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 : ```text 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 durables et notifications L'ownership `KSP-NOTIFY-001..006` reste à `ksp-store-api`. Le candidat est une référence compacte : ```text RawDataReference Transaction(RawTransactionReference) Log(RawLogReference) futures variantes non exhaustives ``` Puis un signal logique : ```text RawDataAvailable { reference, } ``` Le mécanisme de diffusion est hors scope. Ordre obligatoire : ```text persist commit notify ``` La notification ne transporte pas le payload RAW et ne remplace jamais `list_*`/backlog Store. Si les références concrètes ne sont pas assez stabilisées lors de leur tranche, le type de notification peut être reporté sans déplacer son ownership. ## 13. Health et diagnostics La façade peut exposer un health commun minimal : ```text 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 : ```text 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-lib` et sont projetés vers le contrat commun seulement lorsque cela a un sens portable. ## 14. Dependency graph `0.3.1` Graphe maximal par défaut : ```text ksp-store-api └── ksp-core-lib ``` Aucune dépendance externe n'est nécessaire pour la foundation candidate : ```text 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 : ```text 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 ``` 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`. ### 15.2 Matrice d'héritage | Concept historique | Observation kbot3 | Décision | Application KSP | |-----------------------------------------|-------------------------------------------------------------------|------------|---------------------------------------------------------------------------------| | séparation façade Store / PostgreSQL | PostgreSQL et `sqlx::PgPool` privés | REPRENDRE | API commune séparée; backend PostgreSQL reporté à `0.3.2` | | `StoreOpenOptions` / backend selection | backend + `serde_json::Value` opaque dans Store | REDESSINER | Config/composition sélectionne une implémentation typée puis construit `Store` | | health/readiness | contrat backend-neutral présent | REPRENDRE | health minimal portable dans `ksp-store-api` | | `contracts/dto/raw.rs` | transaction canonique + observations transaction/account | REDESSINER | transaction + logs + provenance commune, sans JSON universel ni lifecycle D2/D3 | | `contracts/entity/raw.rs` | rows exposant `i64`, JSON et timestamps backend-shaped | REDESSINER | entités logiques sans PK SQL ni type physique | | `RawTransactionStore` | `has_*` puis inserts séparés | REDESSINER | write idempotent atomique sans check-then-insert | | repository traits | capabilities async séparées | REPRENDRE | capabilities N1 séparées + façade commune | | pagination | 100 par défaut, 500 max, cursor opaque | REPRENDRE | mêmes bornes candidates, cursor toujours opaque | | replay contracts | filtres mêlés au processing N2/N3 | REPORTER | `0.3.1` fournit seulement lectures RAW nécessaires au futur replay | | error contracts | erreurs Store historiques | REDESSINER | `ksp_core_lib::Error/Result` + codes Store stables | | schema validation / migration bootstrap | validation stricte des objets PostgreSQL | REPORTER | responsabilité `ksp-store-lib` en `0.3.2` | | migrations atomiques | ressources SQL actives nombreuses | REPORTER | aucune migration dans Store API | | constraints/indexes | 79 index attendus | REPORTER | design physique seulement après stabilisation API | | idempotence / uniqueness | observation keys + canonical identity | REPRENDRE | sémantique publique déterministe; mécanisme backend privé | | raw transaction table | table physique `k_sol_raw_transactions` | REPORTER | `RawTransaction` commun maintenant; table seulement en `0.3.2` | | acquisition observations | transaction + account observations | REPRENDRE | concept N1 central; transaction + log initialement | | processing ledger | couvre N2/N3 processing | REPORTER | appartient aux futures couches de processing, pas N1 foundation | | CORE tables | transaction, keys, instructions, CPI, logs, balances, return data | REPORTER | future couche D2, aucune surface en `0.3.1` | | DECODE coverage/events | contrats decoder/materializer | REPORTER | future D3 | | materialization journal | `k_sol_mat_outputs` | REPORTER | future D3 | | maintenance truncate/drop | scripts destructifs dédiés | REJETER | aucune API runtime générique destructive Store | | Config Store | backend + options JSON historiques | REDESSINER | Config reste owner; settings PostgreSQL typés en `0.3.2` | | runtime diagnostics | résumés sanitised mais backend riches | REDESSINER | health portable dans API; détails backend privés | ## 16. Audit PostgreSQL et driver de référence futur ### 16.1 Décision Le choix opérateur est fixé pour la version suivante : ```text ksp-store-lib backend officiel = 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 : ```text 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 et remplir explicitement user/host/port/database/TLS/options depuis les settings fournis par Config/composition. ### 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` devra implémenter **le même modèle objet et les mêmes opérations** de `ksp-store-api`. Il pourra créer librement : ```text PostgresStoreBackend Postgres*Row privés migrations schema version prepared statements indexes pool transaction handles privés mapping Rust API <-> PostgreSQL ``` mais aucun de ces éléments ne devient une dépendance des consumers. La compatibilité backend devra être testée par round-trip des modèles API et par introspection/validation du schema PostgreSQL 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-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 : ```text 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. ## 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` | implémentations Debug manuelles/redacted pour conteneurs sensibles | | secret provider/endpoint | provenance ne contient que des ids/codes sûrs, jamais URL/credential | | 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 | | backend error leak | mapping vers erreurs KSP sûres; source externe auditée avant chaînage | | closed-world backend | contrat implémentable hors `ksp-store-lib` | | closed-world RAW family | types/familles extensibles sans JSON fourre-tout | | cross-backend mismatch | conformance suite commune sur chaque implémentation | | scope creep D2/D3/D4 | aucun type CORE/DECODE/SPECIALIZED 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 : ```text unit tests module-local public API canaries crate-root external backend canary object-safe facade/backend composition canary manifest/dependency firewall exact public export inventory exact production module inventory bounds payload/provenance/cursor Debug/redaction tests idempotence outcome semantics transaction/log identity validation atomic-operation contract fake backend pagination bounds notification reference contract si introduit 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 : ```text aucune dépendance à ksp-store-lib aucune connaissance PostgreSQL construction de Store depuis l'implémentation mêmes appels consumer ``` Aucun PostgreSQL live test n'appartient à `0.3.1` puisque le backend PostgreSQL n'existe pas encore. ## 21. Ownership | Concept | Owner | Visibilité | Introduction | Raison | |-----------------------------|----------------------------|---------------------------------|---------------------|-------------------------------------------| | `RawTransaction` | `ksp-store-api` | public | `0.3.1` | modèle persistant commun | | `RawTransactionObservation` | `ksp-store-api` | public | `0.3.1` | provenance/acquisition distincte du RAW | | `RawLog` | `ksp-store-api` | public | `0.3.1` | seconde famille N1 explicitement demandée | | `RawLogObservation` | `ksp-store-api` | public | `0.3.1` | provenance logs | | `RawPayload` | `ksp-store-api` | public | `0.3.1` | contrat persistant replayable versionné | | RAW references | `ksp-store-api` | public | `0.3.1` | reads, notification, 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 extension trait | `ksp-store-api` | public | `0.3.1` | implémentation externe réelle | | `Store` facade | `ksp-store-api` | public | `0.3.1` | appels communs indépendants du backend | | PostgreSQL settings | `ksp-store-lib` | public ou adapter-boundary | `0.3.2` | construction du backend de référence | | PostgreSQL rows | `ksp-store-lib` | privé | `0.3.2` | mapping physique | | pool/connection | `ksp-store-lib` | privé | `0.3.2` | détail runtime | | SQL/statements | `ksp-store-lib` | privé | `0.3.2` | détail backend | | migrations/schema | `ksp-store-lib` | privé/opérateur | `0.3.2` | détail backend | | transport -> RAW conversion | composition/pipeline futur | privé/réutilisable selon besoin | release acquisition | `DEP-KSP-003` | | RAW -> D2 normalization | future pipeline générique | hors `0.3.1` | série suivante | aucune dépendance Program | | notification mechanism | futur host/runtime | hors API de diffusion | ultérieur | Store API possède seulement le format | ## 22. Hors scope strict `0.3.1` ```text 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 D2/CORE structs et persistence RAW -> D2 normalizer Program decode Materializer processing ledger worker/job/backfill application Store/RAW notification transport concret retention/archive/purge lifecycle truncate/drop/maintenance API ``` ## 23. Prévision souple recalibrée `0.3.1` Chaque tranche vise environ 15–20 minutes de travail effectif. ### `pre.001` — Audit, modèle N1 et split API/backend Base, règles, architecture, audit kbot3, audit PostgreSQL/driver futur, RAW transactions/logs, ownership, API candidate, threat model, tests et sizing. ### `pre.002` — Scaffold `ksp-store-api` Créer uniquement la crate API, manifest Core-only, façade crate-root et documentation initiale. Aucun modèle fonctionnel lourd. ### `pre.003` — Primitives RAW communes + transaction Introduire payload/reference/provenance/idempotence/timestamps bornés puis `RawTransaction` et son observation. ### `pre.004` — RAW logs + extensibilité N1 Introduire `RawLog`/observation, vérifier ordering/identity/bounds et verrouiller l'absence de type RAW fourre-tout. ### `pre.005` — Capabilities backend + façade `Store` Implémenter les contracts object-safe, la façade commune et l'external backend canary, sans runtime DB. ### `pre.006` — Queries, pagination, outcomes et notification reference Finaliser reads/list/backlog bornés, atomic acquisition contract et référence de notification si suffisamment stable. ### `pre.007` — Adversarial/API hardening + completeness Payload/cursor/provenance hostile, Debug, external backend, exact exports/modules, dependency firewall et scope négatif D2/D3/D4. ### `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 : ```text Cargo.toml CHANGELOG.md ROADMAP.md prompts/021-V0_3_2_START_PROMPT.md delta pre.010 ``` Le prompt `0.3.2` ouvre `ksp-store-lib` PostgreSQL/`tokio-postgres`. ### `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 codec/normalizer D2 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 : ```text 0.3.1 ksp-store-api / N1 RAW contract 0.3.2 ksp-store-lib / PostgreSQL reference via tokio-postgres 0.3.3 generic Interface/wire additions nécessaires à acquisition/normalisation 0.3.4 ksp-job-api + premier backfill RAW concret 0.3.5 application backfill/inspection RAW ensuite worker/service live RAW avant ouverture D2 ``` Cette renumérotation remplace la prévision `0.3.1..0.3.4` de la base `v0.2.14`; elle sera écrite dans le `ROADMAP.md` au moment prévu par le workflow documentaire. ## 25. Critères de fermeture de `0.3.1` La stable `0.3.1` est prête seulement si : ```text ksp-store-api existe seule dans le domaine Store modèle objet RAW commun stable RawTransaction + observations stables RawLog + observations stables future extension N1 non bloquée façade Store commune stable 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 notification format introduit ou reporté explicitement aucune dépendance PostgreSQL aucune surface D2/D3/D4 workspace et graphes entièrement verts prompt `0.3.2` cohérent avec PostgreSQL/tokio-postgres ```