From fa04bdbcf698c8fc5b21adbd9ee80cce9603ad01 Mon Sep 17 00:00:00 2001 From: SinuS Von SifriduS Date: Wed, 2 Sep 2026 21:10:07 +0200 Subject: [PATCH] v0.3.7-pre.013 --- Cargo.toml | 4 +- .../tests/config_composition.rs | 44 ++++++- .../tests/dependency_boundary.rs | 78 +++++++++++ .../tests/desktop_contract.rs | 30 ++++- .../tests/desktop_security.rs | 30 ++++- .../tests/release_completeness.rs | 121 ++++++++++++++++++ deltas/0.3.7/pre.013.md | 91 +++++++++++++ docs/plans/028-V0_3_7_BACKFILL_DESK_PLAN.md | 6 +- docs/validation/024-V0_3_7_BACKFILL_DESK.md | 22 +++- 9 files changed, 417 insertions(+), 9 deletions(-) create mode 100644 crates/ksp-app-backfill-desk/tests/dependency_boundary.rs create mode 100644 crates/ksp-app-backfill-desk/tests/release_completeness.rs create mode 100644 deltas/0.3.7/pre.013.md diff --git a/Cargo.toml b/Cargo.toml index 0d2cac8..ed85dfc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ # file: Cargo.toml -# version: 437 +# version: 438 [workspace] resolver = "3" members = ["crates/ksp-app-backfill-desk", "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-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-store-api", "crates/ksp-store-lib", "crates/ksp-store-postgres-lib", "crates/ksp-wallet-lib"] [workspace.package] -version = "0.3.7-pre.12.fix.1" +version = "0.3.7-pre.13" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" diff --git a/crates/ksp-app-backfill-desk/tests/config_composition.rs b/crates/ksp-app-backfill-desk/tests/config_composition.rs index a0feaa7..d2bee59 100644 --- a/crates/ksp-app-backfill-desk/tests/config_composition.rs +++ b/crates/ksp-app-backfill-desk/tests/config_composition.rs @@ -1,5 +1,5 @@ // file: crates/ksp-app-backfill-desk/tests/config_composition.rs -// version: 6 +// version: 7 //! Config composite, Transport readiness and Store-network composition contracts for Backfill Desk through pre.006. @@ -264,3 +264,45 @@ fn pre_006_default_mainnet_profile_exposes_pool_and_targeted_http_routes_without assert!(roles.contains("backfill_publicnode")); assert!(roles.contains("backfill_solana_public")); } + +#[test] +fn pre_013_composite_profile_and_component_inventory_is_exact() { + let workspace = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../.."); + let source = std::fs::read_to_string(workspace.join("config/composite.ksp-app-backfill-desk.json")); + assert!(source.is_ok(), "Backfill Desk composite should be readable"); + let source = match source { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + let document = serde_json::from_str::(source.as_str()); + assert!(document.is_ok(), "Backfill Desk composite should remain valid JSON"); + let document = match document { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + assert_eq!(document.get("default_profile").and_then(serde_json::Value::as_str), std::option::Option::Some("mainnet")); + let profiles = document.get("profiles").and_then(serde_json::Value::as_array); + assert!(profiles.is_some(), "Backfill Desk composite should expose profiles"); + let profiles = match profiles { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let profile_ids = profiles + .iter() + .filter_map(|profile| return profile.get("profile_id").and_then(serde_json::Value::as_str)) + .collect::>(); + assert_eq!(profile_ids, std::collections::BTreeSet::from(["devnet", "mainnet", "testnet"])); + for profile in profiles { + let documents = profile.get("documents").and_then(serde_json::Value::as_array); + assert!(documents.is_some(), "every Backfill Desk profile should expose component documents"); + let documents = match documents { + std::option::Option::Some(value) => value, + std::option::Option::None => continue, + }; + let component_ids = documents + .iter() + .filter_map(|document| return document.get("component_id").and_then(serde_json::Value::as_str)) + .collect::>(); + assert_eq!(component_ids, std::collections::BTreeSet::from(["logging", "store", "transport"])); + } +} diff --git a/crates/ksp-app-backfill-desk/tests/dependency_boundary.rs b/crates/ksp-app-backfill-desk/tests/dependency_boundary.rs new file mode 100644 index 0000000..cfb9cb0 --- /dev/null +++ b/crates/ksp-app-backfill-desk/tests/dependency_boundary.rs @@ -0,0 +1,78 @@ +// file: crates/ksp-app-backfill-desk/tests/dependency_boundary.rs +// version: 1 + +//! Final dependency-boundary checks for the KSP Backfill desktop application. + +#![forbid(unsafe_code)] +#![deny(unreachable_pub)] +#![warn(missing_docs)] + +fn app_root() -> std::path::PathBuf { + return std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); +} + +fn read_text(path: &std::path::Path) -> String { + let source = std::fs::read_to_string(path); + assert!(source.is_ok(), "unable to read {}", path.display()); + return match source { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => String::new(), + }; +} + +#[test] +fn pre_013_manifest_ksp_dependency_surface_is_exact_and_backend_neutral() { + let manifest = read_text(app_root().join("Cargo.toml").as_path()); + let dependency_section = manifest.split("[dependencies]").nth(1).and_then(|tail| return tail.split("[dev-dependencies]").next()); + assert!(dependency_section.is_some(), "Backfill Desk manifest should expose a dependency section"); + let dependency_section = match dependency_section { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let ksp_dependencies = dependency_section + .lines() + .filter_map(|line| { + let name = line.split('=').next().map(str::trim); + return match name { + std::option::Option::Some(value) if value.starts_with("ksp-") => std::option::Option::Some(value), + _ => std::option::Option::None, + }; + }) + .collect::>(); + assert_eq!( + ksp_dependencies, + std::collections::BTreeSet::from([ + "ksp-config-lib", + "ksp-core-lib", + "ksp-job-api", + "ksp-job-backfill-lib", + "ksp-logging-lib", + "ksp-onchain-transport-lib", + "ksp-store-lib", + ]) + ); + for forbidden in ["ksp-store-api", "ksp-store-postgres-lib", "tokio-postgres", "reqwest", "tonic", "yellowstone-grpc-proto", "mpl-token-metadata"] { + assert!(!dependency_section.contains(forbidden), "Backfill Desk manifest owns forbidden dependency {forbidden}"); + } +} + +#[test] +fn pre_013_production_sources_do_not_cross_into_physical_store_or_provider_clients() { + let source_root = app_root().join("src"); + let entries = std::fs::read_dir(source_root.as_path()); + assert!(entries.is_ok(), "Backfill Desk source directory should be readable"); + let entries = match entries { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + for entry in entries.flatten() { + let path = entry.path(); + if path.extension().and_then(std::ffi::OsStr::to_str) != std::option::Option::Some("rs") { + continue; + } + let source = read_text(path.as_path()); + for forbidden in ["ksp_store_api", "ksp_store_postgres_lib", "tokio_postgres", "reqwest::", "tonic::", "yellowstone_grpc_proto"] { + assert!(!source.contains(forbidden), "{} crosses forbidden dependency boundary via {forbidden}", path.display()); + } + } +} diff --git a/crates/ksp-app-backfill-desk/tests/desktop_contract.rs b/crates/ksp-app-backfill-desk/tests/desktop_contract.rs index fff0125..529f268 100644 --- a/crates/ksp-app-backfill-desk/tests/desktop_contract.rs +++ b/crates/ksp-app-backfill-desk/tests/desktop_contract.rs @@ -1,5 +1,5 @@ // file: crates/ksp-app-backfill-desk/tests/desktop_contract.rs -// version: 14 +// version: 15 //! Structural desktop contract checks for the Backfill Desk scaffold. @@ -434,3 +434,31 @@ fn pre_012_free_address_autocomplete_is_html_datalist_derived_from_core_registry assert!(!html.contains(forbidden), "HTML hardcodes canonical Program ID {forbidden}"); } } + +#[test] +fn pre_013_final_v1_command_and_frontend_control_inventory_is_exact() { + let root = app_root(); + let tauri = read_text(root.join("src/tauri.rs").as_path()); + let commands = [ + "backfill_cancel", + "backfill_options", + "backfill_resume", + "backfill_start", + "backfill_status", + "backfill_validate_request", + "emit_frontend_log", + "get_runtime_status", + "splash_frontend_ready", + ]; + for command in commands { + assert!(tauri.contains(format!("fn {command}").as_str()), "missing final Tauri command {command}"); + } + assert_eq!(tauri.matches("#[tauri::command]").count(), commands.len()); + for forbidden in ["backfill_reset", "backfill_delete", "backfill_retry", "backfill_schedule", "backfill_history"] { + assert!(!tauri.contains(forbidden), "unexpected V1 Tauri command marker {forbidden}"); + } + let frontend = read_text(root.join("frontend/main.html").as_path()); + for required in ["backfillStart", "backfillCancel", "backfillResume", "backfillRefresh", "backfillProgramIds"] { + assert!(frontend.contains(required), "missing final frontend control {required}"); + } +} diff --git a/crates/ksp-app-backfill-desk/tests/desktop_security.rs b/crates/ksp-app-backfill-desk/tests/desktop_security.rs index 76d09cc..6c849ba 100644 --- a/crates/ksp-app-backfill-desk/tests/desktop_security.rs +++ b/crates/ksp-app-backfill-desk/tests/desktop_security.rs @@ -1,5 +1,5 @@ // file: crates/ksp-app-backfill-desk/tests/desktop_security.rs -// version: 15 +// version: 16 //! Security and dependency-boundary checks for the Backfill Desk scaffold. @@ -365,3 +365,31 @@ fn pre_012_program_id_autocomplete_exposes_only_public_registry_metadata_and_no_ assert!(selection_source.contains("registryMatch")); assert!(!selection_source.contains("address.value")); } + +#[test] +fn pre_013_final_frontend_and_ipc_surface_keeps_physical_and_checkpoint_material_backend_owned() { + let root = app_root(); + let frontend = read_text(root.join("frontend/ts/main.ts").as_path()); + for forbidden in ["http://", "https://", "localStorage", "sessionStorage", "indexedDB", "document.cookie", "privateKey", "secretKey"] { + assert!(!frontend.contains(forbidden), "final frontend contains forbidden marker {forbidden}"); + } + let tauri = read_text(root.join("src/tauri.rs").as_path()); + for forbidden in ["BackfillCheckpoint", "HttpTransportPool", "ksp_store_postgres_lib", "tokio_postgres", "reqwest", "tonic"] { + assert!(!tauri.contains(forbidden), "Tauri IPC shell owns forbidden physical/runtime marker {forbidden}"); + } + let dto = read_text(root.join("src/dto_backfill.rs").as_path()); + for marker in ["BackfillStartResponseDto", "BackfillCancelResponseDto", "BackfillResumeResponseDto"] { + let source = struct_source(dto.as_str(), marker); + assert!(!source.is_empty(), "missing DTO {marker}"); + for forbidden in [ + "pub(crate) checkpoint:", + "pub(crate) endpoint:", + "pub(crate) provider:", + "pub(crate) credential:", + "pub(crate) token:", + "pub(crate) payload:", + ] { + assert!(!source.contains(forbidden), "DTO {marker} leaks forbidden field {forbidden}"); + } + } +} diff --git a/crates/ksp-app-backfill-desk/tests/release_completeness.rs b/crates/ksp-app-backfill-desk/tests/release_completeness.rs new file mode 100644 index 0000000..54aeab4 --- /dev/null +++ b/crates/ksp-app-backfill-desk/tests/release_completeness.rs @@ -0,0 +1,121 @@ +// file: crates/ksp-app-backfill-desk/tests/release_completeness.rs +// version: 1 + +//! Final V1 completeness canaries for the KSP Backfill desktop application. + +#![forbid(unsafe_code)] +#![deny(unreachable_pub)] +#![warn(missing_docs)] + +fn app_root() -> std::path::PathBuf { + return std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); +} + +fn read_text(path: &std::path::Path) -> String { + let source = std::fs::read_to_string(path); + assert!(source.is_ok(), "unable to read {}", path.display()); + return match source { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => String::new(), + }; +} + +#[test] +fn pre_013_production_module_inventory_is_exact() { + let lib = read_text(app_root().join("src/lib.rs").as_path()); + let modules = lib + .lines() + .filter_map(|line| { + let line = line.trim(); + if !line.starts_with("mod ") || !line.ends_with(';') { + return std::option::Option::None; + } + return std::option::Option::Some(line.trim_start_matches("mod ").trim_end_matches(';')); + }) + .collect::>(); + assert_eq!( + modules, + std::collections::BTreeSet::from([ + "app_state", + "backfill_request", + "backfill_run", + "backfill_status", + "bootstrap", + "constants", + "dto_backfill", + "dto_common", + "errors", + "frontend_logging", + "logging_runtime", + "splash", + "store_runtime", + "tauri", + "transport_runtime", + "tw_main", + "tw_splash", + ]) + ); +} + +#[test] +fn pre_013_integration_suite_inventory_closes_v1_contracts() { + let tests_root = app_root().join("tests"); + let entries = std::fs::read_dir(tests_root.as_path()); + assert!(entries.is_ok(), "Backfill Desk tests directory should be readable"); + let entries = match entries { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + let files = entries + .flatten() + .filter_map(|entry| { + let path = entry.path(); + if path.extension().and_then(std::ffi::OsStr::to_str) != std::option::Option::Some("rs") { + return std::option::Option::None; + } + return path.file_name().and_then(std::ffi::OsStr::to_str).map(str::to_owned); + }) + .collect::>(); + assert_eq!( + files, + std::collections::BTreeSet::from([ + "config_composition.rs".to_owned(), + "dependency_boundary.rs".to_owned(), + "desktop_contract.rs".to_owned(), + "desktop_security.rs".to_owned(), + "public_api.rs".to_owned(), + "release_completeness.rs".to_owned(), + ]) + ); +} + +#[test] +fn pre_013_final_surface_contains_every_planned_v1_capability_without_deferred_extensions() { + let tauri = read_text(app_root().join("src/tauri.rs").as_path()); + for required in ["backfill_start", "backfill_status", "backfill_cancel", "backfill_resume"] { + assert!(tauri.contains(required), "missing final V1 capability marker {required}"); + } + let options = read_text(app_root().join("src/dto_common.rs").as_path()); + assert!(options.contains("program_id_options")); + assert!(options.contains("program_id_autocomplete_options")); + let status = read_text(app_root().join("src/backfill_status.rs").as_path()); + for required in ["checkpoint_present", "contiguous_completed", "failure_code", "failure_domain"] { + assert!(status.contains(required), "missing final monitoring marker {required}"); + } + for forbidden in ["Grpc", "Yellowstone", "WebSocketBackfill", "scheduler", "job_history"] { + assert!(!tauri.contains(forbidden), "deferred capability leaked into V1 Tauri surface via {forbidden}"); + } +} + +#[test] +fn pre_013_shared_autocomplete_dto_remains_exported_and_consumed_through_crate_root() { + let root = app_root(); + let lib = read_text(root.join("src/lib.rs").as_path()); + assert!(lib.contains("pub(crate) use self::dto_common::ProgramIdAutocompleteOptionDto;")); + let dto = read_text(root.join("src/dto_common.rs").as_path()); + assert!(dto.contains("pub(crate) struct ProgramIdAutocompleteOptionDto")); + assert!(dto.contains("std::vec::Vec")); + assert!(dto.contains("crate::ProgramIdAutocompleteOptionDto {")); + let bare_construction = dto.matches("\n ProgramIdAutocompleteOptionDto {").count(); + assert_eq!(bare_construction, 0, "shared autocomplete DTO must not bypass the crate-root facade"); +} diff --git a/deltas/0.3.7/pre.013.md b/deltas/0.3.7/pre.013.md new file mode 100644 index 0000000..b031019 --- /dev/null +++ b/deltas/0.3.7/pre.013.md @@ -0,0 +1,91 @@ + + + +# Delta `0.3.7-pre.013` — hardening et complétude Backfill Desk V1 + +## 1. Base requise + +```text +0.3.7-pre.012-fix.001 +workspace.package.version = 0.3.7-pre.12.fix.1 +``` + +## 2. Objectif + +Fermer la surface fonctionnelle V1 de Backfill Desk par des canaries de dépendances, composition, desktop/security et release completeness, sans ajouter de capacité métier ni modifier le runtime. + +## 3. Hardening ajouté + +Deux suites d'intégration dédiées sont ajoutées : + +- `dependency_boundary.rs` : dépendances KSP exactes et interdiction des backends/clients physiques ; +- `release_completeness.rs` : inventaire exact des modules/tests et présence des capacités V1 finales sans extensions différées. + +Les suites existantes sont renforcées : + +- `config_composition.rs` verrouille les trois profils et les trois composants exacts ; +- `desktop_contract.rs` verrouille les neuf commandes Tauri et les contrôles frontend V1 ; +- `desktop_security.rs` ajoute un scan final des matériaux physiques/checkpoint/browser storage ; +- la discipline crate-root du DTO autocomplete partagé est canariée explicitement. + +## 4. Invariants fermés + +```text +single active run +HTTP-only Backfill V1 +backend-neutral Store facade +no physical provider/store client in app production +no checkpoint payload through IPC +no browser persistence +mainnet default + devnet/testnet configured +free Program ID autocomplete from ksp-core-lib +Start + monitoring + Cancel + Resume in-session +``` + +## 5. Hors périmètre + +Aucun runtime Job/Store/Transport, DTO/IPC, frontend, Config ou dépendance/feature n'est modifié. Aucun gRPC/WS Backfill, scheduler, history, retry Job, reset ou checkpoint durable n'est ajouté. + +## 6. Version + +```text +workspace.package.version = 0.3.7-pre.13 +label = 0.3.7-pre.013 +``` + +## 7. Fichiers ajoutés + +```text +crates/ksp-app-backfill-desk/tests/dependency_boundary.rs +crates/ksp-app-backfill-desk/tests/release_completeness.rs +deltas/0.3.7/pre.013.md +``` + +## 8. Fichiers modifiés + +```text +Cargo.toml +crates/ksp-app-backfill-desk/tests/config_composition.rs +crates/ksp-app-backfill-desk/tests/desktop_contract.rs +crates/ksp-app-backfill-desk/tests/desktop_security.rs +docs/plans/028-V0_3_7_BACKFILL_DESK_PLAN.md +docs/validation/024-V0_3_7_BACKFILL_DESK.md +``` + +## 9. Fichiers supprimés + +Aucun. + +## 10. Gate après application + +```text +cargo fmt --all +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.7 +cargo check --workspace +cargo clippy --workspace --all-targets +cargo test -p ksp-job-backfill-lib +cargo test -p ksp-app-backfill-desk +``` + +Aucun `cargo tree` requis : aucune dépendance ni feature Cargo n'est modifiée. diff --git a/docs/plans/028-V0_3_7_BACKFILL_DESK_PLAN.md b/docs/plans/028-V0_3_7_BACKFILL_DESK_PLAN.md index 1d289f8..7231b78 100644 --- a/docs/plans/028-V0_3_7_BACKFILL_DESK_PLAN.md +++ b/docs/plans/028-V0_3_7_BACKFILL_DESK_PLAN.md @@ -1,5 +1,5 @@ - + # Plan v0.3.7 — Backfill Desk @@ -296,7 +296,9 @@ La tranche matérialise ce contrat via `ProgramIdAutocompleteOptionDto`, ajouté ### pre.013 — hardening et complétude -Tests desktop/security/dependency/composition/release completeness, canaries DTO et scan des dépendances/interdictions. +Aucune nouvelle capacité métier. La tranche ferme la surface V1 par des canaries dédiés : inventaire exact des dépendances KSP et interdictions des backends/clients physiques, inventaire exact des modules de production et suites d’intégration, surface Tauri/contrôles frontend finaux, composition Config exacte sur `devnet`/`mainnet`/`testnet`, scan agrégé de sécurité IPC/frontend et contrôle de la discipline crate-root pour les DTO `pub(crate)` partagés introduits par la Desk. + +Les nouvelles suites `dependency_boundary.rs` et `release_completeness.rs` complètent `desktop_contract.rs`, `desktop_security.rs` et `config_composition.rs`. Elles ne modifient ni runtime Job, ni Store, ni Transport, ni DTO/IPC, ni frontend. `pre.014` reste propriétaire du gate technique final workspace/Tauri build. ### pre.014 — gate technique final diff --git a/docs/validation/024-V0_3_7_BACKFILL_DESK.md b/docs/validation/024-V0_3_7_BACKFILL_DESK.md index a588e2e..09872b6 100644 --- a/docs/validation/024-V0_3_7_BACKFILL_DESK.md +++ b/docs/validation/024-V0_3_7_BACKFILL_DESK.md @@ -1,5 +1,5 @@ - + # Validation v0.3.7 — Backfill Desk @@ -461,7 +461,7 @@ Le correctif réutilise uniquement les helpers déjà possédés par chaque fich - [X] aucun fichier de production ou Config n'est modifié ; - [X] version Cargo synchronisée en `0.3.7-pre.9.fix.1` ; - [X] audits statiques Rust/Markdown rejoués dans l'environnement d'assemblage ; -- [ ] replay opérateur `cargo fmt/check/clippy/test` du fix à exécuter ; aucun `cargo tree` requis car dépendances/features inchangées. +- [X] replay opérateur `cargo fmt/check/clippy/test` du fix propre : aucun warning, 50 tests unitaires Job Backfill et toutes les suites Backfill Desk passent ; aucun `cargo tree` requis car dépendances/features inchangées. ## 23. Correctif `pre.009-fix.002` — canari monitoring borné aux champs sérialisés @@ -557,3 +557,21 @@ Le correctif conserve donc le réexport et normalise uniquement les trois usages - [X] audits statiques Rust/Markdown rejoués dans l'environnement d'assemblage ; - [ ] replay opérateur `cargo fmt/check/clippy/test` du fix à exécuter ; aucun `cargo tree` requis car dépendances/features inchangées. +## 27. `pre.013` — hardening et complétude V1 + +Le gate opérateur de `pre.012-fix.001` ferme la tranche frontend : audits Rust/Markdown, `cargo check --workspace`, Clippy, les 50 tests unitaires `ksp-job-backfill-lib` et toutes les suites Backfill Desk passent sans warning. `pre.013` n'ouvre donc aucune capacité et ajoute uniquement des canaries de clôture V1. + +### Gate statique local `pre.013` + +- [X] suite `dependency_boundary.rs` ajoutée avec inventaire exact des dépendances KSP normales ; +- [X] `ksp-store-api`, backend PostgreSQL direct, `tokio-postgres`, `reqwest`, `tonic` et Yellowstone interdits dans le manifest/app production ; +- [X] suite `release_completeness.rs` ajoutée avec inventaire exact des 17 modules de production et des 6 suites d’intégration ; +- [X] surface Tauri finale bornée à 9 commandes, sans reset/retry/scheduler/history V1 ; +- [X] contrôles frontend finaux Start/Cancel/Resume/Refresh/autocomplete présents ; +- [X] scan sécurité agrégé : aucune URL physique, browser storage, checkpoint concret ou client/backend physique dans IPC/frontend ; +- [X] composite final borné à `devnet`, `mainnet`, `testnet`, avec exactement `logging`, `transport`, `store` ; +- [X] `ProgramIdAutocompleteOptionDto` reste réexporté et consommé via `crate::ProgramIdAutocompleteOptionDto` ; +- [X] aucun code runtime, DTO/IPC, frontend, Config, Store, Transport ou Job modifié ; +- [X] aucune dépendance/feature Cargo ajoutée ; +- [X] audits statiques Rust/Markdown propres dans l'environnement d'assemblage ; +- [ ] replay opérateur `cargo fmt/check/clippy/test` de `pre.013` à exécuter avant le gate technique final `pre.014`.