48 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: falseen 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 = -1accepté ; - 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
Verrouiller la surface V1 sans ajouter de fonctionnalité métier ni modifier Store API/façade/PostgreSQL. Un nouveau firewall exécutable release_completeness.rs doit rendre exacts les inventaires directs Cargo/npm, les modules Rust Store Desk, les commands Tauri et les permissions desktop. La composition Config Store Desk reste exactement logging + store sur devnet/mainnet/testnet, sans URI ni secret dans le composite.
Le frontend est rescanné contre toute ouverture réseau navigateur, stockage navigateur RAW, dialogue natif ou matérialisation PostgreSQL/SQL. Les DTO/IPC doivent rester backend-neutral et metadata-only, avec les previews RAW bornés uniquement dans les DTO de détail déjà admis. Enfin, la coexistence des deux paradigmes de pagination est verrouillée explicitement : RawPageCursor/RawPageRequest et les statements PostgreSQL LIST_* restent keyset et OFFSET-free pour les machines/workers, tandis que RawInspectionPageRequest et les statements INSPECT_* conservent leur random access OFFSET pour Store Desk/DataTables. Aucun runtime n'est ajouté par cette tranche.
pre.012 — gate technique final
Ouvrir exclusivement la preuve technique finale sur la candidate déjà durcie, sans nouveau runtime, frontend, DTO, contrat Store, SQL, migration, Config, dépendance ou test fonctionnel. Le gate est séquentiel : tout échec invalide la tranche même si le shell poursuit ensuite d'autres commandes.
Ordre attendu avant le build desktop final :
cargo fmt --all -- --check
python3 scripts/audit_rust_workspace_rules.py
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas
cargo check --workspace
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace --all-targets --all-features
cargo check -p ksp-store-lib --no-default-features
cargo tree -p ksp-app-store-desk --edges normal
cargo tree -p ksp-app-store-desk -e features
cargo tree -p ksp-store-lib --edges normal
cargo tree -p ksp-store-lib -e features
cargo tree --duplicates
Si un environnement PostgreSQL opérateur sûr est disponible, un smoke strictement read-only est exécuté ensuite via Store Desk : Store ready, page Transaction, page Account, détail/observations lorsqu'une row existe, puis fermeture propre. Aucune archive, purge, force-rehydrate ou autre mutation n'est admise.
Le build Tauri est l'ultime opération lourde de la tranche :
(cd crates/ksp-app-store-desk && cargo tauri build)
Un défaut ouvre pre.012-fix.NNN; aucune réconciliation README/USAGE durable n'est faite avant un gate entièrement vert.
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.