Files
khadhroony-solana-project/docs/plans/029-V0_3_8_STORE_DESK_PLAN.md
2026-09-03 21:36:34 +02:00

46 KiB

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 :

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 :

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

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 REQUISE SCAFFOLD erreurs applicatives typées et gabarit Desk KSP
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.

4.2 Invariant tracing Store Desk

Pendant le développement de ksp-app-store-desk, le profil Logging demandé explicitement est supertrace; le fallback local reste Trace. Cette sélection est temporairement volontairement verbeuse et sera réévaluée avant rel.001.

Toute interaction opérateur significative du frontend doit être tracée via le bridge KSP, sans contenu sensible :

clic bouton / action explicite
activation tab / vue
changement de filtre ou contrôle
Refresh
ouverture détail
pagination / changement de page length DataTables
redraw/query serverSide et échec IPC associé

Les événements transportent uniquement des identifiants de contrôle, catégories d'action et compteurs sûrs. Les valeurs de filtres, payloads, URI, credentials et contenu RAW ne sont jamais injectés dans les logs. Les handlers métier peuvent ajouter un événement plus précis, mais aucune interaction nouvellement introduite ne doit rester silencieuse.

5. npm / DataTables map

Les ranges KSP v0.3.7 observés dans Config/Wallet Desk sont :

@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 :

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 :

RawPageRequest { cursor?: RawPageCursor, limit: RawPageLimit }
RawPage<T> { 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 :

RawInspectionPageRequest
    offset: u64
    limit: RawPageLimit

RawInspectionPage<T>
    items: Vec<T>
    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 :

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 :

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<RawTransactionReference> cursor références seulement table Tx
get_raw_transaction(reference) référence Tx Option<RawTransaction> non payload jusqu'à 16 MiB détail Tx
get_raw_transaction_observation(key) observation key Option<RawTransactionObservation> 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<RawAccountStateReference> cursor références seulement table Account
get_raw_account_state(reference) référence Account Option<RawAccountState> non data jusqu'à 16 MiB détail Account
get_raw_account_observation(key) observation key Option<RawAccountObservation> 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<T> 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 :

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 :

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

filtres Store : slot min/max, direction
page size/pagination : contrôles DataTables 25/50/100 + pager unique
contrôle applicatif : 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 :

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 :

config/composite.ksp-app-store-desk.json

Composition V1 :

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 :

identités
u64 en texte
metadata de payload/data
hashes
états
aucun byte RAW complet

Detail :

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 :

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 :

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. Le skeleton peut paginer son dataset vide/local pour valider le rendu DataTables, mais ne matérialise aucun second pager Store et ne préjuge pas du branchement serverSide de pre.007/pre.008.

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.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. L'inspection utilise une seule instruction SQL par requête afin que total_items, filtered_items et la page partagent le même snapshot de statement. Le SQL ne sélectionne jamais les payload bytes : il projette uniquement OCTET_LENGTH du payload actif/archivé et les marqueurs de présence nécessaires au contrôle de cohérence. Les tombstones Purged restent inspectables. Tests no-payload/N+1, counts filtres, offset hostile, pages vides profondes et conformance.

pre.005 — inspection PostgreSQL RawAccountState

Même vertical slice pour Account : filtre exact pubkey, range slot inclusif, ordre canonique (slot, pubkey, state_hash), data_length_bytes via OCTET_LENGTH(data) sans sélectionner data, counts exacts et page LIMIT/OFFSET dans une seule instruction SQL par direction. RawAccountStateInspectionRead est implémenté sur PostgreSQL puis dispatché par Store, ce qui porte l'inventaire symétrique à 12 capabilities RAW. Les quatre statements cursor/keyset Account restent strictement OFFSET-free et ne sont jamais réécrits. Le live proof opt-in couvre filtre pubkey, counts, asc/desc, offset et page vide profonde.

pre.006 — composite Config + lifecycle Store + Overview

Créer config/composite.ksp-app-store-desk.json, enregistrer son file_id dans le registre partagé ksp-config-lib, propager la quinzième ressource Config aux cinq bundles Desk utilisant prepare_packaged_runtime(), puis brancher Store Desk sur ksp-store-lib::Store uniquement. Le composite sélectionne Logging supertrace et le profil Store homonyme devnet/mainnet/testnet. L'Overview et Diagnostics exposent uniquement backend logique, network, health, migration et compteurs de pool via un DTO IPC backend-neutral ; aucune URI, credential, SQL, handle physique ou byte RAW. La fermeture de la fenêtre principale déclenche une fermeture Store one-shot et bornée avant sortie Tauri. Les DataTables Transactions/Accounts restent locales et serverSide:false jusqu'à pre.007/pre.008.

pre.006-fix.001 — conformité crate-root des items visibles Store Desk

Le gate opérateur de pre.006 est fonctionnellement vert mais signale un warning unused_imports sur le réexport crate-root de StoreRuntime. Le réexport n'est pas supprimé : conformément à RUST-IMPORT-009, RUST-IMPORT-012 et RUST-API-004, les items pub/pub(crate) partagés restent réexportés via lib.rs et sont consommés par crate::Item, y compris depuis leur module propriétaire. Le correctif normalise les usages Store Desk concernés (CommandErrorDto, FrontendLogPayloadDto, SplashSettings, SplashOrderDto, StoreStartup, StoreRuntime) et rend privés les deux labels de fenêtre qui n'ont aucune consommation crate-wide au lieu de les promouvoir artificiellement. Aucun comportement Config/Store/Tauri/IPC/frontend n'est modifié.

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.

Implémentation retenue : store_query_transactions reçoit uniquement offset, limit, slot_min, slot_max et direction, dérive le network du Store déjà ouvert et appelle RawTransactionInspectionRead. Les counts restent des chaînes décimales jusqu'au contrôle Number.MAX_SAFE_INTEGER côté frontend. Les rows sont payload-free. store_get_transaction_detail reçoit uniquement la signature de la row, recharge l'entité via RawTransactionRead/RawTransactionRetentionRead et expose au maximum 512 bytes sous forme de preview hex, avec taille exacte et indicateur de troncature. Full, Archived et Purged sont distingués ; un Purged utilise son tombstone. Les Accounts restent serverSide:false jusqu'à pre.008.

pre.007-fix.001 — UX uniforme pour identifiants longs

Les signatures et content hashes déjà visibles dans la table Transactions et son modal sont affichés sous une forme tronquée au centre, avec la valeur complète disponible au survol via tooltip natif et un bouton de copie explicite. Le mécanisme reste générique et doit être réutilisé par pre.008 pour les pubkeys, owners, state hashes et autres identifiants longs. Les actions de copie sont tracées uniquement par identifiant logique de champ ; la valeur copiée n'est jamais journalisée. Aucun plugin clipboard Tauri ni capability supplémentaire n'est ajouté : le frontend utilise l'API Clipboard du WebView avec fallback local document.execCommand("copy").

pre.007-fix.002 — copie explicite du preview payload

Le preview transaction reste borné à 512 bytes côté Rust mais devient copiable explicitement depuis le modal. Le bouton copie uniquement le preview hex déjà présent côté frontend ; il ne charge ni n'expose le payload complet. Les canaris Rust ajoutés en fix.001 sont corrigés avec des raw string literals valides.

pre.007-fix.003 — rescope du canari de tracing copie

Le canari de sécurité ne bannit plus globalement des variables métier légitimes comme signature : il vérifie uniquement que les événements de copie transportent { fieldId } et jamais la valeur copiée. Aucun runtime/frontend n'est modifié.

pre.008 — RawAccountState DataTables serverSide + détail

Brancher RAW Accounts sur RawAccountStateInspectionRead avec le même modèle server-side que Transactions : start/length -> offset/limit, counts exacts, filtres Store explicites pubkey exacte + slot min/max + direction, searching:false, ordering:false, whitelist 25/50/100 et DataTables comme unique pager. Les rows restent data-free et exposent pubkey, slot, state hash, owner, lamports, executable, rent epoch et data length.

Le détail reçoit uniquement l'identité de row (pubkey, slot, state_hash), dérive le network du Store ouvert et appelle RawAccountStateRead. Les bytes Account ne traversent jamais la table ; le modal reçoit au maximum 512 bytes sous forme de preview hex avec taille exacte et flag de troncature. Pubkey, owner, state hash et preview data réutilisent le helper générique troncature/tooltip/copie stabilisé en pre.007-fix.001/002/003. Les traces ne contiennent aucune valeur de pubkey, slot, state hash, owner ou data.

pre.009 — extension Store observations

Étendre le contrat Store backend-neutral avec RawTransactionObservationInspectionRead et RawAccountObservationInspectionRead, sans modifier les reads par clé existants. Les deux familles utilisent RawInspectionPageRequest / RawInspectionPage<T> avec counts exacts et random access offset/limit. La requête porte le network obligatoire, une référence d'entité exacte optionnelle et la direction canonique ; la cohérence network/référence est validée avant dispatch.

Les summaries d'observation restent strictement sans bytes RAW : clé d'observation, référence durable et RawAcquisitionProvenance sûre ; Account conserve seulement les metadata Yellowstone optionnelles déjà présentes dans le modèle (is_startup, transaction signature associée, write_version). PostgreSQL implémente quatre statements privés, un ASC et un DESC par famille, ordonnés déterministiquement par (received_at_unix_millis, observation_key) et utilisant counts + LEFT JOIN LATERAL + LIMIT/OFFSET. Les queries keyset/read-by-key historiques restent inchangées. L'inventaire symétrique Store/PostgreSQL passe de 12 à 14 capabilities RAW. Aucune migration, index, UI ou DTO DataTables n'est ajouté dans cette tranche ; l'intégration visuelle reste réservée à pre.010.

pre.010 — observations UI + hardening UX

Intégrer les observations dans les modals de détail Transaction et Account via deux DataTables server-side dédiées, filtrées implicitement par l'identité exacte de l'entité ouverte. Les tables utilisent exclusivement les capabilities RawTransactionObservationInspectionRead / RawAccountObservationInspectionRead livrées en pre.009, avec ordre descending fixé côté application, start/length -> offset/limit, whitelist 25/50/100, counts exacts et DataTables comme seul propriétaire de pagination. Aucun cursor, network, search globale ou ordre DataTables arbitraire ne traverse l'IPC.

Les rows Observation projettent uniquement RawAcquisitionProvenance sûre : provider, protocol, acquisition method, origin, received/observed timestamps, endpoint/session/filter/commitment optionnels et hash/taille source. Account ajoute uniquement les metadata déjà admises (is_startup, write_version, transaction signature associée). Aucun source payload byte, payload RAW Transaction ni data Account n'est exposé. Les identifiants longs réutilisent le helper troncature/tooltip/copie sans valeur dans le tracing.

Le hardening V1 centralise la validation offset/limit côté Rust, rejette les draw/start/length hostiles avant IPC, refuse les counts dépassant Number.MAX_SAFE_INTEGER, ignore les réponses Observation devenues stale après changement/fermeture d'entité, ajuste les colonnes à l'ouverture du modal et conserve les erreurs d'observation dans une surface dédiée sans donnée métier.

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

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.