diff --git a/Cargo.toml b/Cargo.toml index e90aade..fe92ebf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ # file: Cargo.toml -# version: 393 +# version: 394 [workspace] resolver = "3" members = ["crates/ksp-app-config-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-interface-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-program-api", "crates/ksp-store-api", "crates/ksp-store-lib", "crates/ksp-store-postgres-lib", "crates/ksp-wallet-lib"] [workspace.package] -version = "0.3.6-pre.1" +version = "0.3.6-pre.1.fix.1" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" diff --git a/deltas/0.1.4/pre.004.md b/deltas/0.1.4/pre.004.md index e89c7f5..e9b7a43 100644 --- a/deltas/0.1.4/pre.004.md +++ b/deltas/0.1.4/pre.004.md @@ -1,5 +1,5 @@ - + # Delta 0.1.4-pre.004 — squelette Rust/Tauri de `ksp-app-config-desk` @@ -74,11 +74,11 @@ La bibliothèque utilise les crate types Tauri usuels `staticlib`, `cdylib` et ` Les versions ont été revérifiées au moment de cette tranche : -| Dépendance | Version auditée | Contrainte workspace | Usage immédiat | -| ------------- | --------------: | -------------------: | -------------------------- | -| `tauri` | `2.11.5` | `^2.11` | runtime/application | -| `tauri-build` | `2.6.3` | `^2.6` | build script Tauri | -| `fs2` | `0.4.3` | `^0.4` | verrou single-instance | +| Dépendance | Version auditée | Contrainte workspace | Usage immédiat | +|---------------|----------------:|---------------------:|------------------------| +| `tauri` | `2.11.5` | `^2.11` | runtime/application | +| `tauri-build` | `2.6.3` | `^2.6` | build script Tauri | +| `fs2` | `0.4.3` | `^0.4` | verrou single-instance | Les contraintes sont déclarées sous `[workspace.dependencies]`; la crate applicative les consomme avec `.workspace = true`. @@ -142,7 +142,7 @@ La destination de build est fixée dès maintenant à : Le premier couple de ports desk est réservé : | Usage | Port | -| --------- | ---: | +|-----------|-----:| | Vite HTTP | 1430 | | Vite HMR | 1431 | diff --git a/deltas/0.1.4/pre.005-fix.001.md b/deltas/0.1.4/pre.005-fix.001.md index 060fa2e..8ea9757 100644 --- a/deltas/0.1.4/pre.005-fix.001.md +++ b/deltas/0.1.4/pre.005-fix.001.md @@ -1,5 +1,5 @@ - + # `0.1.4-pre.005-fix.001` — Classification npm et commande Tauri workspace @@ -98,7 +98,7 @@ Capability: tracing:default ## 7. Fichiers modifiés | Fichier | Action | -| ---------------------------------------------- | ------: | +|------------------------------------------------|--------:| | `Cargo.toml` | modifié | | `crates/ksp-app-config-desk/package.json` | modifié | | `crates/ksp-app-config-desk/README.md` | modifié | diff --git a/deltas/0.1.4/pre.005.md b/deltas/0.1.4/pre.005.md index 3ebfc9a..0db9b71 100644 --- a/deltas/0.1.4/pre.005.md +++ b/deltas/0.1.4/pre.005.md @@ -1,5 +1,5 @@ - + # Delta 0.1.4-pre.005 — gabarit frontend Vite/TypeScript/SCSS et tracing Tauri @@ -97,18 +97,18 @@ La permission tracing entre dans le même delta que le plugin, conformément à Les générations actuelles ont été revérifiées avant rédaction du manifest : -| Dépendance | Version actuelle auditée | Contrainte déclarée | -| ---------------------------------- | -----------------------: | ------------------: | -| `@fltsci/tauri-plugin-tracing` | `0.3.4` | `^0.3` | -| `@fortawesome/fontawesome-free` | `7.3.1` | `^7.3` | -| `@tauri-apps/api` | `2.11.1` | `^2.11` | -| `@types/node` | `26.1.2` | `^26.1` | -| `bootstrap` | `5.3.8` | `^5.3` | -| `resize-observer-polyfill` | `1.5.1` | `^1.5` | -| `sass-embedded` | `1.102.0` | `^1.102` | -| `simplebar` | `6.3.3` | `^6.3` | -| `typescript` | `7.0.2` | `^7.0` | -| `vite` | `8.2.0` | `^8.2` | +| Dépendance | Version actuelle auditée | Contrainte déclarée | +|---------------------------------|-------------------------:|--------------------:| +| `@fltsci/tauri-plugin-tracing` | `0.3.4` | `^0.3` | +| `@fortawesome/fontawesome-free` | `7.3.1` | `^7.3` | +| `@tauri-apps/api` | `2.11.1` | `^2.11` | +| `@types/node` | `26.1.2` | `^26.1` | +| `bootstrap` | `5.3.8` | `^5.3` | +| `resize-observer-polyfill` | `1.5.1` | `^1.5` | +| `sass-embedded` | `1.102.0` | `^1.102` | +| `simplebar` | `6.3.3` | `^6.3` | +| `typescript` | `7.0.2` | `^7.0` | +| `vite` | `8.2.0` | `^8.2` | Ces dépendances sont placées sous `devDependencies`, conformément à l'usage `npm i -D` retenu pour les applications desk : elles participent au build du bundle desktop et ne constituent pas un runtime Node déployé séparément. @@ -155,10 +155,10 @@ afin de ne pas introduire dès la première application KSP des options désorma Le serveur est configuré ainsi : -| Usage | Port | Politique | -| ------------------------ | ---: | --------- | -| Vite HTTP | 1430 | strict | -| WebSocket dev/HMR | 1431 | dédié | +| Usage | Port | Politique | +|-------------------|-----:|-----------| +| Vite HTTP | 1430 | strict | +| WebSocket dev/HMR | 1431 | dédié | `strictPort = true` interdit le basculement silencieux du serveur HTTP vers un autre port. diff --git a/deltas/0.1.4/pre.006.md b/deltas/0.1.4/pre.006.md index 2273a89..66c584d 100644 --- a/deltas/0.1.4/pre.006.md +++ b/deltas/0.1.4/pre.006.md @@ -1,5 +1,5 @@ - + # `0.1.4-pre.006` — Bootstrap backend, `AppState` et runtime Logging initial @@ -193,25 +193,25 @@ ts-rs ## 11. Fichiers -| Fichier | Action | -| ----------------------------------------------------------- | ------: | -| `Cargo.toml` | modifié | -| `crates/ksp-app-config-desk/Cargo.toml` | modifié | -| `crates/ksp-app-config-desk/src/app_state.rs` | ajouté | -| `crates/ksp-app-config-desk/src/bootstrap.rs` | ajouté | -| `crates/ksp-app-config-desk/src/constants.rs` | ajouté | -| `crates/ksp-app-config-desk/src/dto_common.rs` | ajouté | -| `crates/ksp-app-config-desk/src/errors.rs` | modifié | -| `crates/ksp-app-config-desk/src/lib.rs` | modifié | -| `crates/ksp-app-config-desk/src/tauri.rs` | modifié | -| `crates/ksp-app-config-desk/unit_tests/bootstrap.rs` | ajouté | -| `crates/ksp-app-config-desk/unit_tests/dto_common.rs` | ajouté | -| `crates/ksp-app-config-desk/frontend/fonts/README.md` | ajouté | -| `crates/ksp-app-config-desk/README.md` | modifié | -| `crates/ksp-app-config-desk/USAGE.md` | modifié | -| `crates/ksp-app-config-desk/TODO.md` | modifié | -| `docs/plans/006-V0_1_4_CONFIG_DESKTOP_PLAN.md` | modifié | -| `deltas/0.1.4/pre.006.md` | ajouté | +| Fichier | Action | +|-------------------------------------------------------|--------:| +| `Cargo.toml` | modifié | +| `crates/ksp-app-config-desk/Cargo.toml` | modifié | +| `crates/ksp-app-config-desk/src/app_state.rs` | ajouté | +| `crates/ksp-app-config-desk/src/bootstrap.rs` | ajouté | +| `crates/ksp-app-config-desk/src/constants.rs` | ajouté | +| `crates/ksp-app-config-desk/src/dto_common.rs` | ajouté | +| `crates/ksp-app-config-desk/src/errors.rs` | modifié | +| `crates/ksp-app-config-desk/src/lib.rs` | modifié | +| `crates/ksp-app-config-desk/src/tauri.rs` | modifié | +| `crates/ksp-app-config-desk/unit_tests/bootstrap.rs` | ajouté | +| `crates/ksp-app-config-desk/unit_tests/dto_common.rs` | ajouté | +| `crates/ksp-app-config-desk/frontend/fonts/README.md` | ajouté | +| `crates/ksp-app-config-desk/README.md` | modifié | +| `crates/ksp-app-config-desk/USAGE.md` | modifié | +| `crates/ksp-app-config-desk/TODO.md` | modifié | +| `docs/plans/006-V0_1_4_CONFIG_DESKTOP_PLAN.md` | modifié | +| `deltas/0.1.4/pre.006.md` | ajouté | ## 12. Validations à exécuter diff --git a/deltas/0.1.4/pre.007.md b/deltas/0.1.4/pre.007.md index b3b5d5b..2527293 100644 --- a/deltas/0.1.4/pre.007.md +++ b/deltas/0.1.4/pre.007.md @@ -1,5 +1,5 @@ - + # `0.1.4-pre.007` — Bridge frontend logging KSP @@ -165,22 +165,22 @@ Le derive TS-RS ajoute le test d'export de `FrontendLogPayloadDto`. ## 11. Fichiers -| Fichier | Action | -| ------------------------------------------------------------- | ------: | -| `Cargo.toml` | modifié | -| `crates/ksp-app-config-desk/src/constants.rs` | modifié | -| `crates/ksp-app-config-desk/src/errors.rs` | modifié | -| `crates/ksp-app-config-desk/src/frontend_logging.rs` | ajouté | -| `crates/ksp-app-config-desk/src/lib.rs` | modifié | -| `crates/ksp-app-config-desk/src/tauri.rs` | modifié | -| `crates/ksp-app-config-desk/unit_tests/frontend_logging.rs` | ajouté | -| `crates/ksp-app-config-desk/frontend/ts/frontend_log.ts` | ajouté | -| `crates/ksp-app-config-desk/frontend/ts/main.ts` | modifié | -| `crates/ksp-app-config-desk/frontend/ts/splash.ts` | modifié | -| `crates/ksp-app-config-desk/README.md` | modifié | -| `crates/ksp-app-config-desk/USAGE.md` | modifié | -| `crates/ksp-app-config-desk/TODO.md` | modifié | -| `deltas/0.1.4/pre.007.md` | ajouté | +| Fichier | Action | +|-------------------------------------------------------------|--------:| +| `Cargo.toml` | modifié | +| `crates/ksp-app-config-desk/src/constants.rs` | modifié | +| `crates/ksp-app-config-desk/src/errors.rs` | modifié | +| `crates/ksp-app-config-desk/src/frontend_logging.rs` | ajouté | +| `crates/ksp-app-config-desk/src/lib.rs` | modifié | +| `crates/ksp-app-config-desk/src/tauri.rs` | modifié | +| `crates/ksp-app-config-desk/unit_tests/frontend_logging.rs` | ajouté | +| `crates/ksp-app-config-desk/frontend/ts/frontend_log.ts` | ajouté | +| `crates/ksp-app-config-desk/frontend/ts/main.ts` | modifié | +| `crates/ksp-app-config-desk/frontend/ts/splash.ts` | modifié | +| `crates/ksp-app-config-desk/README.md` | modifié | +| `crates/ksp-app-config-desk/USAGE.md` | modifié | +| `crates/ksp-app-config-desk/TODO.md` | modifié | +| `deltas/0.1.4/pre.007.md` | ajouté | ## 12. Validations à exécuter diff --git a/deltas/0.1.4/pre.008-fix.001.md b/deltas/0.1.4/pre.008-fix.001.md index 6b1eb3a..97c1a13 100644 --- a/deltas/0.1.4/pre.008-fix.001.md +++ b/deltas/0.1.4/pre.008-fix.001.md @@ -1,5 +1,5 @@ - + # `0.1.4-pre.008-fix.001` — Contextes splash numériques et réexports de labels inutiles @@ -86,13 +86,13 @@ Il vérifie que les erreurs bornées conservent bien ces valeurs dans `Error::co ## 6. Fichiers modifiés -| Fichier | Action | -| ------------------------------------------------------ | ------: | -| `Cargo.toml` | modifié | -| `crates/ksp-app-config-desk/src/lib.rs` | modifié | -| `crates/ksp-app-config-desk/src/splash.rs` | modifié | -| `crates/ksp-app-config-desk/unit_tests/splash.rs` | modifié | -| `deltas/0.1.4/pre.008-fix.001.md` | ajouté | +| Fichier | Action | +|---------------------------------------------------|--------:| +| `Cargo.toml` | modifié | +| `crates/ksp-app-config-desk/src/lib.rs` | modifié | +| `crates/ksp-app-config-desk/src/splash.rs` | modifié | +| `crates/ksp-app-config-desk/unit_tests/splash.rs` | modifié | +| `deltas/0.1.4/pre.008-fix.001.md` | ajouté | ## 7. Validations demandées diff --git a/deltas/0.1.4/pre.008-fix.003.md b/deltas/0.1.4/pre.008-fix.003.md index b028c86..07df205 100644 --- a/deltas/0.1.4/pre.008-fix.003.md +++ b/deltas/0.1.4/pre.008-fix.003.md @@ -1,5 +1,5 @@ - + # `0.1.4-pre.008-fix.003` — visibilité des logs `debug` de Config Desk @@ -57,12 +57,12 @@ ksp-app-config-desk -> Debug ## 5. Fichiers modifiés -| Fichier | Action | -| ------------------------------------------------- | ------: | -| `Cargo.toml` | modifié | -| `config/std.logging.json` | modifié | -| `crates/ksp-config-lib/unit_tests/logging.rs` | modifié | -| `deltas/0.1.4/pre.008-fix.003.md` | ajouté | +| Fichier | Action | +|-----------------------------------------------|--------:| +| `Cargo.toml` | modifié | +| `config/std.logging.json` | modifié | +| `crates/ksp-config-lib/unit_tests/logging.rs` | modifié | +| `deltas/0.1.4/pre.008-fix.003.md` | ajouté | Aucune dépendance n'est ajoutée. diff --git a/deltas/0.3.6/pre.001-fix.001.md b/deltas/0.3.6/pre.001-fix.001.md new file mode 100644 index 0000000..46e8767 --- /dev/null +++ b/deltas/0.3.6/pre.001-fix.001.md @@ -0,0 +1,125 @@ + + + +# Delta `0.3.6-pre.001-fix.001` — sizing souple et audit Markdown effectif + +## Base requise + +```text +0.3.6-pre.001 +workspace.package.version = 0.3.6-pre.1 +``` + +Ce fix corrige la tranche de cadrage `pre.001`. Il modifie un script d'audit consommé par le gate ; conformément à `VER-ID-007` et `VER-ID-010`, la version Cargo devient `0.3.6-pre.1.fix.1`. + +## Objectif + +- remplacer le forecast tabulaire par une hiérarchie éditable `### pre.NNN` puis `#### pre.NNN-fix.MMM` ; +- corriger le faux négatif de `scripts/audit_markdown_tables.py` ; +- imposer l'alignement source gauche, droit ou centré indiqué par la ligne séparatrice ; +- réaligner les documents actifs révélés par le validateur corrigé sans réécrire les anciens deltas publiés. + +## Cause racine + +`_is_separator_row` appliquait la regex aux cellules brutes. Une ligne telle que `| --- | ---: |` contient des espaces dans chaque cellule et n'était donc pas reconnue comme séparateur. Le tableau entier était ignoré, son compteur n'augmentait pas et aucune largeur n'était contrôlée. + +Le validateur contrôlait par ailleurs uniquement le padding droit des cellules de contenu. Il n'appliquait pas la sémantique de `---:` pour une colonne alignée à droite ni celle de `:---:` pour une colonne centrée. + +## Corrections + +### Détection et validation + +- détection tolérante par `strip` afin qu'un séparateur plausible mais mal formaté entre dans l'audit ; +- validation canonique stricte du séparateur brut, qui doit remplir la colonne sans espaces ; +- `---` et `:---` : un espace gauche puis padding droit ; +- `---:` : padding gauche puis un espace droit ; +- `:---:` : paddings équilibrés à un espace près ; +- largeur minimale compatible avec trois tirets et les marqueurs d'alignement ; +- erreurs dédiées par orientation. + +### Régressions automatisées + +Le nouveau test `scripts/tests/test_audit_markdown_tables.py` couvre cinq cas : + +- tableau gauche et droit canonique accepté ; +- tableau gauche explicite et centré accepté ; +- séparateur espacé auparavant ignoré désormais rejeté ; +- cellule alignée à gauche sous `---:` rejetée ; +- cellule alignée à droite sous `:---` rejetée. + +### Sizing de la v0.3.6 + +Le tableau du découpage prévisionnel est remplacé par des sous-sections. Chaque prerelease porte son statut, son budget, son entrée, son contenu et sa sortie. `pre.001-fix.001` est inséré directement sous `pre.001`, comme dans le sizing durable du plan 025. + +### Réalignement documentaire + +Le gate corrigé a révélé 13 documents durables contenant soit un séparateur espacé, soit un padding incompatible avec ses marqueurs. Ils sont réalignés mécaniquement, sans changement sémantique : + +```text +docs/formats/KSPWALLET_V1.md +docs/formats/KSPWALLET_V2.md +docs/plans/005-V0_1_3_CONFIG_FOUNDATION_PLAN.md +docs/plans/006-V0_1_4_CONFIG_DESKTOP_PLAN.md +docs/plans/011-V0_2_4_HTTP_BLOCKS_ECONOMICS_PLAN.md +docs/plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md +docs/plans/014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md +docs/plans/026-V0_3_5_INTERFACE_ACQUISITION_EVENTS_PLAN.md +docs/plans/027-V0_3_6_JOB_API_BACKFILL_PLAN.md +docs/validation/010-V0_2_7_ONCHAIN_WEBSOCKET.md +docs/validation/011-V0_2_8_HELIUS_LASERSTREAM_WEBSOCKET.md +docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md +docs/validation/023-V0_3_6_JOB_API_BACKFILL.md +``` + +Les deltas antérieurs restent inchangés conformément à `DOC-TABLE-006`. + +## Fichiers ajoutés + +```text +scripts/tests/test_audit_markdown_tables.py +deltas/0.3.6/pre.001-fix.001.md +``` + +## Fichiers modifiés + +```text +Cargo.toml +scripts/audit_markdown_tables.py +docs/rules/RULES_DOCUMENTATION.md +les 13 documents durables listés dans la section de réalignement +``` + +## Fichiers supprimés + +Aucun. + +## Décisions prises + +- le script d'audit est un artefact de code d'outillage et impose donc le suffixe Cargo `.fix.1` ; +- le séparateur reste détecté de façon tolérante mais validé selon la forme canonique stricte ; +- l'alignement source suit ses marqueurs Markdown, pas seulement le rendu visuel ; +- le réalignement historique est limité aux documents durables encore audités, jamais aux deltas publiés ; +- aucun contenu fonctionnel Job ou Backfill n'est ouvert par ce fix. + +## Validations exécutées + +```text +python3 -m unittest scripts/tests/test_audit_markdown_tables.py +-> 5 tests PASS + +python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.3.6 +-> clean, 264 tables / 141 fichiers + +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 +``` + +## Validations non exécutées + +`cargo` n'est pas installé dans l'environnement d'assemblage. `cargo fmt`, `cargo check`, `cargo clippy` et `cargo test` ne sont pas annoncés comme réussis. Aucun fichier Rust ou manifeste de crate n'est modifié, mais l'opérateur doit rejouer le gate demandé après application du fix. + +## Questions ouvertes + +Aucune question ne bloque `pre.002`. diff --git a/docs/formats/KSPWALLET_V1.md b/docs/formats/KSPWALLET_V1.md index 36b25b6..52ae69a 100644 --- a/docs/formats/KSPWALLET_V1.md +++ b/docs/formats/KSPWALLET_V1.md @@ -1,5 +1,5 @@ - + # `.kspwallet` V1 — spécification du format natif Wallet KSP @@ -399,29 +399,29 @@ Les champs binaires sont les **octets décodés** du Base64url, jamais le texte | Tag hex | Champ | |--------:|----------------------------------------------------| -| `0001` | magic | -| `0002` | format_version | -| `0003` | owner_auth_public_key | -| `0010` | view enabled | -| `0011` | rôle VIEW littéral `view` | -| `0012` | view slot_id ; longueur zéro lorsque VIEW disabled | -| `0100` | slot_id | -| `0101` | slot role | -| `0102` | KDF algorithm | -| `0103` | KDF version | -| `0104` | KDF memory_kib | -| `0105` | KDF iterations | -| `0106` | KDF parallelism | -| `0107` | KDF salt | -| `0108` | wrap algorithm | -| `0109` | wrap nonce | -| `010A` | wrap ciphertext | -| `0200` | compartment kind | -| `0201` | compartment payload version | -| `0202` | compartment algorithm | -| `0203` | compartment nonce | -| `0204` | compartment ciphertext | -| `0500` | state-signature algorithm | +| `0001` | magic | +| `0002` | format_version | +| `0003` | owner_auth_public_key | +| `0010` | view enabled | +| `0011` | rôle VIEW littéral `view` | +| `0012` | view slot_id ; longueur zéro lorsque VIEW disabled | +| `0100` | slot_id | +| `0101` | slot role | +| `0102` | KDF algorithm | +| `0103` | KDF version | +| `0104` | KDF memory_kib | +| `0105` | KDF iterations | +| `0106` | KDF parallelism | +| `0107` | KDF salt | +| `0108` | wrap algorithm | +| `0109` | wrap nonce | +| `010A` | wrap ciphertext | +| `0200` | compartment kind | +| `0201` | compartment payload version | +| `0202` | compartment algorithm | +| `0203` | compartment nonce | +| `0204` | compartment ciphertext | +| `0500` | state-signature algorithm | Les tags ne remplacent pas l'ordre normatif ; **l'ordre ci-dessous est obligatoire**. diff --git a/docs/formats/KSPWALLET_V2.md b/docs/formats/KSPWALLET_V2.md index 7d7688c..b8082df 100644 --- a/docs/formats/KSPWALLET_V2.md +++ b/docs/formats/KSPWALLET_V2.md @@ -1,5 +1,5 @@ - + # `.kspwallet` V2 — spécification du wire binaire natif KSP @@ -104,14 +104,14 @@ Une implémentation conforme doit vérifier les bornes **avant** toute allocatio Le début de fichier est strictement : -| Ordre | Champ | Taille | Valeur / règle | +| Ordre | Champ | Taille | Valeur / règle | |------:|---------------------------|-----------------:|------------------------------------------------| -| 1 | `magic` | 9 | ASCII exact `KSPWALLET` | -| 2 | `format_version` | 2 | `0x0002` | -| 3 | `document_length` | 4 | longueur totale exacte du fichier | -| 4 | `flags` | 2 | bit 0 = VIEW activé ; tous les autres bits = 0 | -| 5 | `owner_auth_public_key` | 32 | clé publique Ed25519 OWNER | -| 6 | `view_descriptor.slot_id` | 16 conditionnels | présent uniquement si `flags & 0x0001 != 0` | +| 1 | `magic` | 9 | ASCII exact `KSPWALLET` | +| 2 | `format_version` | 2 | `0x0002` | +| 3 | `document_length` | 4 | longueur totale exacte du fichier | +| 4 | `flags` | 2 | bit 0 = VIEW activé ; tous les autres bits = 0 | +| 5 | `owner_auth_public_key` | 32 | clé publique Ed25519 OWNER | +| 6 | `view_descriptor.slot_id` | 16 conditionnels | présent uniquement si `flags & 0x0001 != 0` | Offsets fixes avant le descripteur conditionnel : @@ -154,20 +154,20 @@ Aucun compteur de slots ou de compartiments n'est nécessaire : leur cardinalit Chaque key slot est encodé ainsi : -| Champ | Taille | Valeur / règle | +| Champ | Taille | Valeur / règle | |--------------------------|---------:|-----------------------------------------| -| `role` | 1 | `0x01` OWNER, `0x02` VIEW | -| `slot_id` | 16 | identifiant binaire exact | -| `kdf_algorithm` | 1 | `0x01` Argon2id | -| `kdf_version` | 4 | `19` | -| `memory_kib` | 4 | `1..1 048 576`, et `>= parallelism * 8` | -| `iterations` | 4 | `1..64` | -| `parallelism` | 4 | `1..64` | -| `salt_length` | 1 | `16..64` | +| `role` | 1 | `0x01` OWNER, `0x02` VIEW | +| `slot_id` | 16 | identifiant binaire exact | +| `kdf_algorithm` | 1 | `0x01` Argon2id | +| `kdf_version` | 4 | `19` | +| `memory_kib` | 4 | `1..1 048 576`, et `>= parallelism * 8` | +| `iterations` | 4 | `1..64` | +| `parallelism` | 4 | `1..64` | +| `salt_length` | 1 | `16..64` | | `salt` | variable | exactement `salt_length` octets | -| `wrap_algorithm` | 1 | `0x01` XChaCha20-Poly1305 | -| `wrap_nonce` | 24 | nonce exact | -| `wrap_ciphertext_length` | 2 | `16..4096` | +| `wrap_algorithm` | 1 | `0x01` XChaCha20-Poly1305 | +| `wrap_nonce` | 24 | nonce exact | +| `wrap_ciphertext_length` | 2 | `16..4096` | | `wrap_ciphertext` | variable | exactement la longueur déclarée | Ordre obligatoire : @@ -189,13 +189,13 @@ Toute divergence est invalide avant KDF/déchiffrement. Chaque compartiment est encodé : -| Champ | Taille | Valeur / règle | +| Champ | Taille | Valeur / règle | |---------------------|---------:|------------------------------------------------------| -| `kind` | 1 | `0x01` OWNER-CONTROL, `0x02` METADATA, `0x03` SECRET | -| `payload_version` | 4 | `1` pour le profil initial V2 | -| `algorithm` | 1 | `0x01` XChaCha20-Poly1305 | -| `nonce` | 24 | nonce exact | -| `ciphertext_length` | 4 | longueur exacte | +| `kind` | 1 | `0x01` OWNER-CONTROL, `0x02` METADATA, `0x03` SECRET | +| `payload_version` | 4 | `1` pour le profil initial V2 | +| `algorithm` | 1 | `0x01` XChaCha20-Poly1305 | +| `nonce` | 24 | nonce exact | +| `ciphertext_length` | 4 | longueur exacte | | `ciphertext` | variable | ciphertext + tag Poly1305 | Bornes initiales : @@ -216,8 +216,8 @@ La fin du document est : | Champ | Taille | Valeur / règle | |-----------------------------|-------:|---------------------------| -| `state_signature.algorithm` | 1 | `0x01` Ed25519 | -| `state_signature.signature` | 64 | signature detached exacte | +| `state_signature.algorithm` | 1 | `0x01` Ed25519 | +| `state_signature.signature` | 64 | signature detached exacte | Aucun octet ne peut suivre ces 65 octets. diff --git a/docs/plans/005-V0_1_3_CONFIG_FOUNDATION_PLAN.md b/docs/plans/005-V0_1_3_CONFIG_FOUNDATION_PLAN.md index 70b6e8e..8b83630 100644 --- a/docs/plans/005-V0_1_3_CONFIG_FOUNDATION_PLAN.md +++ b/docs/plans/005-V0_1_3_CONFIG_FOUNDATION_PLAN.md @@ -1,5 +1,5 @@ - + # Plan `0.1.3` — Configuration foundation @@ -605,23 +605,23 @@ Décisions : ### 8.1 Écart `ksp-logging-lib 0.1.2` à fermer -| Capacité | `0.1.2` | Requise par `std.logging.json` | +| Capacité | `0.1.2` | Requise par `std.logging.json` | |-----------------------------------|----------------:|-------------------------------------:| -| filtre global | oui | oui | -| overrides par target | oui | oui | -| lifecycle spans | oui | oui | -| console stdout/stderr | oui | oui | -| console enabled | via `Option` | oui explicite | -| console ANSI configurable | non | oui | -| format console configurable | non | oui | -| plusieurs fichiers | non | oui | -| rotation par fichier | un seul fichier | oui par sink | -| format par fichier | non | oui | -| filtre par sink/target | non | oui | -| filtre par sink/domain | non | oui | -| filtre par sink/niveau | non indépendant | oui | -| hot reload transactionnel | oui | à conserver | -| non-blocking/guards/drop counters | oui | à conserver et généraliser par sinks | +| filtre global | oui | oui | +| overrides par target | oui | oui | +| lifecycle spans | oui | oui | +| console stdout/stderr | oui | oui | +| console enabled | via `Option` | oui explicite | +| console ANSI configurable | non | oui | +| format console configurable | non | oui | +| plusieurs fichiers | non | oui | +| rotation par fichier | un seul fichier | oui par sink | +| format par fichier | non | oui | +| filtre par sink/target | non | oui | +| filtre par sink/domain | non | oui | +| filtre par sink/niveau | non indépendant | oui | +| hot reload transactionnel | oui | à conserver | +| non-blocking/guards/drop counters | oui | à conserver et généraliser par sinks | Ce tableau est un **gap identifié**, pas une invitation à déplacer Logging dans Config. La tranche qui le ferme modifie `ksp-logging-lib` uniquement dans son domaine propriétaire. @@ -1986,4 +1986,3 @@ Le `pre.001-fix.003` est validable lorsque les décisions suivantes sont accept - les évolutions `ksp-logging-lib` sont explicites en `pre.004` (contrats/settings), `pre.005` (runtime multi-sink + level/target/formats) puis `pre.006` (routing structuré domain), avant gel du schema Logging ; - chaque prerelease vise environ 15–20 minutes de travail effectif et doit être scindée si ce budget devient manifestement irréaliste ; - aucune implémentation `pre.002` ne commence avant validation utilisateur de ce plan corrigé. - diff --git a/docs/plans/006-V0_1_4_CONFIG_DESKTOP_PLAN.md b/docs/plans/006-V0_1_4_CONFIG_DESKTOP_PLAN.md index 9d8fe89..c946f6f 100644 --- a/docs/plans/006-V0_1_4_CONFIG_DESKTOP_PLAN.md +++ b/docs/plans/006-V0_1_4_CONFIG_DESKTOP_PLAN.md @@ -1,5 +1,5 @@ - + # Plan `0.1.4` — `ksp-app-config-desk` @@ -239,7 +239,7 @@ Chaque application Tauri desk KSP reçoit un couple de ports Vite/HMR propre. La | Application | Port Vite HTTP | Port HMR | |-----------------------|---------------:|---------:| -| `ksp-app-config-desk` | `1430` | `1431` | +| `ksp-app-config-desk` | `1430` | `1431` | Les applications suivantes incrémentent le couple de deux ports (`1432/1433`, puis `1434/1435`, etc.). Vite doit utiliser un port strict afin qu'une collision soit signalée au lieu de provoquer un basculement silencieux vers un autre port. Cette allocation permet de faire fonctionner simultanément plusieurs applications desk en mode développement. @@ -248,7 +248,7 @@ Les applications suivantes incrémentent le couple de deux ports (`1432/1433`, p Aucune dépendance n'est ajoutée par `pre.001`. Les versions suivantes ont été revérifiées depuis les registres/documentations officiels afin de fixer la génération candidate à réévaluer **au moment exact de l'ajout** : | Dépendance | Version actuelle auditée | Usage envisagé | -| ------------------------------- | -----------------------: | -------------------------------------------- | +|---------------------------------|-------------------------:|----------------------------------------------| | `tauri` | `2.11.5` | runtime desktop | | `tauri-build` | `2.6.3` | build Tauri | | `tauri-plugin-tracing` | `0.3.4` | intégration tracing à la frontière Tauri | @@ -695,7 +695,7 @@ Le shell applique également une instrumentation frontend systématique : clics Les noms ci-dessous sont les noms fonctionnels cibles ; ils pourront être normalisés avant implémentation, mais leurs responsabilités sont fixées. | Commande Tauri | Service interne | API KSP principale | Secret réel ? | -| ------------------------------ | ------------------------------------ | --------------------------------------------------------------------------- | -------------------------------------------: | +|--------------------------------|--------------------------------------|-----------------------------------------------------------------------------|---------------------------------------------:| | `get_app_snapshot` | `config_service` + `logging_service` | registry/management + runtime state | non | | `list_config_documents` | `config_service` | future vue publique du `ConfigFileRegistry` | non | | `inspect_config_document` | `config_service` | `load_validated_document` + `read_source` si erreur | non | @@ -895,7 +895,7 @@ La réussite de ce scénario ferme le critère « une erreur de nouvelle configu La release ne peut pas être clôturée sans preuve des cas suivants : | Cas | Action UI | Résultat attendu | -| --- | ----------------------------- | ------------------------------------------------------------------------------ | +|-----|-------------------------------|--------------------------------------------------------------------------------| | L1 | créer un second profil | profil ajouté via types Config et sauvegardable | | L2 | modifier le profil existant | mutation persistée via `save_logging_document()` | | L3 | profil mono-fichier | console optionnelle + exactement un sink fichier valide | diff --git a/docs/plans/011-V0_2_4_HTTP_BLOCKS_ECONOMICS_PLAN.md b/docs/plans/011-V0_2_4_HTTP_BLOCKS_ECONOMICS_PLAN.md index c6bdb16..b5e543c 100644 --- a/docs/plans/011-V0_2_4_HTTP_BLOCKS_ECONOMICS_PLAN.md +++ b/docs/plans/011-V0_2_4_HTTP_BLOCKS_ECONOMICS_PLAN.md @@ -1,5 +1,5 @@ - + # Plan `0.2.4` — HTTP Blocks + Economics + compliance HTTP finale @@ -225,7 +225,7 @@ Les SIMDs `Review` liés au block-revenue sharing, notamment SIMD-0123, peuvent ## Matrice Blocks — 10 wrappers | Méthode | Requête à couvrir | Résultat à préserver | Contraintes / décisions `KSP-TRANSPORT-007` | -| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +|-------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `getBlock` | `slot`; second paramètre absent, bare encoding legacy, ou config `{commitment, encoding, transactionDetails, maxSupportedTransactionVersion, rewards}` | `object ou null`; `previousBlockhash`, `blockhash`, `parentSlot`, transactions/signatures/rewards selon config, `numRewardPartitions`, `blockTime`, `blockHeight` | commitment runtime au moins `confirmed`; réutiliser les encodings/version/transaction wire de `0.2.3`; préserver omission vs null; `numRewardPartitions` suit SIMD-0118; les rewards préservent `commissionBps` de SIMD-0291; garder les erreurs runtime de bloc/version comme erreurs RPC, sans les masquer | | `getBlockCommitment` | `slot` uniquement | `{commitment: array ou null, totalStake: u64}` | aucune config à inventer; préserver commitment nullable | | `getBlockHeight` | config contextuelle optionnelle `{commitment, minContextSlot}` | `u64` | réutiliser `SolanaContextConfig`; `minContextSlot` est transmis tel quel au runtime | @@ -269,13 +269,13 @@ Les champs top-level dont la présence dépend de la config (`transactions`, `si ## Matrice Economics — 5 wrappers -| Méthode | Requête à couvrir | Résultat à préserver | Contraintes / décisions `KSP-TRANSPORT-007` | -| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `getInflationGovernor` | config commitment optionnelle | `{initial,terminal,taper,foundation,foundationTerm}` en `f64` | réutiliser `SolanaCommitmentConfig`; aucun contexte de réponse | -| `getInflationRate` | aucun paramètre | `{total,validator,foundation,epoch}` | requête exacte `params: []` | -| `getInflationReward` | liste ordonnée d'adresses; config optionnelle `{epoch,commitment,minContextSlot}` | `Vec>` positionnel | commitment >= `confirmed`; ordre/cardinalité identiques aux entrées; préserver `commission: u8 ou null` et `commissionBps: u16` optionnel/omis de Agave `v4.2.1` conformément à SIMD-0291; aucune limite fixe d'adresses inventée | -| `getStakeMinimumDelegation` | config contextuelle optionnelle `{commitment,minContextSlot}` | `SolanaRpcResponse` | réutiliser `SolanaContextConfig` / `SolanaRpcResponse`; restituer la valeur runtime en lamports sans minimum codé en dur; surveiller SIMD-0490 | -| `getSupply` | config optionnelle `{commitment,excludeNonCirculatingAccountsList}` | `SolanaRpcResponse<{total,circulating,nonCirculating,nonCirculatingAccounts}>` | le booléen runtime par défaut est `false`; préserver la liste ordonnée retournée lorsqu'elle est demandée | +| Méthode | Requête à couvrir | Résultat à préserver | Contraintes / décisions `KSP-TRANSPORT-007` | +|-----------------------------|-----------------------------------------------------------------------------------|--------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `getInflationGovernor` | config commitment optionnelle | `{initial,terminal,taper,foundation,foundationTerm}` en `f64` | réutiliser `SolanaCommitmentConfig`; aucun contexte de réponse | +| `getInflationRate` | aucun paramètre | `{total,validator,foundation,epoch}` | requête exacte `params: []` | +| `getInflationReward` | liste ordonnée d'adresses; config optionnelle `{epoch,commitment,minContextSlot}` | `Vec>` positionnel | commitment >= `confirmed`; ordre/cardinalité identiques aux entrées; préserver `commission: u8 ou null` et `commissionBps: u16` optionnel/omis de Agave `v4.2.1` conformément à SIMD-0291; aucune limite fixe d'adresses inventée | +| `getStakeMinimumDelegation` | config contextuelle optionnelle `{commitment,minContextSlot}` | `SolanaRpcResponse` | réutiliser `SolanaContextConfig` / `SolanaRpcResponse`; restituer la valeur runtime en lamports sans minimum codé en dur; surveiller SIMD-0490 | +| `getSupply` | config optionnelle `{commitment,excludeNonCirculatingAccountsList}` | `SolanaRpcResponse<{total,circulating,nonCirculating,nonCirculatingAccounts}>` | le booléen runtime par défaut est `false`; préserver la liste ordonnée retournée lorsqu'elle est demandée | ### Extension runtime `commissionBps` diff --git a/docs/plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md b/docs/plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md index 73edfef..478c555 100644 --- a/docs/plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md +++ b/docs/plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md @@ -1,5 +1,5 @@ - + # Plan `0.2.5` — Wallet foundation @@ -205,23 +205,23 @@ Les permissions/ACL et le contrôle de qui peut écrire le chemin relèvent du s ### 5.7 Matrice des garanties -| Garantie | Cryptographie du format | Types/capabilities KSP | +| Garantie | Cryptographie du format | Types/capabilities KSP | |------------------------------------------------------------------------|-------------------------------------------------------:|-------------------------:| -| confidentialité metadata verrouillées | oui | oui | -| confidentialité secret Solana face à VIEW | oui | oui | -| indépendance VIEW/OWNER | oui | oui | -| VIEW ne signe pas | séparation de clés | oui | -| VIEW ne modifie pas Pubkey/alias/notes via API | authentification OWNER des metadata | oui | -| VIEW change son propre password | slot VIEW rewrappable sous la même capability metadata | oui, opération dédiée | -| VIEW ne change pas password OWNER / activation VIEW / autres key slots | authentification OWNER de l'état de contrôle | oui | -| metadata modifiées par VIEW détectées sous la même autorité OWNER | oui, `state_signature` | oui | -| détection corruption/tampering partiel | oui | parsing strict | -| signature Solana sans OWNER | impossible sous les primitives retenues | API absente hors OWNER | -| export secret sans OWNER | secret non déverrouillable | API absente hors OWNER | -| mutation Pubkey/alias/notes/OWNER-state sans OWNER | état non authentifiable | API absente hors OWNER | -| no-clobber / atomic replace | non | propriété de persistence | -| détection remplacement total par un autre wallet valide | hors garantie V1 | hors garantie V1 | -| détection rollback total vers une copie valide | hors garantie V1 | hors garantie V1 | +| confidentialité metadata verrouillées | oui | oui | +| confidentialité secret Solana face à VIEW | oui | oui | +| indépendance VIEW/OWNER | oui | oui | +| VIEW ne signe pas | séparation de clés | oui | +| VIEW ne modifie pas Pubkey/alias/notes via API | authentification OWNER des metadata | oui | +| VIEW change son propre password | slot VIEW rewrappable sous la même capability metadata | oui, opération dédiée | +| VIEW ne change pas password OWNER / activation VIEW / autres key slots | authentification OWNER de l'état de contrôle | oui | +| metadata modifiées par VIEW détectées sous la même autorité OWNER | oui, `state_signature` | oui | +| détection corruption/tampering partiel | oui | parsing strict | +| signature Solana sans OWNER | impossible sous les primitives retenues | API absente hors OWNER | +| export secret sans OWNER | secret non déverrouillable | API absente hors OWNER | +| mutation Pubkey/alias/notes/OWNER-state sans OWNER | état non authentifiable | API absente hors OWNER | +| no-clobber / atomic replace | non | propriété de persistence | +| détection remplacement total par un autre wallet valide | hors garantie V1 | hors garantie V1 | +| détection rollback total vers une copie valide | hors garantie V1 | hors garantie V1 | Les ACL/permissions OS ne figurent volontairement pas dans cette matrice : elles ne participent pas au modèle de sécurité de `.kspwallet` V1. @@ -388,9 +388,9 @@ Audit source notable : `solana-keypair 3.1.2` contient un bloc `unsafe` interne | Primitive | Version publiée auditée | Décision V1 | |-----------|------------------------:|--------------------------------------------| -| Argon2 | `0.5.3` | **retenu : Argon2id v19** | -| scrypt | `0.12.0` | alternative maintenue, non ajoutée | -| PBKDF2 | `0.13.0` | compatibilité/legacy seulement, non ajouté | +| Argon2 | `0.5.3` | **retenu : Argon2id v19** | +| scrypt | `0.12.0` | alternative maintenue, non ajoutée | +| PBKDF2 | `0.13.0` | compatibilité/legacy seulement, non ajouté | Les paramètres Argon2 de création ont été mesurés avec le benchmark opérateur de `pre.004` : `64 MiB / 3 / 1 = 1742 ms`, `128 MiB / 3 / 1 = 3459 ms`, `256 MiB / 3 / 1 = 6925 ms` sur la machine/profil testés le 2026-08-19. `pre.005` retient donc **64 MiB / 3 passes / 1 lane** comme profil initial de création KSP, avec un salt CSPRNG indépendant de 32 octets par slot. Ce choix n'est copié ni de bot3, ni d'un RFC, ni d'un default de crate. Le fichier sérialise tous les paramètres nécessaires afin qu'un ancien wallet conserve son profil historique même lorsque les defaults KSP seront durcis. @@ -398,10 +398,10 @@ Le parseur impose des **bornes maximales** avant de lancer le KDF, afin qu'un fi ### 8.3 AEAD -| Primitive | Version publiée auditée | Décision V1 | +| Primitive | Version publiée auditée | Décision V1 | |--------------------|--------------------------:|------------------| | XChaCha20-Poly1305 | `chacha20poly1305 0.11.0` | **retenu** | -| AES-256-GCM-SIV | `aes-gcm-siv 0.12.0` | non retenu en V1 | +| AES-256-GCM-SIV | `aes-gcm-siv 0.12.0` | non retenu en V1 | XChaCha20-Poly1305 fournit une clé 256 bits et un nonce étendu 192 bits. Un nonce neuf est généré pour chaque wrapping/chiffrement. La crate RustCrypto documente un audit NCC Group sans constat significatif. @@ -1107,20 +1107,20 @@ liens Markdown locaux audités | Domaine | Taille | Risque principal | |-------------------------------|-------:|----------------------------------------------| -| crate/API foundation | M | capability surface durable | -| threat model | M | faux niveau de garantie | -| format wire | L | strict parsing/versioning | -| interop/test vectors | L | transcript exact multi-langages | -| KDF/AEAD/key wrapping | L | paramètres + nonce/AAD | -| VIEW/OWNER key slots | XL | indépendance et rotations | -| auth crypto metadata niveau B | L/XL | clé admin + transcript + substitution limits | -| persistence | L | no-clobber + crash semantics multi-OS | -| signing | M | aucun secret getter | -| password/key-slot rotation | L | rotation vs vraie révocation | -| alias/notes | M | bornes + persistence | -| import/export | M/L | extension sans secret API générale | -| security/adversarial tests | XL | tamper/fault/diagnostics | -| spec/README/USAGE | L | contrat externe autonome | +| crate/API foundation | M | capability surface durable | +| threat model | M | faux niveau de garantie | +| format wire | L | strict parsing/versioning | +| interop/test vectors | L | transcript exact multi-langages | +| KDF/AEAD/key wrapping | L | paramètres + nonce/AAD | +| VIEW/OWNER key slots | XL | indépendance et rotations | +| auth crypto metadata niveau B | L/XL | clé admin + transcript + substitution limits | +| persistence | L | no-clobber + crash semantics multi-OS | +| signing | M | aucun secret getter | +| password/key-slot rotation | L | rotation vs vraie révocation | +| alias/notes | M | bornes + persistence | +| import/export | M/L | extension sans secret API générale | +| security/adversarial tests | XL | tamper/fault/diagnostics | +| spec/README/USAGE | L | contrat externe autonome | Conclusion : **pas de rescoping fonctionnel**, mais split supplémentaire avant crypto lourde. diff --git a/docs/plans/014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md b/docs/plans/014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md index 2094de6..ac4cb5d 100644 --- a/docs/plans/014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md +++ b/docs/plans/014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md @@ -1,5 +1,5 @@ - + # Plan `0.2.7` — WebSocket Solana standard @@ -361,10 +361,10 @@ Audit au 2026-08-22 : | Candidate | Version auditée | Verdict | Motif | |---------------------|----------------:|---------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| -| `tokio-tungstenite` | `0.30.0` | **retenue** | mature, Tokio-native, TLS rustls, continuité avec bot3 mais réauditée, contrôle de `WebSocketConfig`, client + serveur local de test | -| `futures-util` | `0.3.34` | **retenue comme utilitaire** | `StreamExt`/`SinkExt`; features minimales `std,sink` | -| `tokio-websockets` | `0.13.3` | alternative viable, non retenue | strict/minimal et performant, mais exige davantage d'assemblage/features et n'apporte pas de besoin fonctionnel supérieur démontré pour cette foundation | -| `fastwebsockets` | `0.10.0` | non retenue | plus bas niveau ; peut déléguer davantage de compliance au caller, inutile pour la première foundation KSP | +| `tokio-tungstenite` | `0.30.0` | **retenue** | mature, Tokio-native, TLS rustls, continuité avec bot3 mais réauditée, contrôle de `WebSocketConfig`, client + serveur local de test | +| `futures-util` | `0.3.34` | **retenue comme utilitaire** | `StreamExt`/`SinkExt`; features minimales `std,sink` | +| `tokio-websockets` | `0.13.3` | alternative viable, non retenue | strict/minimal et performant, mais exige davantage d'assemblage/features et n'apporte pas de besoin fonctionnel supérieur démontré pour cette foundation | +| `fastwebsockets` | `0.10.0` | non retenue | plus bas niveau ; peut déléguer davantage de compliance au caller, inutile pour la première foundation KSP | Landing matérialisé par **`pre.004`** : diff --git a/docs/plans/026-V0_3_5_INTERFACE_ACQUISITION_EVENTS_PLAN.md b/docs/plans/026-V0_3_5_INTERFACE_ACQUISITION_EVENTS_PLAN.md index 3cfa66e..86ccc42 100644 --- a/docs/plans/026-V0_3_5_INTERFACE_ACQUISITION_EVENTS_PLAN.md +++ b/docs/plans/026-V0_3_5_INTERFACE_ACQUISITION_EVENTS_PLAN.md @@ -1,5 +1,5 @@ - + # Plan `0.3.5` — Interface passive acquisition events @@ -224,7 +224,7 @@ Une égalité de noms ne suffit pas. Une proximité de commitment ne suffit pas. ## 6. Inventaire producer -> nature de fait | Producer / surface | Nature du fait | Champs structurants obligatoires | Optionalité utile | Ordre / lifecycle | Timestamp semantics | Error semantics | Metadata session/provider | Volume potentiel | Persistent/replayable ? | -| -------------------------------------- | ----------------------------------- | ----------------------------------------------------- | --------------------------------- | ------------------------------------------------- | ----------------------------------------- | -------------------------------------- | -------------------------------------- | --------------------- | --------------------------------------- | +|----------------------------------------|-------------------------------------|-------------------------------------------------------|-----------------------------------|---------------------------------------------------|-------------------------------------------|----------------------------------------|----------------------------------------|-----------------------|-----------------------------------------| | Solana WS `slotSubscribe` | progression de slot | slot, parent, root | aucune | event répété à chaque slot traité | aucun | aucune | subscription id hors DTO métier | minime | non | | Solana WS `rootSubscribe` | progression de root | root slot | aucune | event répété lors du changement de root | aucun | aucune | subscription id | minime | non | | Solana WS `slotsUpdatesSubscribe` | lifecycle interne slot | slot, type, timestamp | parent/stats/error selon variante | multi-event par slot ; méthode instable | Unix update timestamp en ms | texte seulement pour `dead` | subscription id | faible à modéré | non | @@ -248,7 +248,7 @@ Une égalité de noms ne suffit pas. Une proximité de commitment ne suffit pas. ### 6.1 Consumers actuels et imminents | Consumer | Besoin actuel / prévu | Dépendance à Interface event | Décision `pre.001` | -| ----------------------------------------- | --------------------------------------------------------- | ---------------------------- | ------------------------------------------------------------------------- | +|-------------------------------------------|-----------------------------------------------------------|------------------------------|---------------------------------------------------------------------------| | `ksp-program-api` | ProgramInstruction / ProgramAccountMeta | non | préserver la surface Program existante ; aucun re-export acquisition | | `0.3.6` premier backfill RAW | range historique -> Transport -> RAW ingestion -> Store | non | ne pas forcer SlotLifecycle dans le backfill | | futur worker RAW live | subscriptions/fetch live multi-transport -> RAW ingestion | oui, candidat concret | consumer proche justifiant une projection slot lifecycle provider-neutral | @@ -257,24 +257,24 @@ Une égalité de noms ne suffit pas. Une proximité de commitment ne suffit pas. ## 7. Matrice de convergence sémantique -| Nature candidate | Sources comparées | Intersection exacte retenue | Différences non représentables sans perte | Projection commune lossless pour le fait ciblé ? | Consumer concret | Owner | Décision | -| ----------------------------- | --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------ | ----------------------------- | ------------------------------ | ------------------- | -| lifecycle slot | slotSubscribe + slotsUpdates + rootSubscribe + Yellowstone Slot | Processed, FirstShredReceived, Completed, CreatedBank, Dead, OptimisticallyConfirmed, Rooted | Frozen ; parent/timestamps/diagnostics ; noms wire Confirmed/Finalized côté Yellowstone | oui, pour l'occurrence `slot + stage` | futur worker RAW live | Interface | ADMIS | -| transaction execution | logsSubscribe + Yellowstone TransactionStatus + Helius transactionSubscribe | `slot + TransactionSignature + Succeeded/Failed` | metadata/provider/detail modes ; commitment/delivery ; erreur source | oui pour le fait d'exécution observée | futur worker RAW live | Interface | ADMIS `pre.003` | -| logs realtime | logsSubscribe + transaction/meta Yellowstone/Helius | logs associés à une transaction | volume/bornes, richesse de source, disponibilité selon détail | non admis dans cette release sans consumer/bornes | hydratation possible | Interface si futur gate | IDÉE DIFFÉRÉE | -| signature commitment/snapshot | getSignatureStatuses + signatureSubscribe | identité de signature seulement | snapshot HTTP vs transition one-shot commitment | non comme event d'exécution | aucun contrat unique démontré | Transport | REPORTÉ | -| vote | voteSubscribe + Yellowstone transaction `is_vote` | qualificatif « vote » seulement | gossip pré-consensus vs transaction exécutée | non | aucun besoin commun démontré | Transport | REPORTÉ | -| account/program | WS account/program + Yellowstone Account | état account complet possible | persistance/replay déjà possédés par RawAccountState/Observation | non comme event distinct | RAW ingestion | Store/Transport | REJET Interface | -| transaction complète | HTTP/WS/Helius/Yellowstone transaction | transaction complète selon source | format/commitment/provider metadata ; RawTransaction déjà canonique Store | non comme second type | RAW ingestion | Store/Transport | REJET Interface | -| block | getBlock/blockSubscribe/Yellowstone Block | slot + block container | payloads/options/reconstruction ; pas de consumer passif minimal prouvé | non | extraction RAW transaction | Transport | REJET Interface | -| Yellowstone Entry | Yellowstone aujourd'hui | aucune multi-source démontrée | mono-producer et protocole-specific aujourd'hui | non aujourd'hui ; futur fait transversal -> Interface | aucun actuel | Transport DTO | REPORTÉ | -| persisted-data available | Store after commit | référence durable compacte conceptuelle | event réseau sans relation ; owner déjà normé par KSP-NOTIFY | oui mais hors owner Interface | processors futurs | Store API | REJET Interface | -| Helius Enhanced transaction | Helius transactionSubscribe | peut produire `TransactionExecutionEvent` lorsque signature/slot/outcome sont déterminables | filtres/options/provider behavior et DTO riche | DTO non ; projection sémantique admise via converter | futur worker RAW live | Transport DTO / Interface fact | PRODUCTEUR ADMIS | +| Nature candidate | Sources comparées | Intersection exacte retenue | Différences non représentables sans perte | Projection commune lossless pour le fait ciblé ? | Consumer concret | Owner | Décision | +|-------------------------------|-----------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------|-------------------------------------------------------|-------------------------------|--------------------------------|------------------| +| lifecycle slot | slotSubscribe + slotsUpdates + rootSubscribe + Yellowstone Slot | Processed, FirstShredReceived, Completed, CreatedBank, Dead, OptimisticallyConfirmed, Rooted | Frozen ; parent/timestamps/diagnostics ; noms wire Confirmed/Finalized côté Yellowstone | oui, pour l'occurrence `slot + stage` | futur worker RAW live | Interface | ADMIS | +| transaction execution | logsSubscribe + Yellowstone TransactionStatus + Helius transactionSubscribe | `slot + TransactionSignature + Succeeded/Failed` | metadata/provider/detail modes ; commitment/delivery ; erreur source | oui pour le fait d'exécution observée | futur worker RAW live | Interface | ADMIS `pre.003` | +| logs realtime | logsSubscribe + transaction/meta Yellowstone/Helius | logs associés à une transaction | volume/bornes, richesse de source, disponibilité selon détail | non admis dans cette release sans consumer/bornes | hydratation possible | Interface si futur gate | IDÉE DIFFÉRÉE | +| signature commitment/snapshot | getSignatureStatuses + signatureSubscribe | identité de signature seulement | snapshot HTTP vs transition one-shot commitment | non comme event d'exécution | aucun contrat unique démontré | Transport | REPORTÉ | +| vote | voteSubscribe + Yellowstone transaction `is_vote` | qualificatif « vote » seulement | gossip pré-consensus vs transaction exécutée | non | aucun besoin commun démontré | Transport | REPORTÉ | +| account/program | WS account/program + Yellowstone Account | état account complet possible | persistance/replay déjà possédés par RawAccountState/Observation | non comme event distinct | RAW ingestion | Store/Transport | REJET Interface | +| transaction complète | HTTP/WS/Helius/Yellowstone transaction | transaction complète selon source | format/commitment/provider metadata ; RawTransaction déjà canonique Store | non comme second type | RAW ingestion | Store/Transport | REJET Interface | +| block | getBlock/blockSubscribe/Yellowstone Block | slot + block container | payloads/options/reconstruction ; pas de consumer passif minimal prouvé | non | extraction RAW transaction | Transport | REJET Interface | +| Yellowstone Entry | Yellowstone aujourd'hui | aucune multi-source démontrée | mono-producer et protocole-specific aujourd'hui | non aujourd'hui ; futur fait transversal -> Interface | aucun actuel | Transport DTO | REPORTÉ | +| persisted-data available | Store after commit | référence durable compacte conceptuelle | event réseau sans relation ; owner déjà normé par KSP-NOTIFY | oui mais hors owner Interface | processors futurs | Store API | REJET Interface | +| Helius Enhanced transaction | Helius transactionSubscribe | peut produire `TransactionExecutionEvent` lorsque signature/slot/outcome sont déterminables | filtres/options/provider behavior et DTO riche | DTO non ; projection sémantique admise via converter | futur worker RAW live | Transport DTO / Interface fact | PRODUCTEUR ADMIS | ### 7.1 Lifecycle de slot — ADMIS sous intersection stricte | Fait commun | Solana standard | Yellowstone | Stage KSP | Décision Interface | Metadata volontairement non commune | -| ------------------------ | --------------------------------------- | -------------------- | ------------------------- | ------------------ | ------------------------------------------------------------------------------ | +|--------------------------|-----------------------------------------|----------------------|---------------------------|--------------------|--------------------------------------------------------------------------------| | slot traité | `slotSubscribe` | `Processed` | `Processed` | ADMIS | parent/root courant côté WS ; filters/created_at/parent côté Yellowstone | | premier shred reçu | `FirstShredReceived` | `FirstShredReceived` | `FirstShredReceived` | ADMIS | timestamp WS ; created_at/filters côté Yellowstone | | ingestion slot complétée | `Completed` | `Completed` | `Completed` | ADMIS | timestamp WS ; created_at/filters côté Yellowstone | @@ -473,19 +473,19 @@ Ces APIs ne deviennent pas un prérequis de `0.3.6` : le premier backfill histor ## 10. Anti-duplication Store -| Contrat Store comparé | Chevauchement Interface admis | Pourquoi il n'y a pas de seconde vérité | -| ------------------------------ | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------ | -| `RawTransaction` | slot + signature possibles via execution event | aucun payload/format/hash/block_time ; l'event ne peut reconstruire ni remplacer une transaction RAW | -| `RawTransactionReference` | signature sémantiquement apparentée | la référence Store inclut `RawNetworkId` et possède l'identité durable ; Interface reste contextuel | -| `RawTransactionSignature` | même primitive protocolaire 64 octets | wrapper Store et primitive Interface restent owner-specific ; conversion explicite à la composition | -| `RawTransactionObservation` | référence transaction possible indirectement | aucune provenance acquisition, observation key ou timestamp durable | -| `RawAccountState` | slot possible via lifecycle seulement | aucune pubkey/state hash/lamports/owner/data ; aucun état account | -| `RawAccountObservation` | slot indirect possible | aucune référence account/provenance Geyser | -| `RawAcquisitionProvenance` | aucun | pas d'origin/provider code/timestamps ; les metadata source restent Transport/composition | -| `RawTimestamp` | aucun | aucun timestamp commun inventé | -| `RawContentHash` | aucun | aucune identité de contenu durable | -| `RawObservationKey` | aucun | aucune identité d'observation persistante | -| notification after commit | aucun | event réseau observe l'acquisition ; wake-up Store référence une donnée déjà commitée | +| Contrat Store comparé | Chevauchement Interface admis | Pourquoi il n'y a pas de seconde vérité | +|-----------------------------|------------------------------------------------|------------------------------------------------------------------------------------------------------| +| `RawTransaction` | slot + signature possibles via execution event | aucun payload/format/hash/block_time ; l'event ne peut reconstruire ni remplacer une transaction RAW | +| `RawTransactionReference` | signature sémantiquement apparentée | la référence Store inclut `RawNetworkId` et possède l'identité durable ; Interface reste contextuel | +| `RawTransactionSignature` | même primitive protocolaire 64 octets | wrapper Store et primitive Interface restent owner-specific ; conversion explicite à la composition | +| `RawTransactionObservation` | référence transaction possible indirectement | aucune provenance acquisition, observation key ou timestamp durable | +| `RawAccountState` | slot possible via lifecycle seulement | aucune pubkey/state hash/lamports/owner/data ; aucun état account | +| `RawAccountObservation` | slot indirect possible | aucune référence account/provenance Geyser | +| `RawAcquisitionProvenance` | aucun | pas d'origin/provider code/timestamps ; les metadata source restent Transport/composition | +| `RawTimestamp` | aucun | aucun timestamp commun inventé | +| `RawContentHash` | aucun | aucune identité de contenu durable | +| `RawObservationKey` | aucun | aucune identité d'observation persistante | +| notification after commit | aucun | event réseau observe l'acquisition ; wake-up Store référence une donnée déjà commitée | Le gate conserve les canaris conceptuels suivants : @@ -527,7 +527,7 @@ future acquisition composition / RAW live worker ## 12. Threat model | Risque | Menace | Garde planifiée | -| ------------------------------ | -------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | +|--------------------------------|----------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------| | mega enum | Interface devient catalogue de tous les transports | familles admises séparées et étroites ; aucun `Event` générique | | Option soup | sémantiques différentes fusionnées par champs optionnels | familles séparées : SlotLifecycle / TransactionExecution ; logs/commitment restent distincts | | persistence creep | event-only devient second Store | aucun RAW/payload/cursor/retention ; `KSP-NOTIFY-*` reste Store API | @@ -538,7 +538,7 @@ future acquisition composition / RAW live worker | dead diagnostic leak | texte provider/validator traverse une API partagée durable | stage `Dead` sans diagnostic ; détail reste Transport | | cross-network confusion | event sans réseau mélangé entre sessions | conversion autorisée seulement dans une composition déjà liée à un contexte réseau ; pas de network id dupliqué | | signature/payload leak | signature ou payload brut fuit via Debug | signature fixe 64 octets, Debug redacted ; aucun payload variable | -| public enum breakage | ajout futur casse les matches externes | `SlotLifecycleStage` et `TransactionExecutionOutcome` non-exhaustive | +| public enum breakage | ajout futur casse les matches externes | `SlotLifecycleStage` et `TransactionExecutionOutcome` non-exhaustive | | logging/runtime creep | crate passive acquiert tracing/Tokio/channel | aucune dependency/runtime/logging ajoutée | | Helius semantic promotion | DTO/provider extension devient norme KSP | `transactionSubscribe` reste Transport ; seule une projection sémantique commune peut devenir Interface | | Yellowstone protocol promotion | DTO `Entry`/filters/created_at deviennent Interface | types Yellowstone restent Transport ; un futur fait transversal est réaudité séparément | diff --git a/docs/plans/027-V0_3_6_JOB_API_BACKFILL_PLAN.md b/docs/plans/027-V0_3_6_JOB_API_BACKFILL_PLAN.md index 1717e7b..b274733 100644 --- a/docs/plans/027-V0_3_6_JOB_API_BACKFILL_PLAN.md +++ b/docs/plans/027-V0_3_6_JOB_API_BACKFILL_PLAN.md @@ -1,5 +1,5 @@ - + # Plan v0.3.6 — Job API et premier backfill RAW @@ -62,36 +62,36 @@ Hors périmètre : Le tableau décrit des comportements observés ; il ne constitue pas une filiation de code. -| Fonction historique | Preuve fichier, test ou run | Sémantique réelle | Statut KSP | Owner KSP cible | Risque ou différence actuelle | Préversion cible | -| --- | --- | --- | --- | --- | --- | --- | -| Sources latest, before et after | `ks-pipeline/src/backfill.rs`, `collect_before_candidates`, `collect_after_candidates` | Pagination d'adresse, ancre exclusive et fenêtre plus récente proche | REPRENDRE | Backfill avec wrappers Transport | L'ordre after doit être figé et l'ancre non atteinte observable | `pre.005`, `pre.008` | -| Signatures explicites | `explicit_candidates_are_deduplicated_in_input_order` | Première occurrence conservée et ordre d'entrée stable | REPRENDRE | Backfill | Validation Base58 et limite absentes du type KSP actuel | `pre.005` | -| Déduplication inter-pages | `nearest_newer_candidates_deduplicate_page_entries` | Un candidat identique n'est admis qu'une fois | REPRENDRE | Backfill | Doit couvrir toutes les directions, pas seulement after | `pre.005` | -| Fenêtre after la plus proche | `nearest_newer_candidates_keep_only_entries_closest_to_anchor` | Retient les N signatures plus récentes les plus proches de l'ancre | REPRENDRE | Backfill | Nécessite une preuve officielle et un ordre KSP déterministe | `pre.005` | -| Frontière contiguë | `completion_frontier_advances_only_across_contiguous_results` | Une fin hors ordre ne saute pas un candidat incomplet | REPRENDRE | Backfill | Missing et erreurs ne doivent pas être marqués durables | `pre.008` | -| Reprise before | `cancelled_before_resume_uses_last_contiguous_candidate`, `cancelled_before_resume_keeps_anchor_when_nothing_completed` | Curseur repris au dernier préfixe contigu ou à l'ancre initiale | REPRENDRE | Backfill et hôte pour garde durable | Le checkpoint doit être lié au JobId et au scope | `pre.008` | -| Reprise latest | `cancelled_latest_scan_without_completed_candidate_restarts_from_latest` | Une vue latest vide de complétion repart sans curseur | REPRENDRE | Backfill | Le replay doit dépendre de l'idempotence Store | `pre.008` | -| Annulation d'attente et RPC long | `cancellable_retry_wait_returns_immediately_when_already_cancelled`, `long_running_rpc_future_is_cancelled_cooperatively` | Une sélection coopérative abandonne le futur non durable | REPRENDRE | Job API pour token, Backfill pour runtime | Un commit Store soumis ne peut pas être abandonné aveuglément | `pre.002`, `pre.009` | -| Taxonomie Program, Token ou Pool | `BackfillAddressKind`, `filter_code_distinguishes_target_and_direction` | Étiquette UI intégrée au filtre historique | REDESSINER | Application v0.3.7 | Une adresse Core ne porte pas ce domaine de présentation | v0.3.7 | -| Callback `BackfillObserver` | `ks-pipeline/src/backfill.rs`, `DemoBackfillObserver` dans l'application | Le producteur appelle directement un consumer et émet un message | REDESSINER | Job API et snapshot concret Backfill | Blocage, panic consumer et chaînes humaines possibles | `pre.003`, `pre.009` | -| Compteurs `existing_skipped`, `canonical_skipped`, `missing`, `failed` | `BackfillSummary` et `summary_payload` | Plusieurs résultats Store ou RPC sont agrégés avec ambiguïtés | REDESSINER | Backfill snapshot | KSP doit distinguer entité, observation, purge, missing et conflit | `pre.007`, `pre.009` | -| Prélecture Store avant hydratation | `hydrate_candidate`, branche `existing_skipped` | Une signature connue saute l'acquisition et l'observation | REJETER | Aucun | Course TOCTOU, purge et acquisition nouvelle perdues | Aucun | -| Retry, pause et parsing texte | `retry_delay_ms`, `wait_with_cancellation`, branches HTTP 429 et timeout | Le Job rythme et retente selon des chaînes d'erreur | REJETER | Transport exclusivement | Amplification des retries et contrat d'erreur instable | Aucun | -| Persistance missing ou failed | `persist_missing_observation`, `persist_failed_observation` | Une tentative sans transaction canonique devient observation durable | REPORTER | Futur contrat Store de journal d'acquisition | `RawTransactionObservation` KSP exige aujourd'hui une transaction complète | Version ultérieure | -| Reprise durable multi-processus | Résumé `resume_before_signature` et application demo | Le caller reçoit une ancre, sans contrat de garde durable KSP | REPORTER | Application ou scheduler futur | Aucune propriété de persistance de checkpoint n'existe en v0.3.6 | Version ultérieure | +| Fonction historique | Preuve fichier, test ou run | Sémantique réelle | Statut KSP | Owner KSP cible | Risque ou différence actuelle | Préversion cible | +|------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------|------------|----------------------------------------------|----------------------------------------------------------------------------|----------------------| +| Sources latest, before et after | `ks-pipeline/src/backfill.rs`, `collect_before_candidates`, `collect_after_candidates` | Pagination d'adresse, ancre exclusive et fenêtre plus récente proche | REPRENDRE | Backfill avec wrappers Transport | L'ordre after doit être figé et l'ancre non atteinte observable | `pre.005`, `pre.008` | +| Signatures explicites | `explicit_candidates_are_deduplicated_in_input_order` | Première occurrence conservée et ordre d'entrée stable | REPRENDRE | Backfill | Validation Base58 et limite absentes du type KSP actuel | `pre.005` | +| Déduplication inter-pages | `nearest_newer_candidates_deduplicate_page_entries` | Un candidat identique n'est admis qu'une fois | REPRENDRE | Backfill | Doit couvrir toutes les directions, pas seulement after | `pre.005` | +| Fenêtre after la plus proche | `nearest_newer_candidates_keep_only_entries_closest_to_anchor` | Retient les N signatures plus récentes les plus proches de l'ancre | REPRENDRE | Backfill | Nécessite une preuve officielle et un ordre KSP déterministe | `pre.005` | +| Frontière contiguë | `completion_frontier_advances_only_across_contiguous_results` | Une fin hors ordre ne saute pas un candidat incomplet | REPRENDRE | Backfill | Missing et erreurs ne doivent pas être marqués durables | `pre.008` | +| Reprise before | `cancelled_before_resume_uses_last_contiguous_candidate`, `cancelled_before_resume_keeps_anchor_when_nothing_completed` | Curseur repris au dernier préfixe contigu ou à l'ancre initiale | REPRENDRE | Backfill et hôte pour garde durable | Le checkpoint doit être lié au JobId et au scope | `pre.008` | +| Reprise latest | `cancelled_latest_scan_without_completed_candidate_restarts_from_latest` | Une vue latest vide de complétion repart sans curseur | REPRENDRE | Backfill | Le replay doit dépendre de l'idempotence Store | `pre.008` | +| Annulation d'attente et RPC long | `cancellable_retry_wait_returns_immediately_when_already_cancelled`, `long_running_rpc_future_is_cancelled_cooperatively` | Une sélection coopérative abandonne le futur non durable | REPRENDRE | Job API pour token, Backfill pour runtime | Un commit Store soumis ne peut pas être abandonné aveuglément | `pre.002`, `pre.009` | +| Taxonomie Program, Token ou Pool | `BackfillAddressKind`, `filter_code_distinguishes_target_and_direction` | Étiquette UI intégrée au filtre historique | REDESSINER | Application v0.3.7 | Une adresse Core ne porte pas ce domaine de présentation | v0.3.7 | +| Callback `BackfillObserver` | `ks-pipeline/src/backfill.rs`, `DemoBackfillObserver` dans l'application | Le producteur appelle directement un consumer et émet un message | REDESSINER | Job API et snapshot concret Backfill | Blocage, panic consumer et chaînes humaines possibles | `pre.003`, `pre.009` | +| Compteurs `existing_skipped`, `canonical_skipped`, `missing`, `failed` | `BackfillSummary` et `summary_payload` | Plusieurs résultats Store ou RPC sont agrégés avec ambiguïtés | REDESSINER | Backfill snapshot | KSP doit distinguer entité, observation, purge, missing et conflit | `pre.007`, `pre.009` | +| Prélecture Store avant hydratation | `hydrate_candidate`, branche `existing_skipped` | Une signature connue saute l'acquisition et l'observation | REJETER | Aucun | Course TOCTOU, purge et acquisition nouvelle perdues | Aucun | +| Retry, pause et parsing texte | `retry_delay_ms`, `wait_with_cancellation`, branches HTTP 429 et timeout | Le Job rythme et retente selon des chaînes d'erreur | REJETER | Transport exclusivement | Amplification des retries et contrat d'erreur instable | Aucun | +| Persistance missing ou failed | `persist_missing_observation`, `persist_failed_observation` | Une tentative sans transaction canonique devient observation durable | REPORTER | Futur contrat Store de journal d'acquisition | `RawTransactionObservation` KSP exige aujourd'hui une transaction complète | Version ultérieure | +| Reprise durable multi-processus | Résumé `resume_before_signature` et application demo | Le caller reçoit une ancre, sans contrat de garde durable KSP | REPORTER | Application ou scheduler futur | Aucune propriété de persistance de checkpoint n'existe en v0.3.6 | Version ultérieure | ## 5. Ownership par couche -| Sujet | Propriétaire | Contrat | -| --- | --- | --- | -| Identité, état et annulation d'un Job | `ksp-job-api` | Types publics runtime-neutral et sans effet de bord | -| Snapshot concret du backfill | `ksp-job-backfill-lib` | Phase, progression, checkpoint et résultat sûrs | -| Pagination et hydratation Solana | `ksp-onchain-transport-lib` | Wrappers RPC typés existants | -| Routage, pacing, retry et délai | `ksp-onchain-transport-lib` | Aucune boucle concurrente dans Job | -| Conversion en RAW canonique | `ksp-job-backfill-lib` | Adaptateur concret du premier vertical | -| Idempotence, conflit et purge | `ksp-store-lib` | Écriture atomique en mode `Normal` | -| Logs internes | `ksp-logging-lib` | Champs sûrs, pas de secret ni réponse brute | -| Configuration et supervision | Application v0.3.7 | Pas de dépendance Config en v0.3.6 | +| Sujet | Propriétaire | Contrat | +|---------------------------------------|-----------------------------|-----------------------------------------------------| +| Identité, état et annulation d'un Job | `ksp-job-api` | Types publics runtime-neutral et sans effet de bord | +| Snapshot concret du backfill | `ksp-job-backfill-lib` | Phase, progression, checkpoint et résultat sûrs | +| Pagination et hydratation Solana | `ksp-onchain-transport-lib` | Wrappers RPC typés existants | +| Routage, pacing, retry et délai | `ksp-onchain-transport-lib` | Aucune boucle concurrente dans Job | +| Conversion en RAW canonique | `ksp-job-backfill-lib` | Adaptateur concret du premier vertical | +| Idempotence, conflit et purge | `ksp-store-lib` | Écriture atomique en mode `Normal` | +| Logs internes | `ksp-logging-lib` | Champs sûrs, pas de secret ni réponse brute | +| Configuration et supervision | Application v0.3.7 | Pas de dépendance Config en v0.3.6 | ## 6. Contrat public de `ksp-job-api` @@ -116,13 +116,13 @@ La surface concrète du backfill accepte `BackfillRequest`, `HttpTransportPool` Audit des formes candidates avant choix : -| Forme | Runtime-neutralité API | Backpressure | Multi-listener | Livraison terminale | Resynchronisation | Testabilité | Réutilisation Worker future | -| --- | --- | --- | --- | --- | --- | --- | --- | -| Polling snapshot par caller | Forte | Naturellement bornée | Oui | Oui si le caller repolle | Snapshot direct | Simple mais latence artificielle | Possible, coûteux si polling serré | -| Callback ou sink | Moyenne | Le producer peut bloquer | Composition manuelle | Fragile sur panic ou abandon | Aucune native | Courses et réentrance difficiles | Rejetée | -| Watch ou latest-value | Forte avec futur abstrait | O(1), coalescence explicite | Oui | Oui, valeur retenue | Snapshot complet courant | Déterministe avec séquences | Bon motif, contrat Worker distinct plus tard | -| Broadcast ou fan-out | Faible si primitive runtime publique | Lag ou pertes par consumer | Oui | Dépend du buffer et de l'abonnement | Besoin d'un snapshot parallèle | Sensible aux buffers | Ne pas imposer avant Worker API | -| File bornée | Moyenne | Blocage ou drop à définir | Non sans fan-out supplémentaire | Peut être coincée derrière la progression | Replay limité au buffer | Nombreuses politiques périphériques | Trop lourde pour le besoin actuel | +| Forme | Runtime-neutralité API | Backpressure | Multi-listener | Livraison terminale | Resynchronisation | Testabilité | Réutilisation Worker future | +|-----------------------------|--------------------------------------|-----------------------------|---------------------------------|-------------------------------------------|--------------------------------|-------------------------------------|----------------------------------------------| +| Polling snapshot par caller | Forte | Naturellement bornée | Oui | Oui si le caller repolle | Snapshot direct | Simple mais latence artificielle | Possible, coûteux si polling serré | +| Callback ou sink | Moyenne | Le producer peut bloquer | Composition manuelle | Fragile sur panic ou abandon | Aucune native | Courses et réentrance difficiles | Rejetée | +| Watch ou latest-value | Forte avec futur abstrait | O(1), coalescence explicite | Oui | Oui, valeur retenue | Snapshot complet courant | Déterministe avec séquences | Bon motif, contrat Worker distinct plus tard | +| Broadcast ou fan-out | Faible si primitive runtime publique | Lag ou pertes par consumer | Oui | Dépend du buffer et de l'abonnement | Besoin d'un snapshot parallèle | Sensible aux buffers | Ne pas imposer avant Worker API | +| File bornée | Moyenne | Blocage ou drop à définir | Non sans fan-out supplémentaire | Peut être coincée derrière la progression | Replay limité au buffer | Nombreuses politiques périphériques | Trop lourde pour le besoin actuel | Le choix est watch ou latest-value avec séquence et snapshot complet. Il conserve l'observabilité sans imposer la primitive d'un runtime à `ksp-job-api`. @@ -164,10 +164,10 @@ Le fingerprint de scope couvre réseau, adresse ou digest de liste, direction, a ## 9. Sémantique RPC officielle figée -| Appel | Configuration retenue | Sémantique consommée | -| --- | --- | --- | -| `getSignaturesForAddress` | engagement, `before`, `until`, `limit`, `minContextSlot` | Résultats du plus récent au plus ancien ; ancres exclusives | -| `getTransaction` | engagement, encodage `base64`, version maximale `0` | Transaction confirmée ou `null`, avec slot, block time, transaction, meta et version | +| Appel | Configuration retenue | Sémantique consommée | +|---------------------------|----------------------------------------------------------|--------------------------------------------------------------------------------------| +| `getSignaturesForAddress` | engagement, `before`, `until`, `limit`, `minContextSlot` | Résultats du plus récent au plus ancien ; ancres exclusives | +| `getTransaction` | engagement, encodage `base64`, version maximale `0` | Transaction confirmée ou `null`, avec slot, block time, transaction, meta et version | Références officielles : @@ -273,15 +273,15 @@ Le checkpoint contient `JobId`, fingerprint de scope, index de frontière et cur Annuler est idempotent et fait passer `Running` vers `Cancelling` dès que le producteur observe le token. -| Situation | Décision | -| --- | --- | -| Attente ou page RPC en cours | Le futur Transport est abandonné par sélection coopérative | -| Hydratation RPC longue | Le futur est abandonné ; aucune persistance n'est fabriquée | -| Candidat pas encore admis | Il ne démarre pas | -| Persistance Store déjà soumise | Elle est attendue et son résultat est compté | -| Dernier travail finit avant observation du cancel | `Completed` gagne si tout est durable | -| Cancel observé avec travail restant | `Cancelled` gagne après drainage sûr | -| Erreur fatale et cancel simultanés | L'erreur déjà observée reste la cause ; le snapshot indique l'arrêt des admissions | +| Situation | Décision | +|---------------------------------------------------|------------------------------------------------------------------------------------| +| Attente ou page RPC en cours | Le futur Transport est abandonné par sélection coopérative | +| Hydratation RPC longue | Le futur est abandonné ; aucune persistance n'est fabriquée | +| Candidat pas encore admis | Il ne démarre pas | +| Persistance Store déjà soumise | Elle est attendue et son résultat est compté | +| Dernier travail finit avant observation du cancel | `Completed` gagne si tout est durable | +| Cancel observé avec travail restant | `Cancelled` gagne après drainage sûr | +| Erreur fatale et cancel simultanés | L'erreur déjà observée reste la cause ; le snapshot indique l'arrêt des admissions | Le token ne promet pas l'interruption d'un commit externe. La garantie est l'absence de nouvelle admission et une terminaison avec état connu. @@ -317,12 +317,12 @@ Logging et sécurité : ## 18. Politique de retries -| Opération | Propriétaire du retry | Politique Job | -| --- | --- | --- | -| `getSignaturesForAddress` | Transport | Aucun retry supplémentaire | -| `getTransaction` | Transport | Aucun retry supplémentaire | -| Écriture Store | Store ou backend selon son contrat | Aucun retry aveugle | -| Job complet | Hôte futur | Nouvelle exécution explicite avec même JobId ou nouveau JobId | +| Opération | Propriétaire du retry | Politique Job | +|---------------------------|------------------------------------|---------------------------------------------------------------| +| `getSignaturesForAddress` | Transport | Aucun retry supplémentaire | +| `getTransaction` | Transport | Aucun retry supplémentaire | +| Écriture Store | Store ou backend selon son contrat | Aucun retry aveugle | +| Job complet | Hôte futur | Nouvelle exécution explicite avec même JobId ou nouveau JobId | Les erreurs typées finales du Transport deviennent des codes de résultat Job. Les chaînes de fournisseurs ne sont jamais analysées. @@ -372,26 +372,101 @@ Canaries externes : - interdiction de dépendance et de type public ; - smoke opt-in Devnet plus PostgreSQL configuré, sans endpoint payant ni secret versionné. -## 21. Découpage prévisionnel +## 21. Sizing recalibré — prereleases souples -Chaque tranche vise 15 à 20 minutes et doit produire une preuve atomique. +Chaque tranche technique vise environ 15 à 20 minutes de travail effectif lorsque le sujet s'y prête. Cette durée est une cible de granularité, pas une durée maximale de compilation, diagnostic ou preuve live. -| Préversion | Critère d'entrée | Contenu atomique | Critère de sortie | -| --- | --- | --- | --- | -| `pre.001` | Stable v0.3.5 vérifiée | Audit, décisions, plan et validation | Documents, delta et bump de version uniquement | -| `pre.002` | Plan 027 accepté | Identité, lifecycle et token d'annulation Job API | Unitaires API et audit firewalls | -| `pre.003` | Transitions Job stables | Notifications latest-value génériques | Tests multi-listeners et canary externe API | -| `pre.004` | Besoin provenance confirmé | Retour Transport observé pour `getTransaction` | Tests routage, retry et endpoint victorieux | -| `pre.005` | Transport observé stable | Scaffold backfill, requêtes, scopes et découverte | Fixtures latest, before, after et explicite | -| `pre.006` | Candidats déterministes | Conversion RAW v1 et provenance | Golden bytes, hash, Base58 et absent ou null | -| `pre.007` | RAW v1 figé | Persistance Store et idempotence | Insert, already, purge, observation, missing et conflit | -| `pre.008` | Résultats Store typés | Concurrence, résultats et frontière | Fin désordonnée, bornes et checkpoint | -| `pre.009` | Frontière prouvée | Annulation et snapshots concrets | Courses terminales et listener lent | -| `pre.010` | Vertical complet | Hardening, canaries externes et firewalls | Tests adversariaux et preuve live si disponible | -| `pre.011` | Aucun défaut fonctionnel ouvert | Gate technique final et smoke opt-in | Workspace vert et cargo trees audités | -| `pre.012` | Gate technique vert | Réconciliation documentaire finale | Architecture, index, roadmap et contrats alignés | -| `pre.013` | Documentation réconciliée | Gate de publication | Changelog, prompt v0.3.7 et archive minimale | -| `rel.001` | Gate de publication vert | Publication stable | Version stable et deltas Git séparés | +Le forecast reste souple : une tranche peut être scindée, fusionnée ou réordonnée par delta si la réalité technique l'exige, sans fusionner les couloirs de fermeture. Un correctif est inséré sous sa prerelease avec un titre `#### pre.NNN-fix.MMM` afin de conserver une hiérarchie éditable. + +### `pre.001` — Audit, décisions, plan et validation + +**Statut : réalisé ; corrigé par `pre.001-fix.001`.** + +Budget cible : **15-20 min**. L'entrée exigeait la stable v0.3.5 et les deux archives vérifiées. La tranche a produit l'audit KSP, kbot3 et RPC officiel, les décisions d'ownership, le plan, la validation et le bump `0.3.6-pre.1`, sans crate ni code Rust. La sortie est la surface de travail acceptée pour Job API et Backfill. + +#### `pre.001-fix.001` — Sizing éditable et audit Markdown effectif + +**Statut : réalisé ; correctif d'outillage et documentaire.** + +Le sizing tabulaire est remplacé par les présentes sous-sections afin que chaque correctif futur reste sous sa prerelease. Le validateur Markdown reconnaît désormais les séparateurs plausibles contenant des espaces erronés, applique l'alignement source gauche, droit ou centré indiqué par les marqueurs et possède des tests de régression. Les tableaux de `pre.001` sont réellement réalignés. Comme le script d'audit est du code d'outillage consommé par le gate, la version Cargo devient `0.3.6-pre.1.fix.1`. + +### `pre.002` — Identité, lifecycle et annulation Job API + +**Statut : planifié.** + +Budget cible : **15-20 min**. Entrée : plan 027 corrigé et gate Markdown fiable. Implémenter uniquement identité, transitions et token d'annulation runtime-neutral. Sortie : unitaires API et firewalls verts, sans notification concrète ni backfill. + +### `pre.003` — Notifications latest-value génériques + +**Statut : planifié.** + +Budget cible : **15-20 min**. Entrée : transitions Job stables. Implémenter séquences, snapshot source et resynchronisation multi-listeners. Sortie : listener lent, terminal retenu et canary externe API verts. + +### `pre.004` — Provenance Transport observée + +**Statut : planifié.** + +Budget cible : **15-20 min**. Entrée : besoin de provenance confirmé par le plan. Ajouter le retour observé de `getTransaction` sans dupliquer le client. Sortie : tests de routage, retry et endpoint réellement victorieux verts. + +### `pre.005` — Fondation Backfill et découverte + +**Statut : planifié.** + +Budget cible : **15-20 min**. Entrée : Transport observé stable. Créer la crate, les requêtes, scopes, validations, candidats et pagination. Sortie : fixtures latest, before, after et explicite, avec déduplication stable. + +### `pre.006` — Conversion RAW v1 et provenance + +**Statut : planifié.** + +Budget cible : **15-20 min**. Entrée : candidats déterministes. Implémenter décodage Base58 minimal, payload canonique et provenance. Sortie : golden bytes, hash, absent contre `null` et block time hostile couverts. + +### `pre.007` — Persistance Store et idempotence + +**Statut : planifié.** + +Budget cible : **15-20 min**. Entrée : RAW v1 figé. Composer l'écriture atomique Store normale et les outcomes distincts. Sortie : insert, already present, purge, observation, missing, conflit et relance idempotente couverts. + +### `pre.008` — Concurrence, frontier et checkpoint + +**Statut : planifié.** + +Budget cible : **15-20 min**. Entrée : résultats Store typés. Ajouter admissions bornées, réconciliation hors ordre et reprise. Sortie : frontière contiguë, trous, bornes et checkpoints des quatre scopes prouvés. + +### `pre.009` — Annulation et snapshots concrets + +**Statut : planifié.** + +Budget cible : **15-20 min**. Entrée : frontière prouvée. Intégrer annulation sur les phases critiques et progression Backfill latest-value. Sortie : courses terminales, drainage Store et listener lent couverts. + +### `pre.010` — Hardening et canaries externes + +**Statut : planifié.** + +Budget cible : **15-20 min**. Entrée : vertical déterministe complet. Fermer scénarios adversariaux, API externe, dépendances et sécurité ; exécuter la preuve live si justifiée et disponible. Sortie : aucun défaut fonctionnel ou firewall ouvert. + +### `pre.011` — Gate technique final + +**Statut : planifié.** + +Budget cible : **15-20 min**. Entrée : hardening vert. Rejouer le workspace complet, Clippy, tests et cargo trees, plus le smoke opt-in retenu. Sortie : preuve technique finale consignée sans réconciliation documentaire. + +### `pre.012` — Réconciliation documentaire finale + +**Statut : planifié.** + +Budget cible : **10-15 min**. Entrée : gate technique final vert. Aligner architecture, index, README, USAGE, plan et validation sur la surface prouvée. Sortie : documentation durable cohérente, sans CHANGELOG, ROADMAP ni prompt suivant. + +### `pre.013` — Préparation de publication minimale + +**Statut : planifié.** + +Budget cible : **10-15 min**. Entrée : réconciliation documentaire validée. Modifier uniquement la mécanique Cargo/delta, `CHANGELOG.md`, `ROADMAP.md` et le prompt v0.3.7. Sortie : archive minimale prête pour le gate de publication. + +### `rel.001` — Stabilisation et tag `v0.3.6` + +**Statut : planifié.** + +Entrée : gate de publication vert. Synchroniser la version stable, livrer le delta `rel.001`, effectuer les opérations Git séparées et poser le tag stable. Aucun rattrapage fonctionnel ou documentaire n'est admis. Le smoke live reste conditionnel à un PostgreSQL explicitement configuré. Son absence n'annule pas la preuve déterministe et ne doit pas conduire à inventer un résultat. diff --git a/docs/rules/RULES_DOCUMENTATION.md b/docs/rules/RULES_DOCUMENTATION.md index abad4df..0d4eabf 100644 --- a/docs/rules/RULES_DOCUMENTATION.md +++ b/docs/rules/RULES_DOCUMENTATION.md @@ -1,5 +1,5 @@ - + # Règles de documentation @@ -36,9 +36,9 @@ Les règles `DOC-*` s'appliquent aux documents Markdown internes et à leur orga - **DOC-TABLE-001** — Un tableau Markdown KSP utilise `|` uniquement comme séparateur structurel de colonnes. Un caractère `|` littéral, y compris sous forme échappée `\|`, est interdit dans le contenu d’une cellule ; reformuler le contenu, utiliser `/`, `et`, une liste ou un bloc de code hors tableau. - **DOC-TABLE-002** — Toutes les lignes d’un même tableau ont leurs séparateurs verticaux aux mêmes positions. La largeur de chaque colonne est déterminée par le contenu le plus large de cette colonne, en comptant l’en-tête et les lignes de données. -- **DOC-TABLE-003** — Chaque cellule de contenu commence par exactement un espace après le séparateur `|` gauche. Dans la cellule qui porte le contenu le plus large d’une colonne, il existe aussi exactement un espace entre le contenu et le séparateur `|` droit. Les cellules plus courtes conservent l’unique espace gauche et reçoivent uniquement le padding droit nécessaire pour aligner les séparateurs verticaux. -- **DOC-TABLE-004** — La ligne séparatrice en tirets occupe exactement la même largeur que chaque colonne ; les marqueurs `:` de l’alignement Markdown restent autorisés lorsqu’ils sont intentionnels. Le résultat attendu est équivalent au reformatage de tableau produit par RustRover, mais la règle structurelle KSP prime sur l’éditeur utilisé. -- **DOC-TABLE-005** — Lorsqu’une modification touche une ligne d’un tableau, le tableau entier est réaligné avant livraison. `python3 scripts/audit_markdown_tables.py ` est le canari mécanique recommandé pour les fichiers concernés ; les blocs de code fenced ne sont pas interprétés comme des tableaux. +- **DOC-TABLE-003** — L’alignement source des cellules suit la ligne séparatrice. Une colonne `|---|` ou `|:---|` est alignée à gauche : chaque contenu possède exactement un espace gauche et uniquement le padding droit nécessaire. Une colonne `|---:|` est alignée à droite : chaque contenu possède exactement un espace droit et uniquement le padding gauche nécessaire. Une colonne `|:---:|` est centrée : les paddings gauche et droit diffèrent d’au plus un espace. Chaque cellule conserve au minimum un espace de chaque côté du contenu. +- **DOC-TABLE-004** — La cellule séparatrice en tirets occupe exactement toute la largeur de la colonne, sans espace entre ses marqueurs et les séparateurs `|`. Elle contient au moins trois tirets ; `:---` sélectionne l’alignement gauche explicite, `---:` l’alignement droit et `:---:` le centrage. Le résultat attendu est équivalent au reformatage de tableau produit par RustRover, mais la règle structurelle KSP prime sur l’éditeur utilisé. +- **DOC-TABLE-005** — Lorsqu’une modification touche une ligne d’un tableau, le tableau entier est réaligné avant livraison. `python3 scripts/audit_markdown_tables.py ` est le canari mécanique recommandé pour les fichiers concernés ; il doit reconnaître et rejeter une ligne séparatrice Markdown plausible même lorsque des espaces erronés l’empêchent de respecter `DOC-TABLE-004`. Les blocs de code fenced ne sont pas interprétés comme des tableaux. - **DOC-TABLE-006** — L’immuabilité des deltas déjà publiés prime sur un reformatage rétrospectif : un ancien fichier `deltas/` n’est jamais réécrit uniquement pour satisfaire une règle de présentation introduite ultérieurement. Tout nouveau delta et tout autre tableau modifiable créé ou touché doivent en revanche respecter immédiatement `DOC-TABLE-001` à `DOC-TABLE-005`. ## Espacement vertical Markdown diff --git a/docs/validation/010-V0_2_7_ONCHAIN_WEBSOCKET.md b/docs/validation/010-V0_2_7_ONCHAIN_WEBSOCKET.md index a03a691..ea9db12 100644 --- a/docs/validation/010-V0_2_7_ONCHAIN_WEBSOCKET.md +++ b/docs/validation/010-V0_2_7_ONCHAIN_WEBSOCKET.md @@ -1,5 +1,5 @@ - + # Validation `0.2.7` — WebSocket Solana standard @@ -33,17 +33,17 @@ Pour une paire unstable, l'unsubscribe associé est classé `Unstable pair` dans ## 2. Matrice exhaustive des 18 opérations -| # | Méthode | Type | Statut `pre.001` | Paramètres / résultat essentiels | Notification / paire | Stratégie de test | Source officielle | Compliance | +| # | Méthode | Type | Statut `pre.001` | Paramètres / résultat essentiels | Notification / paire | Stratégie de test | Source officielle | Compliance | |---:|---------------------------|-------------|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------|---------------------------------------------------------------|-----------------------------------------------------------------|----------------| -| 1 | `accountSubscribe` | subscribe | Stable/documented | pubkey ; config `commitment`, `encoding`, `dataSlice` ; result numeric id ; `minContextSlot` upstream actuellement ignoré, donc non promis | `accountNotification` | fixture encodings/config + subscribe/notify | `https://solana.com/docs/rpc/websocket/accountsubscribe` | Done `pre.009` | -| 2 | `accountUnsubscribe` | unsubscribe | Stable/documented | remote id ; `true` or RPC error unknown id | account pair | handle local -> remote id fixture | `https://solana.com/docs/rpc/websocket/accountunsubscribe` | Done `pre.009` | -| 3 | `blockSubscribe` | subscribe | **Unstable** | `all`/mentions filter ; confirmed/finalized ; encoding ; tx details ; max tx version ; showRewards | `blockNotification` | all options + null block/error + validator capability fixture | `https://solana.com/docs/rpc/websocket/blocksubscribe` | Done `pre.011` | -| 4 | `blockUnsubscribe` | unsubscribe | **Unstable pair** | remote id ; boolean/error | block pair | generic registry unsubscribe | `https://solana.com/docs/rpc/websocket/blockunsubscribe` | Done `pre.011` | -| 5 | `logsSubscribe` | subscribe | Stable/documented | `all`, `allWithVotes`, exactly one `mentions`; commitment | `logsNotification` | 3 filters + invalid multi-mention + notification | `https://solana.com/docs/rpc/websocket/logssubscribe` | Done `pre.009` | -| 6 | `logsUnsubscribe` | unsubscribe | Stable/documented | remote id ; boolean/error | logs pair | generic registry unsubscribe | `https://solana.com/docs/rpc/websocket/logsunsubscribe` | Done `pre.009` | -| 7 | `programSubscribe` | subscribe | Stable/documented | program pubkey ; commitment ; filters ; encoding ; dataSlice ; `withContext` | `programNotification` | contexted/non-contexted fixtures + filters | `https://solana.com/docs/rpc/websocket/programsubscribe` | Done `pre.009` | -| 8 | `programUnsubscribe` | unsubscribe | Stable/documented | remote id ; boolean/error | program pair | generic registry unsubscribe | `https://solana.com/docs/rpc/websocket/programunsubscribe` | Done `pre.009` | -| 9 | `rootSubscribe` | subscribe | Stable/documented | no params ; numeric id | `rootNotification` => `u64` | exact root fixture | `https://solana.com/docs/rpc/websocket/rootsubscribe` | Done `pre.010` | +| 1 | `accountSubscribe` | subscribe | Stable/documented | pubkey ; config `commitment`, `encoding`, `dataSlice` ; result numeric id ; `minContextSlot` upstream actuellement ignoré, donc non promis | `accountNotification` | fixture encodings/config + subscribe/notify | `https://solana.com/docs/rpc/websocket/accountsubscribe` | Done `pre.009` | +| 2 | `accountUnsubscribe` | unsubscribe | Stable/documented | remote id ; `true` or RPC error unknown id | account pair | handle local -> remote id fixture | `https://solana.com/docs/rpc/websocket/accountunsubscribe` | Done `pre.009` | +| 3 | `blockSubscribe` | subscribe | **Unstable** | `all`/mentions filter ; confirmed/finalized ; encoding ; tx details ; max tx version ; showRewards | `blockNotification` | all options + null block/error + validator capability fixture | `https://solana.com/docs/rpc/websocket/blocksubscribe` | Done `pre.011` | +| 4 | `blockUnsubscribe` | unsubscribe | **Unstable pair** | remote id ; boolean/error | block pair | generic registry unsubscribe | `https://solana.com/docs/rpc/websocket/blockunsubscribe` | Done `pre.011` | +| 5 | `logsSubscribe` | subscribe | Stable/documented | `all`, `allWithVotes`, exactly one `mentions`; commitment | `logsNotification` | 3 filters + invalid multi-mention + notification | `https://solana.com/docs/rpc/websocket/logssubscribe` | Done `pre.009` | +| 6 | `logsUnsubscribe` | unsubscribe | Stable/documented | remote id ; boolean/error | logs pair | generic registry unsubscribe | `https://solana.com/docs/rpc/websocket/logsunsubscribe` | Done `pre.009` | +| 7 | `programSubscribe` | subscribe | Stable/documented | program pubkey ; commitment ; filters ; encoding ; dataSlice ; `withContext` | `programNotification` | contexted/non-contexted fixtures + filters | `https://solana.com/docs/rpc/websocket/programsubscribe` | Done `pre.009` | +| 8 | `programUnsubscribe` | unsubscribe | Stable/documented | remote id ; boolean/error | program pair | generic registry unsubscribe | `https://solana.com/docs/rpc/websocket/programunsubscribe` | Done `pre.009` | +| 9 | `rootSubscribe` | subscribe | Stable/documented | no params ; numeric id | `rootNotification` => `u64` | exact root fixture | `https://solana.com/docs/rpc/websocket/rootsubscribe` | Done `pre.010` | | 10 | `rootUnsubscribe` | unsubscribe | Stable/documented | remote id ; boolean/error | root pair | generic registry unsubscribe | `https://solana.com/docs/rpc/websocket/rootunsubscribe` | Done `pre.010` | | 11 | `signatureSubscribe` | subscribe | Stable/documented | first transaction signature ; commitment ; `enableReceivedNotification` | `signatureNotification` early string or terminal error object | early + terminal + auto-close/no-resubscribe | `https://solana.com/docs/rpc/websocket/signaturesubscribe` | Done `pre.010` | | 12 | `signatureUnsubscribe` | unsubscribe | Stable/documented | remote id before terminal fire ; boolean/error | signature pair | cancel before terminal + stale after terminal | `https://solana.com/docs/rpc/websocket/signatureunsubscribe` | Done `pre.010` | diff --git a/docs/validation/011-V0_2_8_HELIUS_LASERSTREAM_WEBSOCKET.md b/docs/validation/011-V0_2_8_HELIUS_LASERSTREAM_WEBSOCKET.md index eceb1f8..cedc274 100644 --- a/docs/validation/011-V0_2_8_HELIUS_LASERSTREAM_WEBSOCKET.md +++ b/docs/validation/011-V0_2_8_HELIUS_LASERSTREAM_WEBSOCKET.md @@ -1,5 +1,5 @@ - + # Validation `0.2.8` — Helius LaserStream WebSocket @@ -108,23 +108,23 @@ Verdict : **gate `0.2.8-pre.001` positif ; `pre.002` + `fix.001` et `pre.003` so ## 3. Matrice provider normative -| Capability | Standard KSP | Helius doc actuelle | Décision `0.2.8` | Surface Helius attendue | Preuve finale attendue | +| Capability | Standard KSP | Helius doc actuelle | Décision `0.2.8` | Surface Helius attendue | Preuve finale attendue | |------------------------------|----------------:|-------------------------------------------------|--------------------------|-------------------------|---------------------------------------| -| `accountSubscribe` pair | oui | supporté | réutiliser wire standard | présente | exact wire + no regression | -| `logsSubscribe` pair | oui | supporté | réutiliser wire standard | présente | exact wire + no regression | -| `programSubscribe` pair | oui | supporté | réutiliser wire standard | présente | exact wire + no regression | -| `rootSubscribe` pair | oui | supporté | réutiliser standard | présente | exact wire + no regression | -| `signatureSubscribe` pair | oui | supporté | réutiliser standard | présente | one-shot conservé | -| `slotSubscribe` pair | oui | supporté | réutiliser standard | présente | exact wire + no regression | -| `blockSubscribe` pair | oui, unstable | non supporté | ne pas exposer Helius | **absente** | API absence + guard interne avant I/O | -| `slotsUpdatesSubscribe` pair | oui, unstable | page spécifique + overview courant = disponible | réutiliser wire standard | présente | exact wire + warning unstable | -| `voteSubscribe` pair | oui, unstable | non supporté | ne pas exposer Helius | **absente** | API absence + guard interne avant I/O | -| `transactionSubscribe` | non standard | extension Helius | ajouter typed | présente | request/ack/notification fixtures | -| `transactionUnsubscribe` | non standard | documenté dans référence transaction | ajouter via handle | présente | exact method + late-message race | -| `tokenAccounts` | non standard | `none/balanceChanged/all` | ajouter typed | présente dans filter | enum + serialization | -| `notifyOn` account | non standard | deprecated, no-op Agave 4.2 | ne pas exposer | absente | absence DTO public | -| `notifyOn` program | non standard | deprecated, no-op Agave 4.2 | ne pas exposer | absente | absence DTO public | -| enhanced account extra | non standard | mentionné, wire exact non publié | report explicite | absente | réaudit final | +| `accountSubscribe` pair | oui | supporté | réutiliser wire standard | présente | exact wire + no regression | +| `logsSubscribe` pair | oui | supporté | réutiliser wire standard | présente | exact wire + no regression | +| `programSubscribe` pair | oui | supporté | réutiliser wire standard | présente | exact wire + no regression | +| `rootSubscribe` pair | oui | supporté | réutiliser standard | présente | exact wire + no regression | +| `signatureSubscribe` pair | oui | supporté | réutiliser standard | présente | one-shot conservé | +| `slotSubscribe` pair | oui | supporté | réutiliser standard | présente | exact wire + no regression | +| `blockSubscribe` pair | oui, unstable | non supporté | ne pas exposer Helius | **absente** | API absence + guard interne avant I/O | +| `slotsUpdatesSubscribe` pair | oui, unstable | page spécifique + overview courant = disponible | réutiliser wire standard | présente | exact wire + warning unstable | +| `voteSubscribe` pair | oui, unstable | non supporté | ne pas exposer Helius | **absente** | API absence + guard interne avant I/O | +| `transactionSubscribe` | non standard | extension Helius | ajouter typed | présente | request/ack/notification fixtures | +| `transactionUnsubscribe` | non standard | documenté dans référence transaction | ajouter via handle | présente | exact method + late-message race | +| `tokenAccounts` | non standard | `none/balanceChanged/all` | ajouter typed | présente dans filter | enum + serialization | +| `notifyOn` account | non standard | deprecated, no-op Agave 4.2 | ne pas exposer | absente | absence DTO public | +| `notifyOn` program | non standard | deprecated, no-op Agave 4.2 | ne pas exposer | absente | absence DTO public | +| enhanced account extra | non standard | mentionné, wire exact non publié | report explicite | absente | réaudit final | | proactive heartbeat | absent standard | 10 min idle, ping recommandé | Helius-only actor policy | interne | deterministic timer tests | | replay historique | absent standard | pas de contrat WS KSP | interdit | absent | docs/canary no promise | @@ -134,16 +134,16 @@ Verdict : **gate `0.2.8-pre.001` positif ; `pre.002` + `fix.001` et `pre.003` so | Famille | `SolanaStandardWsSession` | `HeliusLaserStreamWsSession` | DTO/wire | |--------------------|:-------------------------:|:----------------------------:|--------------------| -| Account | oui | oui | partagé standard | -| Block | oui | **non** | standard seulement | -| Logs | oui | oui | partagé standard | -| Program | oui | oui | partagé standard | -| Root | oui | oui | partagé standard | -| Signature | oui | oui | partagé standard | -| Slot | oui | oui | partagé standard | -| SlotsUpdates | oui | oui | partagé standard | -| Vote | oui | **non** | standard seulement | -| Helius Transaction | **non** | oui | provider-specific | +| Account | oui | oui | partagé standard | +| Block | oui | **non** | standard seulement | +| Logs | oui | oui | partagé standard | +| Program | oui | oui | partagé standard | +| Root | oui | oui | partagé standard | +| Signature | oui | oui | partagé standard | +| Slot | oui | oui | partagé standard | +| SlotsUpdates | oui | oui | partagé standard | +| Vote | oui | **non** | standard seulement | +| Helius Transaction | **non** | oui | provider-specific | ### 4.2 Invariants architecture diff --git a/docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md b/docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md index 361f722..31fa47c 100644 --- a/docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md +++ b/docs/validation/022-V0_3_5_INTERFACE_ACQUISITION_EVENTS.md @@ -1,5 +1,5 @@ - + # Validation `0.3.5` — Interface passive acquisition events @@ -29,7 +29,7 @@ Cette preuve est l'entrée de `0.3.5`. Elle n'est pas réétiquetée comme valid ## 3. Gate de lecture `pre.001` | Contrôle | Résultat | Preuve / décision | -| ------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------- | +|---------------------------------------|----------|-------------------------------------------------------------------------------------------------------| | règles globales relues | PASS | familles KSP-API/TRANSPORT/STORE/NOTIFY/PROC/REL et DEP applicables relues | | architecture durable relue | PASS | Interface passif, Transport protocol/runtime, Store persistant, composition owner des conversions | | fondation Interface stable relue | PASS | surface Program petite ; dépendance normale Core-only | @@ -45,24 +45,24 @@ Cette preuve est l'entrée de `0.3.5`. Elle n'est pas réétiquetée comme valid ## 4. Matrice de décision par famille -| Famille | Décision | Owner | Motif déterminant | -| ------------------------------------- | ------------------- | ------------------------------ | ---------------------------------------------------------------------------------------- | -| slot lifecycle intersection | ADMIS | Interface | 7 faits compacts normalisés ; détails protocol/provider restent Transport | -| transaction execution | ADMIS `pre.003` | Interface | `slot + TransactionSignature + Succeeded/Failed` multi-producer | -| logs realtime | IDÉE DIFFÉRÉE | Interface si futur gate | payload variable/bornes/consumer non démontrés ; ne pas mélanger avec execution | -| signature commitment/snapshot | REPORTÉ | Transport | HTTP snapshot != WS one-shot commitment ; distinct de l'exécution | -| vote | REPORTÉ | Transport | gossip pré-consensus != transaction qualifiée `is_vote` | -| account/program | REJET Interface | Store/Transport | risque de doublon `RawAccountState`/observation ou wrapper Transport | -| transaction | REJET Interface | Store/Transport | `RawTransaction`/observation déjà persistants | -| block | REJET Interface | Transport | conteneur/payload d'acquisition ; aucun besoin passif transversal démontré | -| Yellowstone Entry | REPORTÉ | Transport DTO | mono-producer aujourd'hui ; un futur fait transversal KSP serait réaudité pour Interface | -| persisted-data available notification | REJET Interface | Store API | ownership normatif `KSP-NOTIFY-001..006` | -| Helius Enhanced transaction | PRODUCTEUR ADMIS | Transport DTO / Interface fact | DTO provider-specific ; projection conservative vers le fait commun admis | +| Famille | Décision | Owner | Motif déterminant | +|---------------------------------------|------------------|--------------------------------|------------------------------------------------------------------------------------------| +| slot lifecycle intersection | ADMIS | Interface | 7 faits compacts normalisés ; détails protocol/provider restent Transport | +| transaction execution | ADMIS `pre.003` | Interface | `slot + TransactionSignature + Succeeded/Failed` multi-producer | +| logs realtime | IDÉE DIFFÉRÉE | Interface si futur gate | payload variable/bornes/consumer non démontrés ; ne pas mélanger avec execution | +| signature commitment/snapshot | REPORTÉ | Transport | HTTP snapshot != WS one-shot commitment ; distinct de l'exécution | +| vote | REPORTÉ | Transport | gossip pré-consensus != transaction qualifiée `is_vote` | +| account/program | REJET Interface | Store/Transport | risque de doublon `RawAccountState`/observation ou wrapper Transport | +| transaction | REJET Interface | Store/Transport | `RawTransaction`/observation déjà persistants | +| block | REJET Interface | Transport | conteneur/payload d'acquisition ; aucun besoin passif transversal démontré | +| Yellowstone Entry | REPORTÉ | Transport DTO | mono-producer aujourd'hui ; un futur fait transversal KSP serait réaudité pour Interface | +| persisted-data available notification | REJET Interface | Store API | ownership normatif `KSP-NOTIFY-001..006` | +| Helius Enhanced transaction | PRODUCTEUR ADMIS | Transport DTO / Interface fact | DTO provider-specific ; projection conservative vers le fait commun admis | ## 5. Intersection lifecycle retenue | Stage Interface prévu | Producer Solana standard | Producer Yellowstone | Verdict | Détails exclus du contrat commun | -| ------------------------- | -------------------------------------- | -------------------- | ------- | -------------------------------------------------------------------------------- | +|---------------------------|----------------------------------------|----------------------|---------|----------------------------------------------------------------------------------| | `Processed` | `slotSubscribe` | `Processed` | PASS | WS parent/root ; Yellowstone filters/created_at/parent | | `FirstShredReceived` | `slotsUpdates::FirstShredReceived` | `FirstShredReceived` | PASS | timestamps/metadata source | | `Completed` | `slotsUpdates::Completed` | `Completed` | PASS | timestamps/metadata source | @@ -112,7 +112,7 @@ TransactionExecutionEvent { slot, signature, outcome } ## 7. Validation anti-duplication Store | Modèle Store stable | Action `0.3.5` | Justification | -| ------------------------------- | ------------------- | ---------------------------------------------- | +|---------------------------------|---------------------|------------------------------------------------| | `RawTransaction` | inchangé | payload durable/replayable | | `RawTransactionObservation` | inchangé | provenance persistante | | `RawAccountState` | inchangé | état account durable | @@ -162,7 +162,7 @@ Yellowstone gRPC -------------+ ## 10. Threat gates | Gate | Attendu `0.3.5` | Statut courant | -| ------------------------------------ | ------------------------------------------------- | ------------------ | +|--------------------------------------|---------------------------------------------------|--------------------| | mega enum | absent | PLAN PASS | | protocol/provider enum leakage | absent | PLAN PASS | | timestamp semantics conflated | absent | PLAN PASS | @@ -440,11 +440,11 @@ Contraintes : ### 21.2 Convergence producer -| Producer | Slot | Signature | Outcome | Metadata volontairement exclue | Verdict | -| -------------------------------------- | ----------------------- | ----------------------------------- | ----------------------------------- | -------------------------------------------------------------- | ------- | -| Solana WS `logsSubscribe` | `context.slot` | base58, décodée par la composition | `err == null` / `err != null` | logs, commitment, subscription | PASS | -| Yellowstone `TransactionStatus` | `slot` | 64 bytes | `err None` / `err Some` | filters, created_at, is_vote, index, opaque error bytes | PASS | -| Helius `transactionSubscribe` | `slot` si forme typée | signature si forme typée | seulement si erreur déterminable | detail mode, transactionIndex, memo, blockTime, confirmation | PASS* | +| Producer | Slot | Signature | Outcome | Metadata volontairement exclue | Verdict | +|---------------------------------|-----------------------|------------------------------------|----------------------------------|--------------------------------------------------------------|---------| +| Solana WS `logsSubscribe` | `context.slot` | base58, décodée par la composition | `err == null` / `err != null` | logs, commitment, subscription | PASS | +| Yellowstone `TransactionStatus` | `slot` | 64 bytes | `err None` / `err Some` | filters, created_at, is_vote, index, opaque error bytes | PASS | +| Helius `transactionSubscribe` | `slot` si forme typée | signature si forme typée | seulement si erreur déterminable | detail mode, transactionIndex, memo, blockTime, confirmation | PASS* | `PASS*` Helius signifie **projection conservative** : aucune `TransactionExecutionEvent` n'est fabriquée pour une forme `none`, inconnue ou dont l'erreur d'exécution est absente/indéterminable. Le DTO provider reste Transport-owned. @@ -933,4 +933,3 @@ deltas/0.3.5/pre.007.md ``` Aucun autre fichier ne doit être modifié. - diff --git a/docs/validation/023-V0_3_6_JOB_API_BACKFILL.md b/docs/validation/023-V0_3_6_JOB_API_BACKFILL.md index 8c7b97f..c948102 100644 --- a/docs/validation/023-V0_3_6_JOB_API_BACKFILL.md +++ b/docs/validation/023-V0_3_6_JOB_API_BACKFILL.md @@ -1,9 +1,9 @@ - + # Validation v0.3.6 — Job API et premier backfill RAW -## 1. Statut de `pre.001` +## 1. Statut de `pre.001` et de son fix - [X] Archive v0.3.5 testée intégralement avant extraction. - [X] Archive kbot3 testée intégralement et isolée de l'arbre source KSP. @@ -17,23 +17,32 @@ - [X] Plan découpé en tranches atomiques de 15 à 20 minutes. - [X] Aucun code de crate, manifeste de crate ou test Rust généré en `pre.001`. - [X] Version workspace ouverte en `0.3.6-pre.1`. +- [X] Sizing converti en sous-sections `### pre.NNN` et `#### pre.NNN-fix.MMM`. +- [X] Faux négatif du validateur Markdown reproduit avec une ligne séparatrice espacée. +- [X] Reconnaissance des séparateurs plausibles corrigée avant validation d'alignement. +- [X] Alignements gauche, droit et centré reliés aux marqueurs de la ligne séparatrice. +- [X] Cinq tests de régression positifs et négatifs ajoutés au validateur. +- [X] Treize documents durables révélés par le validateur corrigé réalignés mécaniquement. +- [X] Audit général corrigé : 264 tables et 141 fichiers propres. +- [X] Anciens deltas publiés laissés byte-inchangés. +- [X] Version workspace du correctif d'outillage synchronisée en `0.3.6-pre.1.fix.1`. ## 2. Preuve des archives d'entrée -| Archive | SHA-256 | Résultat ZIP | Usage | -| --- | --- | --- | --- | -| `khadhroony-solana-project-v0.3.5-thelatest1.zip` | `c395efb6a09c786069aa5063a7a414b55f8ae24e2b9d6eaa414b2f312cd62588` | 1 515 entrées, test intégral propre | Source exclusive de code | +| Archive | SHA-256 | Résultat ZIP | Usage | +|--------------------------------------------------------|--------------------------------------------------------------------|-------------------------------------|--------------------------------| +| `khadhroony-solana-project-v0.3.5-thelatest1.zip` | `c395efb6a09c786069aa5063a7a414b55f8ae24e2b9d6eaa414b2f312cd62588` | 1 515 entrées, test intégral propre | Source exclusive de code | | `khadhroony-bot3_v0.5.3-pre.005-fix010-thelatest1.zip` | `ee47643b9f8b582ee8db97b2381ec107e45aef8c009fee44757531514615d318` | 2 501 entrées, test intégral propre | Référence fonctionnelle isolée | -| `Texte collé.txt` | `0b86c7107349bcbf0a80319ba8cf9aabdd2809f27af9459592667f3e425e8d58` | Sans objet | Journal opérateur v0.3.5 | +| `Texte collé.txt` | `0b86c7107349bcbf0a80319ba8cf9aabdd2809f27af9459592667f3e425e8d58` | Sans objet | Journal opérateur v0.3.5 | Aucune entrée absolue, traversée, avec séparateur inversé ou lien symbolique ZIP n'a été trouvée dans les deux archives. ## 3. Baseline v0.3.5 observée - [X] `scripts/audit_rust_workspace_rules.py` : propre avant modification. -- [X] `scripts/audit_markdown_tables.py` : 240 tables et 150 fichiers propres avant modification. +- [X] Le résultat historique `240 tables / 150 fichiers` est identifié comme incomplet : les séparateurs espacés n'étaient pas reconnus. - [X] Audits Rust workspace post-modification : propres, aucune crate Rust ajoutée. -- [X] Audit Markdown post-modification complet hors deltas historiques antérieurs : 240 tables et 153 fichiers propres. +- [X] Le résultat historique `240 tables / 153 fichiers` de `pre.001` est identifié comme faux positif pour les nouveaux tableaux non alignés. - [X] Contrats et tailles des trois nouveaux fichiers vérifiés. - [X] Le journal opérateur fourni rapporte `cargo fmt`, audits, `cargo check`, `cargo clippy` et suites Interface ou Program verts sur v0.3.5. - [X] L'environnement courant ne fournit pas l'exécutable `cargo` ; aucune validation Rust locale n'est annoncée comme exécutée. diff --git a/scripts/audit_markdown_tables.py b/scripts/audit_markdown_tables.py index 5ea487b..2051da2 100755 --- a/scripts/audit_markdown_tables.py +++ b/scripts/audit_markdown_tables.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # file: scripts/audit_markdown_tables.py -# version: 2 +# version: 3 """Validate KSP Markdown tables and vertical spacing for explicitly supplied files or directories.""" @@ -46,7 +46,28 @@ def _is_separator_row(line: str) -> bool: if not _is_table_row(line): return False cells = _cells(line) - return bool(cells) and all(_SEPARATOR_CELL.fullmatch(cell) is not None for cell in cells) + return bool(cells) and all(_separator_marker(cell) is not None for cell in cells) + + +def _separator_marker(cell: str) -> str | None: + marker = cell.strip() + if _SEPARATOR_CELL.fullmatch(marker) is None: + return None + return marker + + +def _separator_alignment(marker: str) -> str: + if marker.startswith(":") and marker.endswith(":"): + return "center" + if marker.endswith(":"): + return "right" + return "left" + + +def _space_padding(cell: str) -> tuple[int, int]: + left = len(cell) - len(cell.lstrip(" ")) + right = len(cell) - len(cell.rstrip(" ")) + return left, right def _validate_table(path: pathlib.Path, start_line: int, rows: list[str]) -> list[str]: @@ -75,27 +96,46 @@ def _validate_table(path: pathlib.Path, start_line: int, rows: list[str]) -> lis ) continue - content_cells = [cell for cell in raw_cells if _SEPARATOR_CELL.fullmatch(cell) is None] + content_cells = [cell for row_index, cell in enumerate(raw_cells) if row_index != 1] if not content_cells: errors.append(f"{path}:{start_line}: column {column_index + 1} has no header/data content") continue max_content_width = max(len(cell.strip()) for cell in content_cells) - required_width = max_content_width + 2 + marker = _separator_marker(raw_cells[1]) + if marker is None: + errors.append(f"{path}:{start_line + 1}: separator for column {column_index + 1} is malformed") + continue + minimum_separator_width = 3 + marker.count(":") + required_width = max(max_content_width + 2, minimum_separator_width) if expected_width != required_width: errors.append( f"{path}:{start_line}: column {column_index + 1} width is {expected_width}; expected {required_width} " - "(longest content plus exactly one space on each side)" + "(longest content plus outer padding, or the minimum Markdown separator width)" ) + alignment = _separator_alignment(marker) for cell in content_cells: - if len(cell) < 2 or not cell.startswith(" ") or cell.startswith(" ") or not cell.endswith(" "): + content_width = len(cell.strip()) + left_padding, right_padding = _space_padding(cell) + if content_width + left_padding + right_padding != len(cell) or left_padding < 1 or right_padding < 1: errors.append( - f"{path}:{start_line}: column {column_index + 1} content cells must start with exactly one space and use right padding only" + f"{path}:{start_line}: column {column_index + 1} content cells must use spaces only for outer alignment padding" ) break - if len(cell.strip()) == max_content_width and cell.endswith(" "): + expected_padding = expected_width - content_width + if alignment == "left" and (left_padding != 1 or right_padding != expected_padding - 1): errors.append( - f"{path}:{start_line}: column {column_index + 1} widest content must have exactly one space before the closing pipe" + f"{path}:{start_line}: column {column_index + 1} is left-aligned; content must use one leading space and right padding only" + ) + break + if alignment == "right" and (right_padding != 1 or left_padding != expected_padding - 1): + errors.append( + f"{path}:{start_line}: column {column_index + 1} is right-aligned; content must use left padding and one trailing space" + ) + break + if alignment == "center" and abs(left_padding - right_padding) > 1: + errors.append( + f"{path}:{start_line}: column {column_index + 1} is centered; left and right padding may differ by at most one space" ) break diff --git a/scripts/tests/test_audit_markdown_tables.py b/scripts/tests/test_audit_markdown_tables.py new file mode 100644 index 0000000..2da1650 --- /dev/null +++ b/scripts/tests/test_audit_markdown_tables.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +# file: scripts/tests/test_audit_markdown_tables.py +# version: 1 + +"""Regression tests for the KSP Markdown table audit.""" + +from __future__ import annotations + +import pathlib +import sys +import tempfile +import unittest + +sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1])) + +import audit_markdown_tables + + +class MarkdownTableAuditTests(unittest.TestCase): + """Cover table recognition and source alignment semantics.""" + + def audit(self, contents: str) -> tuple[int, list[str]]: + """Audit one temporary Markdown fixture.""" + + with tempfile.TemporaryDirectory() as directory: + path = pathlib.Path(directory) / "fixture.md" + path.write_text(contents, encoding="utf-8") + return audit_markdown_tables._audit_file(path) + + def test_accepts_left_and_right_aligned_columns(self) -> None: + """Canonical left and right padding follows the separator markers.""" + + table_count, errors = self.audit("| Name | Count |\n|------|------:|\n| A | 2 |\n") + self.assertEqual(table_count, 1) + self.assertEqual(errors, []) + + def test_accepts_explicit_left_and_centered_columns(self) -> None: + """Leading and paired colons select left and centered alignment.""" + + table_count, errors = self.audit("| Name | State |\n|:-----|:-----:|\n| A | ok |\n") + self.assertEqual(table_count, 1) + self.assertEqual(errors, []) + + def test_rejects_spaced_separator_that_was_previously_skipped(self) -> None: + """A malformed but recognizable separator is audited instead of ignored.""" + + table_count, errors = self.audit("| Name | Count |\n| --- | ---: |\n| A | 2 |\n") + self.assertEqual(table_count, 1) + self.assertTrue(errors) + + def test_rejects_left_padding_in_right_aligned_column(self) -> None: + """A trailing colon requires right-aligned source cells.""" + + table_count, errors = self.audit("| Name | Count |\n|------|------:|\n| A | 2 |\n") + self.assertEqual(table_count, 1) + self.assertTrue(any("right-aligned" in error for error in errors)) + + def test_rejects_right_padding_in_explicit_left_column(self) -> None: + """A leading colon requires left-aligned source cells.""" + + table_count, errors = self.audit("| Name | Count |\n|:-----|-------|\n| A | 2 |\n") + self.assertEqual(table_count, 1) + self.assertTrue(any("left-aligned" in error for error in errors)) + + +if __name__ == "__main__": + unittest.main()