From 2f587e22be9ed7adea3079d1d2f326b7b6506084 Mon Sep 17 00:00:00 2001 From: SinuS Von SifriduS Date: Wed, 16 Sep 2026 09:38:30 +0200 Subject: [PATCH] 0.3.15-pre.011-fix.001 --- Cargo.toml | 4 +- .../README.md | 4 +- .../USAGE.md | 4 +- .../frontend/ts/main.ts | 51 +++++- .../src/tauri.rs | 8 +- .../tests/desktop_contract.rs | 23 ++- .../tests/release_completeness.rs | 14 +- deltas/0.3.15/pre.011-fix.001.md | 153 ++++++++++++++++++ ...0_3_15_RAW_TRANSACTION_INGEST_DESK_PLAN.md | 4 +- ...032-V0_3_15_RAW_TRANSACTION_INGEST_DESK.md | 16 +- 10 files changed, 266 insertions(+), 15 deletions(-) create mode 100644 deltas/0.3.15/pre.011-fix.001.md diff --git a/Cargo.toml b/Cargo.toml index 4817cb2..f3d025b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ # file: Cargo.toml -# version: 620 +# version: 621 [workspace] resolver = "3" members = ["crates/ksp-app-backfill-desk", "crates/ksp-app-config-desk", "crates/ksp-app-raw-transaction-ingest-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-store-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-interface-lib", "crates/ksp-job-api", "crates/ksp-job-backfill-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-program-api", "crates/ksp-raw-transaction-lib", "crates/ksp-store-api", "crates/ksp-store-lib", "crates/ksp-store-postgres-lib", "crates/ksp-wallet-lib", "crates/ksp-worker-api", "crates/ksp-worker-raw-transaction-ingest-lib"] [workspace.package] -version = "0.3.15-pre.11" +version = "0.3.15-pre.11.fix.1" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" diff --git a/crates/ksp-app-raw-transaction-ingest-desk/README.md b/crates/ksp-app-raw-transaction-ingest-desk/README.md index 5cb322d..2167a8f 100644 --- a/crates/ksp-app-raw-transaction-ingest-desk/README.md +++ b/crates/ksp-app-raw-transaction-ingest-desk/README.md @@ -1,5 +1,5 @@ - + # `ksp-app-raw-transaction-ingest-desk` @@ -48,7 +48,7 @@ http-block-polling Les profils standard publics committés déclarent `Block` en plus de `Logs`; `standard-block-direct` est donc composable depuis Config sur Devnet, Mainnet et Testnet. Cette déclaration ne transforme pas `blockSubscribe` en méthode stable et ne remplace pas la revalidation runtime. -Une route `Configured` est uniquement **composable depuis Config**. La disponibilité réelle du Store et du Worker est revalidée lors du Start avant publication de l'accusé runtime. Le backend relaie désormais chaque Worker actif par un snapshot latest-value sûr : lifecycle, health, activity, admission/persistence, backpressure, reconnect/replay, continuité, gaps et repair. Les mises à jour sont émises via l'événement Tauri `ksp-raw-ingest-route-status` et peuvent être resynchronisées avec `get_route_monitoring`. Les compteurs et slots `u64` sont projetés en texte décimal afin de rester exacts côté JavaScript. Le frontend consomme maintenant ce flux en temps réel et via resynchronisation explicite : chaque route affiche lifecycle/health/activity, persisted/source/gap summaries et ouvre un détail complet pipeline/persistence, reconnect/replay, continuité, gaps et repair. Les événements monitoring mettent aussi à jour les états actifs/terminaux afin que le sélecteur réseau et le Store partagé suivent le lifecycle réellement publié par le Worker. +Une route `Configured` est uniquement **composable depuis Config**. La disponibilité réelle du Store et du Worker est revalidée lors du Start avant publication de l'accusé runtime. Le backend relaie désormais chaque Worker actif par un snapshot latest-value sûr : lifecycle, health, activity, admission/persistence, backpressure, reconnect/replay, continuité, gaps et repair. Les mises à jour sont coalescées côté backend à une cadence bornée de `500 ms` maximum par route, émises via l'événement Tauri `ksp-raw-ingest-route-status` et peuvent être resynchronisées avec `get_route_monitoring`. Les compteurs et slots `u64` sont projetés en texte décimal afin de rester exacts côté JavaScript. Le frontend consomme maintenant ce flux en temps réel et via resynchronisation explicite : chaque route affiche lifecycle/health/activity, persisted/source/gap summaries et ouvre un détail complet pipeline/persistence, reconnect/replay, continuité, gaps et repair. Les événements monitoring mettent aussi à jour les états actifs/terminaux afin que le sélecteur réseau et le Store partagé suivent le lifecycle réellement publié par le Worker. Les snapshots steady-state patchent les champs de carte en place ; la reconstruction des contrôles Start/Stop est réservée aux changements de lifecycle, afin de préserver la réactivité des interactions. Pour `yellowstone-hydrated`, la stratégie Mainnet n'effectue plus un `getTransaction` par notification transactionnelle. Le Desk construit un abonnement Yellowstone Block léger ; chaque bloc observé déclenche une reconciliation HTTP `getBlock` Full/Base64 sur le même réseau. Le profil `publicnode_mainnet` expose un pool HTTP logique `default` contenant PublicNode et le RPC public Solana comme fallback de même priorité. Le pool conserve les limites propres à chaque endpoint, son health/cooldown et son round-robin interne. La reconciliation Yellowstone tolère un décalage bref entre le gRPC et les RPC HTTP grâce à un retry borné et interruptible par Stop. Le chemin protobuf -> RAW direct reste différé tant que la canonicalisation exacte du meta n'est pas prouvée pour toutes les formes supportées. diff --git a/crates/ksp-app-raw-transaction-ingest-desk/USAGE.md b/crates/ksp-app-raw-transaction-ingest-desk/USAGE.md index 2ea49bc..46e703b 100644 --- a/crates/ksp-app-raw-transaction-ingest-desk/USAGE.md +++ b/crates/ksp-app-raw-transaction-ingest-desk/USAGE.md @@ -1,5 +1,5 @@ - + # Utilisation de `ksp-app-raw-transaction-ingest-desk` @@ -42,7 +42,7 @@ Pour une route `Configured`, choisir `confirmed` ou `finalized` puis utiliser `S Plusieurs routes peuvent être actives simultanément lorsqu'elles appartiennent au même réseau logique. Chaque route conserve son Worker indépendant et son Stop ciblé. Le Store est partagé entre ces Workers ; le dernier Worker terminal déclenche la fermeture explicite du Store. Une route `Faulted` ne force pas l'arrêt des autres routes. Un Start d'un autre réseau est refusé tant que le Store partagé du réseau courant reste ouvert. -Le backend expose désormais un flux latest-value par route via l'événement Tauri `ksp-raw-ingest-route-status`. Chaque projection contient uniquement l'identité logique sûre de la route, lifecycle/health/activity, compteurs admission/persistence, backpressure, reconnect/replay, continuité, gaps et repair. La commande `get_route_monitoring` permet une resynchronisation explicite des routes actives et des derniers terminaux retenus dans la session runtime courante. Les séquences, slots et compteurs `u64` sont transmis en texte décimal pour préserver leur exactitude côté JavaScript. Le bouton `Resync monitoring` recharge explicitement les latest values backend ; les événements `ksp-raw-ingest-route-status` actualisent ensuite les cartes sans polling frontend. Une carte disposant d'un snapshot montre un résumé sûr (`health`, `activity`, persisted, sources actives, gaps) et le bouton `Supervision` ouvre le détail pipeline/persistence, sources, reconnect/replay, continuité et repair. Les derniers terminaux de la session restent consultables jusqu'à remplacement par un nouveau Start ou changement de session réseau. +Le backend expose désormais un flux latest-value par route via l'événement Tauri `ksp-raw-ingest-route-status`. Chaque projection contient uniquement l'identité logique sûre de la route, lifecycle/health/activity, compteurs admission/persistence, backpressure, reconnect/replay, continuité, gaps et repair. La commande `get_route_monitoring` permet une resynchronisation explicite des routes actives et des derniers terminaux retenus dans la session runtime courante. Les séquences, slots et compteurs `u64` sont transmis en texte décimal pour préserver leur exactitude côté JavaScript. Le bouton `Resync monitoring` recharge explicitement les latest values backend ; les événements `ksp-raw-ingest-route-status` actualisent ensuite les cartes sans polling frontend. Le relais backend coalesce les changements à une cadence maximale d'une émission toutes les `500 ms` par route, et les changements steady-state mettent à jour la carte en place sans recréer les boutons Start/Stop. Une carte disposant d'un snapshot montre un résumé sûr (`health`, `activity`, persisted, sources actives, gaps) et le bouton `Supervision` ouvre le détail pipeline/persistence, sources, reconnect/replay, continuité et repair. Les derniers terminaux de la session restent consultables jusqu'à remplacement par un nouveau Start ou changement de session réseau. Sur Mainnet, `yellowstone-hydrated` utilise un abonnement Yellowstone Block comme signal de temps réel puis reconcile chaque slot via `getBlock` Full/Base64. Cette stratégie évite l'hydration `getTransaction` unitaire par transaction. Le profil PublicNode peut exposer plusieurs endpoints HTTP sous le même rôle logique ; Transport distribue alors les requêtes selon ses règles de priorité, disponibilité, concurrence et cooldown. La conversion directe du payload Yellowstone vers le RAW canonique reste réservée à une évolution ultérieure tant que la parité complète du meta n'est pas prouvée. diff --git a/crates/ksp-app-raw-transaction-ingest-desk/frontend/ts/main.ts b/crates/ksp-app-raw-transaction-ingest-desk/frontend/ts/main.ts index 9e49876..c072f0c 100644 --- a/crates/ksp-app-raw-transaction-ingest-desk/frontend/ts/main.ts +++ b/crates/ksp-app-raw-transaction-ingest-desk/frontend/ts/main.ts @@ -1,5 +1,5 @@ // file: crates/ksp-app-raw-transaction-ingest-desk/frontend/ts/main.ts -// version: 7 +// version: 8 import "bootstrap"; import ResizeObserver from "resize-observer-polyfill"; @@ -29,6 +29,7 @@ const viewTitles: Record = { const activeRuntimes = new Map(); const lastRuntimes = new Map(); const routeMonitoring = new Map(); +const routeCards = new Map(); let routeInventory: RawIngestRouteInventoryDto | null = null; let selectedProfileId: string | null = null; let selectedMonitoringKey: string | null = null; @@ -215,6 +216,7 @@ function renderSelectedProfile(): void { text("#headerProfile", profile.profileId); const routeRoot = document.querySelector("#routeFoundation"); if (routeRoot) { + routeCards.clear(); routeRoot.replaceChildren(); for (const route of profile.routes) { const key = runtimeKey(profile.profileId, route.routeId); @@ -246,6 +248,7 @@ function renderSelectedProfile(): void { meta.className = "d-flex flex-wrap gap-2 mt-3 app-route-meta"; const state = document.createElement("span"); state.className = lifecycleBadgeClass(projectedState); + state.dataset.monitoringField = "state"; state.textContent = projectedState; const network = document.createElement("span"); network.className = "badge text-bg-light border text-dark"; @@ -260,18 +263,23 @@ function renderSelectedProfile(): void { summary.className = "app-route-monitoring-summary mt-3"; const health = document.createElement("span"); health.className = monitoring.health === "healthy" ? "badge text-bg-success" : "badge text-bg-warning"; + health.dataset.monitoringField = "health"; health.textContent = `health: ${monitoring.health}`; const activity = document.createElement("span"); activity.className = "badge text-bg-light border text-dark"; + activity.dataset.monitoringField = "activity"; activity.textContent = `activity: ${monitoring.activity}`; const persisted = document.createElement("span"); persisted.className = "badge text-bg-light border text-dark"; + persisted.dataset.monitoringField = "persisted"; persisted.textContent = `persisted: ${monitoring.persistedTotal}`; const sources = document.createElement("span"); sources.className = "badge text-bg-light border text-dark"; + sources.dataset.monitoringField = "sources"; sources.textContent = `sources: ${monitoring.sourceActive}/${monitoring.sourceTotal}`; const gaps = document.createElement("span"); gaps.className = monitoring.openGapCount === 0 ? "badge text-bg-light border text-dark" : "badge text-bg-warning"; + gaps.dataset.monitoringField = "gaps"; gaps.textContent = `gaps: ${monitoring.openGapCount}`; summary.append(health, activity, persisted, sources, gaps); body.append(summary); @@ -324,6 +332,7 @@ function renderSelectedProfile(): void { body.append(actions); } card.append(body); + routeCards.set(key, card); column.append(card); routeRoot.append(column); } @@ -450,6 +459,34 @@ function renderMonitoringDetail(): void { }); } +function renderRouteMonitoringCard(status: RawIngestRouteMonitoringDto): boolean { + const key = runtimeKey(status.profileId, status.routeId); + const card = routeCards.get(key); + if (!card) { + return false; + } + const field = (name: string): HTMLElement | null => card.querySelector(`[data-monitoring-field="${name}"]`); + const state = field("state"); + const health = field("health"); + const activity = field("activity"); + const persisted = field("persisted"); + const sources = field("sources"); + const gaps = field("gaps"); + if (!state || !health || !activity || !persisted || !sources || !gaps) { + return false; + } + state.className = lifecycleBadgeClass(status.state); + state.textContent = status.state; + health.className = status.health === "healthy" ? "badge text-bg-success" : "badge text-bg-warning"; + health.textContent = `health: ${status.health}`; + activity.textContent = `activity: ${status.activity}`; + persisted.textContent = `persisted: ${status.persistedTotal}`; + sources.textContent = `sources: ${status.sourceActive}/${status.sourceTotal}`; + gaps.className = status.openGapCount === 0 ? "badge text-bg-light border text-dark" : "badge text-bg-warning"; + gaps.textContent = `gaps: ${status.openGapCount}`; + return true; +} + function selectedCommitment(): RawIngestCommitment { const selector = document.querySelector("#routeCommitment"); return selector?.value === "finalized" ? "finalized" : "confirmed"; @@ -548,13 +585,21 @@ async function stopRoute(runtime: RawIngestRouteRuntimeDto): Promise { } function applyRouteMonitoring(status: RawIngestRouteMonitoringDto, source: "event" | "startup" | "start" | "stop" | "user"): void { + const key = runtimeKey(status.profileId, status.routeId); + const previous = routeMonitoring.get(key) ?? null; recordRouteMonitoring(status); if (selectedMonitoringKey === null && status.profileId === selectedProfileId) { - selectedMonitoringKey = runtimeKey(status.profileId, status.routeId); + selectedMonitoringKey = key; } renderMultiRouteRuntime(); - renderSelectedProfile(); + const lifecycleChanged = previous === null || previous.state !== status.state; + if (lifecycleChanged || !renderRouteMonitoringCard(status)) { + renderSelectedProfile(); + } else if (selectedMonitoringKey === key) { + renderMonitoringDetail(); + } frontendTrace("main", "Raw Transaction Ingest Desk route monitoring latest value applied", { + lifecycleChanged, routeId: status.routeId, sequence: status.sequence, source, diff --git a/crates/ksp-app-raw-transaction-ingest-desk/src/tauri.rs b/crates/ksp-app-raw-transaction-ingest-desk/src/tauri.rs index 1ca9b58..4eaa22e 100644 --- a/crates/ksp-app-raw-transaction-ingest-desk/src/tauri.rs +++ b/crates/ksp-app-raw-transaction-ingest-desk/src/tauri.rs @@ -1,5 +1,5 @@ // file: crates/ksp-app-raw-transaction-ingest-desk/src/tauri.rs -// version: 7 +// version: 8 //! Tauri runtime assembly for the KSP Raw Transaction Ingest desktop application. @@ -186,6 +186,8 @@ async fn start_route( return std::result::Result::Ok(acknowledgement); } +const ROUTE_MONITORING_MIN_EMIT_INTERVAL_MS: u64 = 500; + async fn monitor_route_status( app: tauri::AppHandle, runtime: crate::RawIngestRouteRuntimeDto, @@ -223,6 +225,10 @@ async fn monitor_route_status( } let observed = snapshot.worker_snapshot().sequence(); snapshot = source.wait_for_change(observed).await; + if !snapshot.worker_snapshot().state().is_terminal() { + tokio::time::sleep(std::time::Duration::from_millis(ROUTE_MONITORING_MIN_EMIT_INTERVAL_MS)).await; + snapshot = source.current(); + } } } diff --git a/crates/ksp-app-raw-transaction-ingest-desk/tests/desktop_contract.rs b/crates/ksp-app-raw-transaction-ingest-desk/tests/desktop_contract.rs index 9041466..6babc8e 100644 --- a/crates/ksp-app-raw-transaction-ingest-desk/tests/desktop_contract.rs +++ b/crates/ksp-app-raw-transaction-ingest-desk/tests/desktop_contract.rs @@ -1,5 +1,5 @@ // file: crates/ksp-app-raw-transaction-ingest-desk/tests/desktop_contract.rs -// version: 8 +// version: 9 //! Desktop scaffold contract canaries for Raw Transaction Ingest Desk. @@ -253,3 +253,24 @@ fn pre_011_frontend_consumes_latest_value_monitoring_and_renders_full_route_supe assert!(styles.contains(required), "missing pre.011 bounded supervision style {required}"); } } + +#[test] +fn pre_011_fix_001_monitoring_bridge_is_bounded_and_steady_updates_preserve_route_controls() { + let tauri = read_text(app_root().join("src/tauri.rs").as_path()); + for required in [ + "ROUTE_MONITORING_MIN_EMIT_INTERVAL_MS: u64 = 500", + "tokio::time::sleep(std::time::Duration::from_millis(ROUTE_MONITORING_MIN_EMIT_INTERVAL_MS)).await", + "snapshot = source.current()", + ] { + assert!(tauri.contains(required), "missing pre.011-fix.001 bounded relay marker {required}"); + } + let main = read_text(app_root().join("frontend/ts/main.ts").as_path()); + for required in [ + "const routeCards = new Map()", + "renderRouteMonitoringCard", + "const lifecycleChanged = previous === null || previous.state !== status.state", + "if (lifecycleChanged || !renderRouteMonitoringCard(status))", + ] { + assert!(main.contains(required), "missing pre.011-fix.001 stable-control marker {required}"); + } +} diff --git a/crates/ksp-app-raw-transaction-ingest-desk/tests/release_completeness.rs b/crates/ksp-app-raw-transaction-ingest-desk/tests/release_completeness.rs index 5548a23..d63868b 100644 --- a/crates/ksp-app-raw-transaction-ingest-desk/tests/release_completeness.rs +++ b/crates/ksp-app-raw-transaction-ingest-desk/tests/release_completeness.rs @@ -1,5 +1,5 @@ // file: crates/ksp-app-raw-transaction-ingest-desk/tests/release_completeness.rs -// version: 10 +// version: 11 //! Release-completeness canaries for Raw Transaction Ingest Desk Start-resource reconstruction. @@ -188,3 +188,15 @@ fn pre_011_frontend_supervision_closes_planned_ui_without_backend_module_growth( assert!(html.contains(required), "missing pre.011 operator supervision surface {required}"); } } + +#[test] +fn pre_011_fix_001_preserves_latest_value_contract_without_backend_module_growth() { + let lib = read_text(app_root().join("src/lib.rs").as_path()); + assert_eq!(lib.matches("mod ").count(), 16); + let tauri = read_text(app_root().join("src/tauri.rs").as_path()); + assert!(tauri.contains("source.wait_for_change(observed).await")); + assert!(tauri.contains("ROUTE_MONITORING_MIN_EMIT_INTERVAL_MS: u64 = 500")); + let main = read_text(app_root().join("frontend/ts/main.ts").as_path()); + assert!(main.contains("renderRouteMonitoringCard")); + assert!(main.contains("renderSelectedProfile")); +} diff --git a/deltas/0.3.15/pre.011-fix.001.md b/deltas/0.3.15/pre.011-fix.001.md new file mode 100644 index 0000000..5915e4c --- /dev/null +++ b/deltas/0.3.15/pre.011-fix.001.md @@ -0,0 +1,153 @@ + + + +# Delta `0.3.15-pre.011-fix.001` + +## Base + +```text +base archive : ksp-general-0.3.15-pre.011.zip +base SHA-256 : 13d9e9ff3feaae9fd5c74a44346a27293e20b53883a839e68b7c6793a82a7d08 +base version : 0.3.15-pre.11 +``` + +Le gate opérateur du 16 septembre 2026 confirme une base statiquement propre : audits Rust/Markdown, `cargo check --workspace`, Clippy strict, tests ciblés Raw Transaction Ingest Desk/Worker et workspace all-targets/all-features passent. Le défaut est live/frontend : après le Start Mainnet de `yellowstone-hydrated`, le Worker continue à hydrater des blocs alors qu'aucune seconde action Start/Stop n'atteint le tracing frontend avant la fin de l'essai. + +## Cause + +Le pont `monitor_route_status` de `pre.010` attendait chaque changement de séquence Worker puis émettait immédiatement un DTO complet vers Tauri. Avec une route productive, la séquence peut évoluer au rythme de l'ingestion transactionnelle. + +Le frontend `pre.011` appliquait ensuite chaque événement avec : + +```text +record latest-value +render runtime header +rebuild intégral des cartes de routes +rebuild du détail de supervision +tracing frontend +``` + +La reconstruction intégrale remplaçait aussi les boutons Start/Stop. Sous flux soutenu, un contrôle pouvait donc être détruit/recréé entre les événements pointeur et `click`, tout en surchargeant le thread UI. + +## Correctif backend Tauri + +Le contrat reste latest-value et event-driven, sans polling métier. Le relayeur coalesce désormais les snapshots avec une cadence maximale d'une émission toutes les `500 ms` par route : + +```text +wait_for_change(sequence) +terminal -> émission immédiate au prochain tour +non-terminal -> fenêtre de coalescence 500 ms +current() -> projection de la dernière valeur seulement +``` + +La source Worker reste une watch latest-value ; aucun snapshot intermédiaire n'est requis pour la correction opérationnelle. Le snapshot terminal reste projeté et la commande `get_route_monitoring` reste l'autorité de resynchronisation explicite. + +## Correctif frontend + +Une map DOM `routeCards` conserve les cartes de la vue courante. Les champs de résumé steady-state sont maintenant patchés en place : + +```text +lifecycle +health +activity +persisted +sources active / total +open gaps +``` + +`renderSelectedProfile()` n'est plus exécuté pour chaque incrément de compteur. Une reconstruction complète de la grille est réservée à : + +```text +premier snapshot/non-patchable +changement de lifecycle +resynchronisation explicite +Start/Stop explicites +``` + +Le détail de supervision n'est rerendu sur un snapshot steady-state que si ce snapshot appartient à la route actuellement sélectionnée. + +## Frontières inchangées + +Aucune modification de : + +```text +Config +Transport +Store +ksp-worker-raw-transaction-ingest-lib +Common RAW +stratégies acquisition/retry/replay/repair +``` + +Aucun polling frontend, aucune seconde pipeline et aucune ressource physique supplémentaire ne sont introduits. + +## Canaris + +Ajouts : + +```text +pre_011_fix_001_monitoring_bridge_is_bounded_and_steady_updates_preserve_route_controls +pre_011_fix_001_preserves_latest_value_contract_without_backend_module_growth +``` + +Ils figent la cadence bornée, la conservation de `wait_for_change`, le `current()` de coalescence, le patch des cartes steady-state et l'absence de croissance du module Rust de production. + +## Version + +```text +workspace.package.version : 0.3.15-pre.11.fix.1 +root Cargo header counter : 621 +``` + +## Validation d'assemblage + +Exécuté dans l'environnement d'assemblage : + +```text +python3 scripts/audit_rust_workspace_rules.py +python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.3.15 +TypeScript auxiliary no-emit check of main.ts/frontend_log.ts/invoke.ts +``` + +Cargo/rustfmt ne sont pas disponibles dans cet environnement ; aucun gate Cargo du fix n'est déclaré PASS avant exécution opérateur. + +Le gate opérateur attendu reste : + +```bash +cargo fmt --all +cargo fmt --all -- --check +python3 scripts/audit_rust_workspace_rules.py +python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.3.15 +cargo check --workspace +cargo clippy --workspace --all-targets --all-features -- -D warnings +cargo test -p ksp-app-raw-transaction-ingest-desk --all-targets --all-features +cargo test -p ksp-worker-raw-transaction-ingest-lib --all-targets --all-features +cargo test --workspace --all-targets --all-features +(cd crates/ksp-app-raw-transaction-ingest-desk && cargo tauri dev) +``` + +Aucun `npm run build` manuel n'est demandé. + +## Inventaire exact du delta + +Ajout : + +```text +deltas/0.3.15/pre.011-fix.001.md +``` + +Modifications : + +```text +Cargo.toml +crates/ksp-app-raw-transaction-ingest-desk/README.md +crates/ksp-app-raw-transaction-ingest-desk/USAGE.md +crates/ksp-app-raw-transaction-ingest-desk/frontend/ts/main.ts +crates/ksp-app-raw-transaction-ingest-desk/src/tauri.rs +crates/ksp-app-raw-transaction-ingest-desk/tests/desktop_contract.rs +crates/ksp-app-raw-transaction-ingest-desk/tests/release_completeness.rs +docs/plans/036-V0_3_15_RAW_TRANSACTION_INGEST_DESK_PLAN.md +docs/validation/032-V0_3_15_RAW_TRANSACTION_INGEST_DESK.md +``` + +Suppressions : aucune. diff --git a/docs/plans/036-V0_3_15_RAW_TRANSACTION_INGEST_DESK_PLAN.md b/docs/plans/036-V0_3_15_RAW_TRANSACTION_INGEST_DESK_PLAN.md index f091293..5992274 100644 --- a/docs/plans/036-V0_3_15_RAW_TRANSACTION_INGEST_DESK_PLAN.md +++ b/docs/plans/036-V0_3_15_RAW_TRANSACTION_INGEST_DESK_PLAN.md @@ -1,5 +1,5 @@ - + # Plan v0.3.15 — Raw Transaction Ingest Desk @@ -823,7 +823,7 @@ Budget cible : une tranche observabilité. ### `pre.011` — frontend fonctionnel et tracing TypeScript -**État : implémenté.** Le frontend consomme maintenant `ksp-raw-ingest-route-status` et `get_route_monitoring` sans polling métier ni seconde logique d'acquisition. Les latest values mettent à jour les cartes de routes, les états actifs/terminaux, le verrouillage du sélecteur réseau et l'indicateur du Store partagé. +**État : implémenté, puis durci par `pre.011-fix.001`.** Le frontend consomme maintenant `ksp-raw-ingest-route-status` et `get_route_monitoring` sans polling métier ni seconde logique d'acquisition. Les latest values mettent à jour les cartes de routes, les états actifs/terminaux, le verrouillage du sélecteur réseau et l'indicateur du Store partagé. Le relais Tauri coalesce les snapshots à une cadence maximale d'une émission toutes les `500 ms` par route, et les mises à jour steady-state patchent les champs de carte en place au lieu de reconstruire continuellement les contrôles interactifs. Chaque route disposant d'un snapshot affiche un résumé source-neutral (`health`, `activity`, persisted, sources actives, gaps). Le contrôle `Supervision` ouvre un détail borné couvrant pipeline/admission/persistence, entity/observation outcomes, backpressure, hydration/pending/frontier, source lifecycle/reconnect/replay, continuité, gaps et compteurs de repair. Les slots/séquences/compteurs `u64` restent affichés comme texte décimal exact ; le frontend ne parse pas ces valeurs en `number`. diff --git a/docs/validation/032-V0_3_15_RAW_TRANSACTION_INGEST_DESK.md b/docs/validation/032-V0_3_15_RAW_TRANSACTION_INGEST_DESK.md index 267eb56..dd52ee1 100644 --- a/docs/validation/032-V0_3_15_RAW_TRANSACTION_INGEST_DESK.md +++ b/docs/validation/032-V0_3_15_RAW_TRANSACTION_INGEST_DESK.md @@ -1,5 +1,5 @@ - + # Validation v0.3.15 — Raw Transaction Ingest Desk @@ -443,6 +443,20 @@ La consommation UI détaillée de ces snapshots et le tracing TypeScript associ Frontend fonctionnel complet + tracing TypeScript sûr. +### `pre.011-fix.001` — réactivité du monitoring frontend + +Le gate statique de `pre.011` est propre, mais l'essai Mainnet révèle une saturation de l'interface dès qu'une route Yellowstone productive est active : le Worker continue à hydrater des blocs, alors qu'aucune seconde action Start/Stop n'atteint le tracing frontend après le démarrage. La cause est applicative : `monitor_route_status` relayait chaque changement de séquence Worker et `applyRouteMonitoring` reconstruisait toute la grille de routes pour chaque snapshot. Sur un flux transactionnel soutenu, les boutons Start/Stop étaient donc remplacés continuellement dans le DOM et pouvaient perdre leur cible entre les événements pointeur/click. + +Le fix conserve le contrat latest-value sans polling métier, mais borne le pont backend à une émission maximale toutes les `500 ms` par route. Le snapshot terminal reste projeté ; entre deux émissions, la source watch conserve naturellement la dernière valeur. Côté TypeScript, les snapshots steady-state patchent uniquement les champs monitoring de la carte existante et le détail sélectionné. Une reconstruction complète des cartes n'est effectuée que lorsqu'aucune carte patchable n'existe ou lorsque le lifecycle change, ce qui maintient les boutons Start/Stop stables pendant l'activité normale. + +Canari ajouté : + +```text +pre_011_fix_001_monitoring_bridge_is_bounded_and_steady_updates_preserve_route_controls +``` + +Ce correctif ne modifie ni Config, ni Transport, ni Store, ni Worker, ni Common RAW, ni les stratégies d'acquisition. + ### `pre.012` — hardening Races, shutdown app, sécurité IPC, hardening hostile/stale inventory.