# Plan v0.3.8 — Store Desk V1 RAW ## 1. But de la version Créer `ksp-app-store-desk`, Desk Tauri KSP read-only destinée à l'inspection backend-agnostique du Store et à la navigation des données RAW réellement persistées. La V1 doit exposer les contrats logiques KSP et jamais le schéma PostgreSQL physique. L'application consomme exclusivement `ksp-store-lib` pour le Store, `ksp-config-lib` pour Config et `ksp-logging-lib` pour le tracing/runtime Logging. ## 2. Base autoritaire et gate d'ouverture Base vérifiée : ```text archive : khadhroony-solana-project-v0.3.7.zip workspace.package.version : 0.3.7 delta stable : deltas/0.3.7/rel.001.md prompt : prompts/027-V0_3_8_START_PROMPT.md ``` L'archive kbot3 est extraite dans un arbre distinct et ne sert que de référence fonctionnelle/UX. ### 2.1 Preuve des archives | Source | SHA-256 | Entrées | Contrôle ZIP | Rôle | |--------------------------------------------------------|--------------------------------------------------------------------|--------:|--------------------------------------------------|----------------------------| | `khadhroony-solana-project-v0.3.7.zip` | `d57132960e4c280a67e59bea02ce08a61665890bdfb6ef16b7faf1b3c57070ae` | 1 702 | propre, aucun traversal/absolu/backslash/symlink | source exclusive de code | | `khadhroony-bot3_v0.5.3-pre.005-fix010-thelatest1.zip` | `ee47643b9f8b582ee8db97b2381ec107e45aef8c009fee44757531514615d318` | 2 501 | propre, aucun traversal/absolu/backslash/symlink | référence fonctionnelle/UX | ### 2.2 Baseline locale Exécuté avant modification : ```text python3 scripts/audit_rust_workspace_rules.py General Rust rule audit: clean Rust export completeness audit: 0 candidate(s) KSP workspace Rust rule audit: clean python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas Markdown table audit: clean (301 table(s), 687 file(s)) ``` `cargo` n'est pas installé dans l'environnement d'assemblage. `cargo fmt`, `cargo check`, Clippy et les tests ciblés ne sont donc pas déclarés PASS localement. Le journal opérateur fourni pour la base stable v0.3.7 rapporte un gate complet incluant ces commandes ; il reste une preuve externe de baseline. ## 3. Règles et frontières confirmées ### 3.1 Ownership ```text Config/env/secrets -> ksp-config-lib Store contracts persistés -> ksp-store-api Store facade consumer -> ksp-store-lib PostgreSQL physique -> ksp-store-postgres-lib Logging/tracing runtime -> ksp-logging-lib Store Desk -> composition + DTO + commands + UX ``` ### 3.2 Dépendances applicatives prévues | Dépendance | Statut Store Desk | Justification | |--------------------------|-------------------|----------------------------------------------------| | `ksp-config-lib` | REQUISE | composite, profils, bootstrap Config | | `ksp-logging-lib` | REQUISE | runtime Logging et tracing KSP | | `ksp-store-lib` | REQUISE | unique façade Store autorisée | | `ksp-core-lib` | NON REQUISE V1 | réexports Store suffisants pour les contrats visés | | `ksp-store-api` | INTERDITE DIRECTE | la Desk ne contourne pas la façade | | `ksp-store-postgres-lib` | INTERDITE | backend physique privé | | `tokio-postgres` | INTERDITE | driver physique | | `deadpool-postgres` | INTERDITE | pool physique | | Transport RPC/gRPC/WS | INTERDIT V1 | inspection Store uniquement | ## 4. Gabarit KSP Store Desk **GABARIT STORE DESK = KSP.** kbot3 n'est jamais une source de package, HTML, SASS, assets, Rust, TypeScript ou versions npm. ### 4.1 Matrice Desk | Élément | Config Desk | Wallet Desk | SOL Prices Desk | Backfill Desk | Choix Store Desk | |-----------------------------|-------------|-------------|-----------------|---------------|-----------------------------------| | package Rust lib + bin | oui | oui | oui | oui | identique | | launcher `main.rs` mince | oui | oui | oui | oui | identique | | Tauri lib `run()` | oui | oui | oui | oui | identique | | `tauri.rs` centralisé | oui | oui | oui | oui | identique | | splash KSP commun | oui | oui | oui | oui | identique, titre Store uniquement | | shell header/sidebar/footer | oui | oui | oui | oui | identique | | fonts/assets KSP | oui | oui | oui | oui | identiques | | tracing frontend | oui | oui | oui | oui | obligatoire | | Vite strict | 1430/1431 | 1432/1433 | 1434/1435 | 1436/1437 | **1438/1439** | | DataTables | oui | oui | non | non | oui, inspection `serverSide` RAW | | Select extension | oui | oui | non | non | non par défaut | La sélection DataTables n'est pas justifiée par une action V1 : le détail s'ouvre par une action explicite dans la row. `datatables.net-select-bs5` n'est donc pas requis tant qu'une sélection persistante n'apporte pas une fonction réelle. ## 5. npm / DataTables map Les ranges KSP v0.3.7 observés dans Config/Wallet Desk sont : ```text @fltsci/tauri-plugin-tracing ^0.3 @fortawesome/fontawesome-free ^7.3 @tauri-apps/api ^2.11 bootstrap ^5.3 datatables.net-bs5 ^3.0 datatables.net-select-bs5 ^4.0 resize-observer-polyfill ^1.5 simplebar ^6.3 @tauri-apps/cli ^2.11 @types/bootstrap ^5.2 @types/node ^26.1 sass-embedded ^1.102 typescript ^7.0 vite ^8.2 ``` Audit externe du 2026-09-03 : DataTables stable courant `3.0.3`, Select `4.0.1`. Les ranges KSP `^3.0` / `^4.0` couvrent déjà ces familles ; aucune mise à niveau opportuniste du workspace n'est requise. Choix Store Desk : même socle KSP, `datatables.net-bs5 ^3.0`, sans `datatables.net-select-bs5` tant qu'aucun use case de sélection persistante ne le justifie. DataTables est utilisé en `serverSide: true` avec `ajax` sous forme de fonction afin d'appeler les commands Tauri sans requête HTTP directe. ## 6. Audit DataTables et décision de pagination ### 6.1 Contrat DataTables réellement requis Le mode `serverSide` DataTables échange : ```text request : draw + start + length + search/order metadata response: draw + recordsTotal + recordsFiltered + data ``` `start` est un offset absolu zéro-based et `length` la taille de page. DataTables exige les totaux pour construire son pager et son information de dataset. Son option `ajax` accepte une fonction custom : Store Desk peut donc transformer une demande DataTables en `invoke()` Tauri puis appeler le callback DataTables, sans `fetch` réseau. ### 6.2 Contrat RAW canonique KSP conservé KSP v0.3.7 expose : ```text RawPageRequest { cursor?: RawPageCursor, limit: RawPageLimit } RawPage { items, next_cursor? } ``` Le backend PostgreSQL implémente une pagination keyset déterministe et opaque. Elle reste la primitive canonique pour workers, backfills, replays et parcours séquentiels volumineux. Aucun contrat existant n'est remplacé, renommé ou affaibli. ### 6.3 Nouvelle primitive d'inspection backend-neutral Le brainstorming conclut qu'une UI d'inspection interactive a un besoin distinct et réutilisable : navigation random-access, taille de page, total exact et summaries sans gros payloads. Ajouter une surface Store dédiée, sans nom DataTables : ```text RawInspectionPageRequest offset: u64 limit: RawPageLimit RawInspectionPage items: Vec total_items: u64 filtered_items: u64 RawTransactionInspectionQuery RawAccountStateInspectionQuery RawTransactionSummary RawAccountStateSummary RawTransactionInspectionRead RawAccountStateInspectionRead ``` Sémantique : - `total_items` = nombre total de lignes logiques de la famille dans le network du Store, avant filtres optionnels de la query ; - `filtered_items` = nombre total après filtres Store de la query, avant offset/limit ; - `offset` = position logique dans l'ordre déterministe de la query ; - `RawPage*` cursor/keyset reste inchangé et continue d'être préféré pour les parcours machine ; - aucune structure de Store API ne porte le nom DataTables ou un DTO Tauri. Deux traits d'inspection séparés préservent le caractère fine-grained des capabilities et évitent d'ajouter des méthodes aux traits RAW existants, ce qui casserait inutilement les implémentations externes. ### 6.4 Projection summary obligatoire La nouvelle pagination ne doit pas déclencher un N+1 de `get_*` ni transporter 16 MiB par row. Les pages d'inspection retournent directement des summaries logiques. `RawTransactionSummary` contient au minimum : référence, slot, block time, format id/version, content hash, taille de payload lorsqu'elle est encore connaissable, retention state. Le payload lui-même reste absent. `RawAccountStateSummary` contient au minimum : référence, owner, lamports, executable, rent epoch et data length. Les bytes `data` restent absents. Ces projections sont backend-neutral et utiles hors Tauri : CLI d'inspection, diagnostics admin ou API read-only peuvent les consommer. ### 6.5 Mapping DataTables unique Dans Store Desk : ```text DataTables start -> RawInspectionPageRequest.offset DataTables length -> RawInspectionPageRequest.limit Store total_items -> recordsTotal Store filtered_items -> recordsFiltered Store summary items -> data DataTables draw -> callback draw, jamais une donnée Store ``` **Une seule pagination est visible : le pager DataTables.** Aucun contrôle `Premier / Précédent / Suivant` Store supplémentaire n'est rendu. La page length DataTables `25 / 50 / 100` est également l'unique taille de page visible et devient directement la limite d'inspection Store. ### 6.6 Filtres et ordre V1 Pour éviter de prétendre supporter une recherche globale que le Store ne possède pas : - `searching: false` en V1 ; les filtres Store sont des contrôles explicites hors champ de recherche DataTables ; - Transactions : slot range optionnelle + direction canonique ; - Accounts : pubkey exacte optionnelle + slot range + direction canonique ; - les colonnes arbitraires restent non-orderable tant qu'un ordre backend-neutral n'est pas contracté ; - le contrôle de direction Store provoque un redraw DataTables depuis `start = 0`. Le pager DataTables n'invente donc ni filtre ni tri que le backend ne sait pas appliquer globalement. ### 6.7 Coût accepté et garde-fous Le backend PostgreSQL devra implémenter un chemin d'inspection avec `COUNT` exact et navigation offset. Ce chemin peut être plus coûteux à grande profondeur que keyset ; ce coût est accepté **uniquement pour l'inspection interactive** et ne remplace jamais le chemin cursor. Garde-fous : - page size UX strictement 25/50/100 ; - aucun `length = -1` accepté ; - conversion offset/limit PostgreSQL vérifiée avant I/O ; - réutiliser le count filtré comme total lorsque la query ne possède aucun filtre optionnel ; - aucun cache de count dans Store API ; une optimisation/caching éventuelle reste backend/app-owned et mesurée ; - tests de grand offset et d'overflow ; - si la profondeur réelle devient pathologique, l'UX pourra être recalibrée sans supprimer la pagination keyset canonique. ### 6.8 Recalibrage du prompt initial Le prompt 027 partait de l'hypothèse « pagination Store opaque distincte du paging local DataTables ». `pre.001` avait explicitement pour mission de brainstormer cette frontière. La décision retenue conserve la séparation **des contrats** mais change la composition UI : ```text RAW cursor/keyset = primitive canonique durable, non exposée par le pager Desk RAW inspection offset/page = primitive dédiée à l'inspection interactive DataTables pager = unique représentation visuelle de cette primitive d'inspection ``` C'est une exception locale et bornée aux passages du prompt qui imposaient `paging: false` ou une double notion de page. Toutes les frontières Store/Config/backend, l'interdiction SQL dans l'app et l'objectif read-only restent inchangés. ## 7. Audit fonctionnel kbot3 | Fonction historique | Source kbot3 observée | Valeur UX | Équivalent KSP V1 | Classification | |-------------------------------|----------------------------------|-----------------------------|------------------------------------------------|-----------------------------| | health/diag Store | `demo_store_diag.*` | visibilité opérationnelle | `Store::runtime_snapshot` + `health` | REPRENDRE FONCTIONNELLEMENT | | refresh manuel | vues diag/raw | resynchronisation explicite | refresh Overview/query | REPRENDRE FONCTIONNELLEMENT | | tables horizontales | `demo_store_tables.ts` | lisibilité grands tableaux | DataTables KSP + `scrollX` | REPRENDRE FONCTIONNELLEMENT | | filtre local DataTables | `demo_store_tables.ts` | exploration rapide | filtres Store explicites, pas de search locale | REDESSINER | | page length DataTables locale | `demo_store_tables.ts` | densité configurable | page size inspection Store 25/50/100 | REDESSINER | | pagination backend par blocs | `demo_store_block_pagination.ts` | datasets volumineux | pager DataTables -> inspection Store | REDESSINER | | total rows / total blocks | `demo_store_block_pagination.ts` | repère global | totals exacts inspection | REPRENDRE FONCTIONNELLEMENT | | stack de cursors frontend | `demo_store_block_pagination.ts` | previous | inutile pour le chemin inspection | REJETER | | replay candidates | `demo_store_replay_candidates.*` | diagnostics replay | hors Store Desk RAW V1 | REJETER V1 | | JSON diagnostic détail | tables/raw views | inspection détaillée | détail app-owned borné | REPRENDRE FONCTIONNELLEMENT | | SQL/repositories physiques | backend historique | implémentation | interdit dans Desk | REJETER | kbot3 montre précisément le coût UX de deux paginations successives. KSP reprend l'intention fonctionnelle mais fusionne la navigation visible dans DataTables grâce à la nouvelle capability d'inspection. Aucun code, DTO, SQL ou package kbot3 n'est repris. ## 8. Capability map Store v0.3.7 | Surface / méthode | Entrée | Sortie | Pagination | Taille / sécurité | Utilité UI | |----------------------------------------|-------------------------------------|-------------------------------------|------------|--------------------------|----------------| | `Store::runtime_snapshot()` | aucune | `StoreRuntimeSnapshot` | non | safe, sans I/O | Overview | | `Store::health().await` | aucune | `StoreHealthSnapshot` | non | safe, diagnostics bornés | Overview | | `list_raw_transactions(query)` | network/range/direction/page | `RawPage` | cursor | références seulement | table Tx | | `get_raw_transaction(reference)` | référence Tx | `Option` | non | payload jusqu'à 16 MiB | détail Tx | | `get_raw_transaction_observation(key)` | observation key | `Option` | non | provenance sûre | détail | | retention read Tx | référence Tx | state/tombstone selon capability | non | métadonnées uniquement | table/detail | | `list_raw_account_states(query)` | network/pubkey/range/direction/page | `RawPage` | cursor | références seulement | table Account | | `get_raw_account_state(reference)` | référence Account | `Option` | non | data jusqu'à 16 MiB | détail Account | | `get_raw_account_observation(key)` | observation key | `Option` | non | provenance sûre | détail | ## 9. Gap map ### 9.1 Pagination/summaries d'inspection Gap confirmé : v0.3.7 ne possède ni offset random-access, ni count backend-neutral, ni summary page évitant N+1. | Besoin UI | Gap v0.3.7 | Contrat candidat | Backend | Coût | |--------------------------------|----------------------|-----------------------------------------------------|---------------------|-------| | pager DataTables unique | pas d'offset/count | `RawInspectionPageRequest` + `RawInspectionPage` | PostgreSQL + facade | moyen | | rows Tx sans payload | références seulement | `RawTransactionSummary` + inspection query/read | PostgreSQL + facade | moyen | | rows Account sans data | références seulement | `RawAccountStateSummary` + inspection query/read | PostgreSQL + facade | moyen | | totaux filtrés exacts | aucun count public | `total_items` + `filtered_items` | PostgreSQL | moyen | | navigation machine performante | déjà présente | conserver `RawPage`/cursor keyset | inchangé | aucun | Décision : cette extension est **requise avant les tables métier Store Desk**. Elle est implémentée dans `ksp-store-api`, `ksp-store-lib` et `ksp-store-postgres-lib`, jamais dans l'application. ### 9.2 Observations Gap confirmé : aucune capability backend-neutral de listing des observations transaction/account. | Besoin UI | Gap | Contrat candidat | Backend | Coût | |-----------------------------|-------------------|----------------------------------------------------------|---------------------|-------| | lister observations Tx | pas de query/list | query d'inspection observation + summary/provenance safe | PostgreSQL + facade | moyen | | lister observations Account | pas de query/list | query d'inspection observation + summary/provenance safe | PostgreSQL + facade | moyen | | filtrer par entité | pas de list | filtre référence dans query | PostgreSQL | moyen | Si des tables d'observations dédiées utilisent DataTables, elles emploient le même modèle d'inspection offset/count afin de conserver une seule pagination visuelle. Le read par `RawObservationKey` existant reste inchangé pour le détail. ### 9.3 Diagnostics physiques Les counts exposés par `RawInspectionPage` portent sur les **entités logiques de la query**, jamais sur des noms de tables, indexes, schemas ou ressources PostgreSQL. Aucun diagnostic physique ne traverse la façade. ## 10. Screen map Navigation V1 : ```text Overview RAW Transactions RAW Accounts ``` Les observations sont intégrées au **détail de l'entité** dans la V1 initiale. Une table d'observations dédiée n'est ajoutée qu'après extension Store ; si elle existe, elle utilise le même pager DataTables unique via la primitive d'inspection. ### 10.1 Overview Cartes : ```text Store target/network logique backend kind logique health state migration state/version/pending pool safe counters last stable error code refresh ``` ### 10.2 RAW Transactions ```text filtres Store : slot min/max, direction, page size contrôles Store : Premier, Précédent, Suivant, Refresh filtres Store explicites : dataset complet de la query table bouton/interaction Détail ``` ### 10.3 RAW Accounts Même pattern avec filtre pubkey optionnel. ### 10.4 Detail Un panneau/modal Bootstrap non bloquant charge une seule entité et ses métadonnées. Les observations sont chargées séparément. Aucun payload n'est mis dans les rows du tableau. ## 11. Table / column map ### 11.1 RawTransaction | Colonne | Source Store | IPC | Filtre Store | Order V1 | Copy | Détail seulement | |-----------------|------------------------------------|----------------|--------------|---------------------|------|------------------| | signature | `RawTransactionSummary::reference` | texte encodé | non V1 | tie-breaker interne | oui | non | | slot | `RawTransactionSummary::slot` | texte décimal | range | asc/desc canonique | non | non | | block time | summary | texte décimal? | non | non | non | non | | format id | summary | texte borné | non | non | non | non | | format version | summary | entier JS-safe | non | non | non | non | | payload size | summary | texte décimal? | non | non | non | non | | content hash | summary | texte hex | non | non | oui | non | | retention state | summary | enum/badge | non V1 | non | non | non | | payload bytes | `get_raw_transaction` | détail borné | non | non | non | **oui** | ### 11.2 RawAccountState | Colonne | Source Store | IPC | Filtre Store | Order V1 | Copy | Détail seulement | |-------------|-------------------------------------|---------------|--------------|---------------------|------|------------------| | pubkey | `RawAccountStateSummary::reference` | texte | exact | tie-breaker interne | oui | non | | slot | summary/reference | texte décimal | range | asc/desc canonique | non | non | | state hash | summary/reference | texte hex | non | tie-breaker interne | oui | non | | owner | summary | texte | non | non | oui | non | | lamports | summary | texte décimal | non | non | non | non | | executable | summary | bool | non | non | non | non | | rent epoch | summary | texte décimal | non | non | non | non | | data length | summary | texte décimal | non | non | non | non | | data bytes | `get_raw_account_state` | détail borné | non | non | non | **oui** | Tous les `u64` Store traversent IPC sous forme de chaînes décimales, sauf valeurs bornées explicitement dans un domaine JS-safe. Les summaries ne transportent jamais payload transaction ou account data. ## 12. Pagination map finale | Action / donnée | Propriétaire | Mapping / règle | |----------------------------|---------------------|----------------------------------------------------------------------| | première page | DataTables | `start = 0`, `length = pageLength` | | page N | DataTables | `start = N * length` -> `RawInspectionPageRequest.offset` | | page précédente/suivante | DataTables | même mapping offset, aucun cursor UI | | page length 25/50/100 | DataTables + app | whitelist Rust -> `RawPageLimit` | | total avant filtres | Store inspection | `total_items` -> `recordsTotal` | | total après filtres | Store inspection | `filtered_items` -> `recordsFiltered` | | rows | Store inspection | summaries -> `data` | | `draw` | DataTables/frontend | recopié comme entier dans le callback, jamais persisté par Store | | slot/pubkey/direction | Store query | contrôles explicites, redraw depuis page 1 | | search DataTables | désactivée V1 | aucune recherche locale ou pseudo-globale | | ordre colonnes arbitraires | désactivé V1 | seule direction canonique Store est supportée | | cursor/keyset RAW | Store canonical | conservé pour workers/backfills/replay, hors pager visuel Store Desk | **Invariant UX : un seul composant de pagination est rendu par table, celui de DataTables.** Aucun pager Store parallèle, aucun « bloc Store » imbriqué et aucune seconde page locale. Le frontend utilise `ajax(data, callback)` de DataTables pour appeler une command Tauri ; aucune URL HTTP n'est configurée. Le callback reçoit `draw`, `recordsTotal`, `recordsFiltered` et `data` depuis la projection app-owned. ## 13. DTO / command map Noms candidats stabilisés pour le plan ; les types Store restent réexportés via `ksp-store-lib`, l'application ne dépend pas directement de `ksp-store-api`. | Command | Request | Response | Capability Store | |----------------------------------------|-----------------------------------------------|----------------------------------|------------------------------------| | `store_runtime_status` | aucune | runtime + health safe DTO | `runtime_snapshot`, `health` | | `store_query_transactions` | offset/limit/range/direction | summaries + total/filtered total | Tx inspection read | | `store_get_transaction_detail` | app-owned row identity | detail DTO | get Tx + retention/tombstone | | `store_query_accounts` | offset/limit/pubkey?/range/direction | summaries + total/filtered total | Account inspection read | | `store_get_account_detail` | app-owned row identity | detail DTO | get Account | | `store_query_transaction_observations` | offset/limit + entity filter, après extension | observation summaries + totals | future observation inspection read | | `store_query_account_observations` | offset/limit + entity filter, après extension | observation summaries + totals | future observation inspection read | Request DTO DataTables : seuls `offset`, `limit` et les filtres Store autorisés traversent IPC. `draw`, configuration de colonnes, search strings génériques et metadata DataTables ne deviennent pas un contrat Store. Response DTO app-owned : ```text rows records_total_decimal records_filtered_decimal ``` Le frontend convertit les counts vers `number` uniquement après `Number.isSafeInteger`. Une valeur hors domaine JS-safe produit une erreur UX explicite plutôt qu'un arrondi silencieux. Le risque est théorique à l'échelle actuelle mais le contrat reste exact. ## 14. Composite Config Créer : ```text config/composite.ksp-app-store-desk.json ``` Composition V1 : ```text Logging + Store ``` Aucun Transport. Le profil sélectionné détermine un seul target/network Store. Changer de target, si exposé ultérieurement, implique fermeture puis réouverture explicite ; la V1 utilise le profil composite au démarrage. ## 15. Payload/detail policy Rows IPC : ```text identités u64 en texte metadata de payload/data hashes états aucun byte RAW complet ``` Detail : ```text une entité à la fois chargement explicite preview bornée si affichage texte/hex nécessaire pas de browser persistence pas de logging du contenu pas de duplication dans DataTables ``` Un export exact de 16 MiB n'est pas requis V1. ## 16. Threat model | Menace / erreur | Mitigation V1 | |---------------------------------------|-------------------------------------------------------------------------| | page size 0/pathologique | whitelist UX 25/50/100 + validation Rust | | `length = -1` DataTables | rejet avant Store | | offset > capacité backend | conversion checked, erreur statique sans I/O | | offset très profond | chemin inspection seulement ; keyset canonique conservé pour machines | | count coûteux | exact seulement pour inspection ; reuse total=filtered sans filtre | | total > JS safe integer | DTO décimal + conversion checked côté TS | | range slot inversée | validation DTO + `RawSlotRange::new` | | pubkey invalide | parsing Rust via type KSP réexporté | | payload/data jusqu'à 16 MiB | summaries sans bytes, detail-only | | N+1 massif | supprimé par summaries d'inspection | | refresh/filtre rapide | DataTables `draw` + génération de filtre ; réponse stale ignorée | | redraw race | un seul pipeline `ajax` DataTables par table | | search globale mensongère | `searching: false` V1 | | ordre colonne non supporté | colonnes non-orderable ; direction Store explicite | | Store close pendant query | état Rust de fermeture, refus nouvelle admission, close borné | | URI/backend leak | DTO app-owned safe, aucune erreur Debug backend | | logging payload/query sensible | instrumentation metadata-only | | localStorage/sessionStorage/IndexedDB | interdits et testés statiquement | | SQL/backend physical leak | dependency/security canaries | | confusion DataTables/RAW cursor | types/traits distincts + tests exacts ; aucun cursor dans le pager Desk | ## 17. Instrumentation frontend Tracer sans secrets/payload/cursor ni contenu de filtres : ```text startup / runtime status refresh DataTables draw start/end offset/page length changés filtre Store changé direction Store changée view/tab changé detail opened/closed count/query failure par code stable ``` `draw`, offset et page length peuvent être journalisés comme metadata numérique sûre. Signature, pubkey, hash, payload, URI, credentials et valeurs de filtre ne sont pas journalisés. ## 18. Gate live Smoke final optionnel, read-only : ```text Config -> Store open -> health -> DataTables first inspection page -> next/random page via offset -> filter Store + redraw -> one transaction detail -> one account detail -> close ``` Le smoke vérifie aussi la cohérence `recordsTotal/recordsFiltered` sur un dataset contrôlé. Aucune migration destructive, archive, purge ou force-rehydrate n'est déclenchée. ## 19. Prévision souple recalibrée — prereleases souples ### pre.001 — audit, brainstorming, architecture et planification Archive/règles/prompt, gabarit KSP/npm, audit fonctionnel kbot3, capability/gap/screen/table/pagination/DTO/threat maps. Décision : primitive d'inspection backend-neutral + **un seul pager DataTables visible**. Aucun scaffold lourd. ### pre.002 — scaffold `ksp-app-store-desk` Package lib/bin, ports 1438/1439, shell/splash/assets/fonts/SASS KSP, tracing frontend, capabilities minimales, `datatables.net-bs5` et table skeleton sans données Store. Aucun Store ouvert. ### pre.003 — contrats Store d'inspection Ajouter dans `ksp-store-api` les types page offset/count, queries Tx/Account, summaries sans gros bytes et deux traits read d'inspection. Réexports `ksp-store-lib`, canaris external backend/public API/security. Aucun SQL encore. #### pre.003-fix.NNN — correctifs éventuels de contrat Lane strictement bornée aux invariants des nouveaux contrats d'inspection. ### pre.004 — inspection PostgreSQL RawTransaction Implémenter summary query, counts exacts, offset/limit checked et trait Tx dans `ksp-store-postgres-lib`, puis dispatch façade Store. Conserver intégralement le listage keyset existant. Tests no-payload/N+1, counts filtres, offset hostile et conformance. ### pre.005 — inspection PostgreSQL RawAccountState Même vertical slice pour Account : pubkey/slot filters, ordre canonique, data length sans data bytes, counts exacts, offset/limit checked, façade et canaris cross-family. ### pre.006 — composite Config + lifecycle Store + Overview Créer `config/composite.ksp-app-store-desk.json`, packaging resources, bootstrap Logging+Store, runtime/health safe et fermeture bornée. ### pre.007 — RawTransaction DataTables serverSide + détail Brancher l'unique pager DataTables via `ajax` function/Tauri invoke, mapping offset/limit/totaux, filtres Store explicites, summary rows, retention badges, détail à la demande et instrumentation. Aucun second pager. ### pre.008 — RawAccountState DataTables serverSide + détail Même intégration pour Account, pubkey/range/direction, summaries JS-safe, détail data borné, responsive/scrollX/copy UX. ### pre.009 — extension Store observations Ajouter les listings/summaries d'observations transaction/account backend-neutral. S'ils sont tabulaires, employer la même primitive d'inspection offset/count ; read-by-key existant reste intact. ### pre.010 — observations UI + hardening UX Intégrer provenance sûre dans les détails/tables retenues, aucune source payload byte, puis hardening des draws, counts, filtres, gros offsets et erreurs. ### pre.011 — security / dependency / release completeness Dependency firewall, Config composition, IPC/security scans, no-browser-storage, exact module/dependency/capability inventories, coexistence cursor + inspection. ### pre.012 — gate technique final `cargo fmt --check`, audits, check/clippy/tests workspace complets selon règles, cargo trees, duplicates, `cargo tauri build`, smoke PostgreSQL read-only opt-in si environnement sûr. ### pre.013 — réconciliation documentaire finale README/USAGE Store Desk, architecture/index, plan/validation. Aucun changement runtime. ### pre.014 — préparation publication Prompt suivant + CHANGELOG + ROADMAP uniquement, avec bump prerelease requis. ### rel.001 — publication stable Publication mécanique `v0.3.8`, aucun rattrapage. Le sizing reste crédible pour une session parce que la modification Store est séparée en contrat (`pre.003`), Tx backend (`pre.004`) et Account backend (`pre.005`) avant toute table métier. La pagination cursor existante n'est jamais réécrite. ## 20. Hors périmètre v0.3.8 ```text remplacement/suppression de RawPage cursor/keyset DataTables-specific DTO dans ksp-store-api search globale arbitraire DataTables sans contrat Store tri arbitraire sur toutes les colonnes sans contrat Store SQL app-owned backend PostgreSQL direct actions archive/purge/force-rehydrate backfill/job control transport RPC ingest live worker lifecycle STRUCTURAL/DECODED/DOMAIN/materialization analytics/trading browser persistence RAW ``` L'utilisation de `OFFSET` et `COUNT` est autorisée uniquement dans l'implémentation privée PostgreSQL de la nouvelle capability d'inspection ; elle reste interdite dans l'application et ne remplace pas les queries keyset existantes.