diff --git a/Cargo.toml b/Cargo.toml index c54fcc9..e39c189 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ # file: Cargo.toml -# version: 417 +# version: 418 [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.2" +version = "0.3.7-pre.3" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" diff --git a/config/composite.ksp-app-backfill-desk.json b/config/composite.ksp-app-backfill-desk.json new file mode 100644 index 0000000..11a2914 --- /dev/null +++ b/config/composite.ksp-app-backfill-desk.json @@ -0,0 +1,66 @@ +{ + "format_version": 1, + "default_profile": "devnet", + "profiles": [ + { + "profile_id": "devnet", + "documents": [ + { + "component_id": "logging", + "file_id": "cfg.std.logging", + "profile_id": "supertrace" + }, + { + "component_id": "transport", + "file_id": "cfg.std.transport", + "profile_id": "devnet_public" + }, + { + "component_id": "store", + "file_id": "cfg.std.store", + "profile_id": "devnet" + } + ] + }, + { + "profile_id": "mainnet", + "documents": [ + { + "component_id": "logging", + "file_id": "cfg.std.logging", + "profile_id": "console_info" + }, + { + "component_id": "transport", + "file_id": "cfg.std.transport", + "profile_id": "mainnet_public" + }, + { + "component_id": "store", + "file_id": "cfg.std.store", + "profile_id": "mainnet" + } + ] + }, + { + "profile_id": "testnet", + "documents": [ + { + "component_id": "logging", + "file_id": "cfg.std.logging", + "profile_id": "console_info" + }, + { + "component_id": "transport", + "file_id": "cfg.std.transport", + "profile_id": "publicnode_testnet" + }, + { + "component_id": "store", + "file_id": "cfg.std.store", + "profile_id": "testnet" + } + ] + } + ] +} diff --git a/crates/ksp-app-backfill-desk/capabilities/default.json b/crates/ksp-app-backfill-desk/capabilities/default.json index f44cb76..a775db2 100644 --- a/crates/ksp-app-backfill-desk/capabilities/default.json +++ b/crates/ksp-app-backfill-desk/capabilities/default.json @@ -10,4 +10,4 @@ "core:default", "tracing:default" ] -} +} \ No newline at end of file diff --git a/crates/ksp-app-backfill-desk/src/bootstrap.rs b/crates/ksp-app-backfill-desk/src/bootstrap.rs index 3595cfb..bb37360 100644 --- a/crates/ksp-app-backfill-desk/src/bootstrap.rs +++ b/crates/ksp-app-backfill-desk/src/bootstrap.rs @@ -1,7 +1,7 @@ // file: crates/ksp-app-backfill-desk/src/bootstrap.rs -// version: 1 +// version: 2 -//! Config and Logging bootstrap for the Backfill Desk scaffold. +//! Config composite and Logging bootstrap for Backfill Desk. /// Crate-internal Logging startup state shared by the application state. pub(crate) struct LoggingStartup { @@ -43,7 +43,48 @@ pub(crate) fn config_management(arguments: &[std::ffi::OsString]) -> ksp_core_li return std::result::Result::Ok(ksp_config_lib::ConfigManagement::new(engine)); } -/// Initializes Logging from the standard managed Config document, with the common Desk fallback policy. +/// Loads the concrete Backfill Desk Config composite using its registered logical `file_id` and autonomous default profile. +pub(crate) fn load_backfill_desk_composite(management: &ksp_config_lib::ConfigManagement) -> ksp_core_lib::Result { + let file_id = ksp_config_lib::ConfigFileId::new(ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_BACKFILL_DESK); + let file_id = match file_id { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return management.engine().load_resolved_composite(&file_id, std::option::Option::None); +} + +/// Returns one required standard profile from the Backfill Desk composite after validating the referenced logical `file_id`. +pub(crate) fn required_composite_component_profile( + composite: &ksp_config_lib::ResolvedConfigComposite, + component_id: &str, + expected_file_id: &str, +) -> ksp_core_lib::Result { + let component = composite.component(component_id); + let component = match component { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + ksp_core_lib::Error::new(crate::ERROR_CODE_CONFIG_COMPOSITE_INVALID, "Backfill Desk composite is missing a required component") + .with_context("composite_file_id", composite.file_id().as_str()) + .with_context("composite_profile_id", composite.profile_id()) + .with_context("component_id", component_id), + ); + }, + }; + if component.resolved().file_id().as_str() != expected_file_id { + return std::result::Result::Err( + ksp_core_lib::Error::new(crate::ERROR_CODE_CONFIG_COMPOSITE_INVALID, "Backfill Desk composite component references an unexpected Config document") + .with_context("composite_file_id", composite.file_id().as_str()) + .with_context("composite_profile_id", composite.profile_id()) + .with_context("component_id", component_id) + .with_context("expected_file_id", expected_file_id) + .with_context("actual_file_id", component.resolved().file_id().as_str()), + ); + } + return std::result::Result::Ok(component.resolved().clone()); +} + +/// Initializes Logging from the Backfill Desk composite, with a trace-level development fallback. pub(crate) fn initialize_logging( management: &ksp_config_lib::ConfigManagement, runtime_identity: &ksp_logging_lib::LoggingRuntimeIdentity, @@ -58,7 +99,7 @@ pub(crate) fn initialize_logging( target: crate::TRACING_TARGET, domain = crate::TRACING_DOMAIN_BOOTSTRAP, active_profile = active_profile_id.as_str(), - "initialized Backfill Desk logging from managed configuration" + "initialized Backfill Desk logging from composite-managed configuration" ); std::result::Result::Ok(LoggingStartup { guard, @@ -78,7 +119,7 @@ pub(crate) fn initialize_logging( fn fallback_logging_settings() -> ksp_logging_lib::LoggingSettings { return ksp_logging_lib::LoggingSettings::new( - ksp_logging_lib::LogFilterLevel::Info, + ksp_logging_lib::LogFilterLevel::Trace, ksp_logging_lib::SpanEvents::Off, std::option::Option::Some(ksp_logging_lib::ConsoleSettings::stderr()), std::vec::Vec::new(), @@ -91,7 +132,26 @@ fn resolve_logging_startup(management: &ksp_config_lib::ConfigManagement) -> Log std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return fallback_startup_plan(error), }; - let resolved = management.engine().load_resolved_logging_config(std::option::Option::None, &environment); + let composite = crate::load_backfill_desk_composite(management); + let composite = match composite { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return fallback_startup_plan(error), + }; + let logging_profile = crate::required_composite_component_profile(&composite, crate::COMPOSITE_COMPONENT_ID_LOGGING, ksp_config_lib::FILE_ID_STD_LOGGING); + let logging_profile = match logging_profile { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return fallback_startup_plan(error), + }; + let transport_profile = + crate::required_composite_component_profile(&composite, crate::COMPOSITE_COMPONENT_ID_TRANSPORT, ksp_config_lib::FILE_ID_STD_TRANSPORT); + if let std::result::Result::Err(error) = transport_profile { + return fallback_startup_plan(error); + } + let store_profile = crate::required_composite_component_profile(&composite, crate::COMPOSITE_COMPONENT_ID_STORE, ksp_config_lib::FILE_ID_STD_STORE); + if let std::result::Result::Err(error) = store_profile { + return fallback_startup_plan(error); + } + let resolved = management.engine().resolve_logging_config_profile(&logging_profile, &environment); let resolved = match resolved { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return fallback_startup_plan(error), @@ -135,7 +195,7 @@ fn initialize_planned_fallback_logging( domain = crate::TRACING_DOMAIN_BOOTSTRAP, error_domain = diagnostic.domain.as_str(), error_code = diagnostic.code.as_str(), - "managed Logging configuration is unavailable; using transient in-memory fallback" + "managed Logging configuration is unavailable; using transient trace-level development fallback" ); return std::result::Result::Ok(LoggingStartup { guard, @@ -144,3 +204,12 @@ fn initialize_planned_fallback_logging( startup_diagnostic: std::option::Option::Some(diagnostic), }); } + +#[cfg(test)] +mod tests { + #[test] + fn development_fallback_keeps_trace_level_enabled() { + let settings = super::fallback_logging_settings(); + assert_eq!(settings.default_filter(), ksp_logging_lib::LogFilterLevel::Trace); + } +} diff --git a/crates/ksp-app-backfill-desk/src/constants.rs b/crates/ksp-app-backfill-desk/src/constants.rs index af2cc21..0d3c3ef 100644 --- a/crates/ksp-app-backfill-desk/src/constants.rs +++ b/crates/ksp-app-backfill-desk/src/constants.rs @@ -1,8 +1,14 @@ // file: crates/ksp-app-backfill-desk/src/constants.rs -// version: 1 +// version: 2 //! Application-owned tracing targets and domains. +/// Composite component identifier for Logging. +pub(crate) const COMPOSITE_COMPONENT_ID_LOGGING: &str = "logging"; +/// Composite component identifier for Store. +pub(crate) const COMPOSITE_COMPONENT_ID_STORE: &str = "store"; +/// Composite component identifier for Transport. +pub(crate) const COMPOSITE_COMPONENT_ID_TRANSPORT: &str = "transport"; /// Structured domain used while bootstrapping Config and Logging. pub(crate) const TRACING_DOMAIN_BOOTSTRAP: &str = "backfill.bootstrap"; /// Structured domain used by technical frontend events. diff --git a/crates/ksp-app-backfill-desk/src/errors.rs b/crates/ksp-app-backfill-desk/src/errors.rs index 70607ec..6eac950 100644 --- a/crates/ksp-app-backfill-desk/src/errors.rs +++ b/crates/ksp-app-backfill-desk/src/errors.rs @@ -1,8 +1,10 @@ // file: crates/ksp-app-backfill-desk/src/errors.rs -// version: 1 +// version: 2 //! Application-local error codes for Backfill Desk composition and desktop runtime surfaces. +/// Backfill Desk composite configuration is missing or references an unexpected document. +pub(crate) const ERROR_CODE_CONFIG_COMPOSITE_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("backfill_desk", "config_composite_invalid"); /// Shared Backfill Desk runtime state is internally inconsistent. pub(crate) const ERROR_CODE_APP_STATE_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("backfill_desk", "app_state_invalid"); /// Shared Backfill Desk runtime state cannot be locked safely. diff --git a/crates/ksp-app-backfill-desk/src/lib.rs b/crates/ksp-app-backfill-desk/src/lib.rs index 23d11b5..58546be 100644 --- a/crates/ksp-app-backfill-desk/src/lib.rs +++ b/crates/ksp-app-backfill-desk/src/lib.rs @@ -1,5 +1,5 @@ // file: crates/ksp-app-backfill-desk/src/lib.rs -// version: 1 +// version: 2 //! Tauri desktop application scaffold for controlling and inspecting KSP RAW backfill jobs. @@ -28,8 +28,18 @@ pub(crate) use self::app_state::AppState; pub(crate) use self::bootstrap::LoggingStartup; /// Builds the Config management facade from the common KSP CLI bootstrap contract. pub(crate) use self::bootstrap::config_management; -/// Initializes the scaffold Logging runtime from standard Config, with a bounded in-memory fallback. +/// Initializes Logging from the Backfill Desk composite, with a trace-level development fallback. pub(crate) use self::bootstrap::initialize_logging; +/// Loads the concrete Backfill Desk Config composite. +pub(crate) use self::bootstrap::load_backfill_desk_composite; +/// Returns one required component profile from the Backfill Desk composite. +pub(crate) use self::bootstrap::required_composite_component_profile; +/// Composite component identifier for Logging. +pub(crate) use self::constants::COMPOSITE_COMPONENT_ID_LOGGING; +/// Composite component identifier for Store. +pub(crate) use self::constants::COMPOSITE_COMPONENT_ID_STORE; +/// Composite component identifier for Transport. +pub(crate) use self::constants::COMPOSITE_COMPONENT_ID_TRANSPORT; /// Structured domain used while bootstrapping Config and Logging. pub(crate) use self::constants::TRACING_DOMAIN_BOOTSTRAP; /// Structured domain used by technical frontend events. @@ -54,6 +64,8 @@ pub(crate) use self::dto_common::ShellStatusDto; pub(crate) use self::errors::ERROR_CODE_APP_STATE_INVALID; /// Shared Backfill Desk runtime state cannot be locked safely. pub(crate) use self::errors::ERROR_CODE_APP_STATE_LOCK_FAILED; +/// Backfill Desk composite configuration is missing or references an unexpected document. +pub(crate) use self::errors::ERROR_CODE_CONFIG_COMPOSITE_INVALID; /// Frontend logging requested an unsupported level. pub(crate) use self::errors::ERROR_CODE_FRONTEND_LOG_LEVEL_INVALID; /// Frontend logging requested a target outside the application whitelist. diff --git a/crates/ksp-app-backfill-desk/src/tauri.rs b/crates/ksp-app-backfill-desk/src/tauri.rs index 28c63cb..68e89bf 100644 --- a/crates/ksp-app-backfill-desk/src/tauri.rs +++ b/crates/ksp-app-backfill-desk/src/tauri.rs @@ -1,11 +1,16 @@ // file: crates/ksp-app-backfill-desk/src/tauri.rs -// version: 1 +// version: 2 //! Tauri runtime assembly for the KSP Backfill desktop application. /// Runs the Backfill desktop application. #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run(arguments: &[std::ffi::OsString]) -> ksp_core_lib::Result<()> { + let context = tauri::generate_context!(); + let runtime_layout = configure_packaged_runtime(&context); + if let std::result::Result::Err(error) = runtime_layout { + return std::result::Result::Err(error); + } let app_state = crate::AppState::initialize(arguments); let app_state = match app_state { std::result::Result::Ok(value) => value, @@ -16,7 +21,7 @@ pub fn run(arguments: &[std::ffi::OsString]) -> ksp_core_lib::Result<()> { builder = configure_plugins(builder); builder = configure_commands(builder); builder = configure_setup(builder); - let run_result = builder.run(tauri::generate_context!()); + let run_result = builder.run(context); return match run_result { std::result::Result::Ok(()) => std::result::Result::Ok(()), std::result::Result::Err(error) => std::result::Result::Err( @@ -26,6 +31,36 @@ pub fn run(arguments: &[std::ffi::OsString]) -> ksp_core_lib::Result<()> { }; } +fn configure_packaged_runtime(context: &tauri::Context) -> ksp_core_lib::Result<()> { + if cfg!(debug_assertions) { + return std::result::Result::Ok(()); + } + let resource_root = tauri::utils::platform::resource_dir(context.package_info(), &tauri::Env::default()); + let resource_root = match resource_root { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err( + ksp_core_lib::Error::new(crate::ERROR_CODE_TAURI_RUNTIME_FAILED, "Cannot resolve packaged Tauri resource directory") + .with_context("tauri_error", error.to_string()), + ); + }, + }; + let layout = ksp_config_lib::prepare_packaged_runtime(resource_root.as_path()); + let layout = match layout { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let working_directory = std::env::set_current_dir(layout.runtime_root()); + return match working_directory { + std::result::Result::Ok(()) => std::result::Result::Ok(()), + std::result::Result::Err(error) => std::result::Result::Err( + ksp_core_lib::Error::new(crate::ERROR_CODE_TAURI_RUNTIME_FAILED, "Cannot activate packaged KSP runtime directory") + .with_context("runtime_root", layout.runtime_root().to_string_lossy().into_owned()) + .with_source(error), + ), + }; +} + fn configure_state(builder: tauri::Builder, app_state: crate::AppState) -> tauri::Builder { return builder.manage(app_state); } diff --git a/crates/ksp-app-backfill-desk/tauri.conf.json b/crates/ksp-app-backfill-desk/tauri.conf.json index d58ae8f..7d6bde4 100644 --- a/crates/ksp-app-backfill-desk/tauri.conf.json +++ b/crates/ksp-app-backfill-desk/tauri.conf.json @@ -50,6 +50,22 @@ "icon": [ "icons/favicon.png", "icons/favicon.ico" - ] + ], + "resources": { + "../../config/composite.ksp-app-backfill-desk.json": "config/composite.ksp-app-backfill-desk.json", + "../../config/composite.ksp-app-solprices-desk.json": "config/composite.ksp-app-solprices-desk.json", + "../../config/composite.ksp-app-wallet-desk.json": "config/composite.ksp-app-wallet-desk.json", + "../../config/std.logging.json": "config/std.logging.json", + "../../config/std.offchain_transport.json": "config/std.offchain_transport.json", + "../../config/std.store.json": "config/std.store.json", + "../../config/std.transport.json": "config/std.transport.json", + "../../config/std.wallet.json": "config/std.wallet.json", + "../../config/schemas/composite.schema.json": "config/schemas/composite.schema.json", + "../../config/schemas/std.logging.schema.json": "config/schemas/std.logging.schema.json", + "../../config/schemas/std.offchain_transport.schema.json": "config/schemas/std.offchain_transport.schema.json", + "../../config/schemas/std.store.schema.json": "config/schemas/std.store.schema.json", + "../../config/schemas/std.transport.schema.json": "config/schemas/std.transport.schema.json", + "../../config/schemas/std.wallet.schema.json": "config/schemas/std.wallet.schema.json" + } } } diff --git a/crates/ksp-app-backfill-desk/tests/config_composition.rs b/crates/ksp-app-backfill-desk/tests/config_composition.rs new file mode 100644 index 0000000..52867cd --- /dev/null +++ b/crates/ksp-app-backfill-desk/tests/config_composition.rs @@ -0,0 +1,88 @@ +// file: crates/ksp-app-backfill-desk/tests/config_composition.rs +// version: 1 + +//! Config composite contracts for Backfill Desk pre.003. + +#![forbid(unsafe_code)] +#![deny(unreachable_pub)] +#![warn(missing_docs)] + +fn workspace_engine() -> ksp_core_lib::Result { + let workspace = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../.."); + let bootstrap = ksp_config_lib::ConfigBootstrapOptions::from_paths(workspace.join("config"), workspace.join("config/schemas")); + let bootstrap = match bootstrap { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let registry = ksp_config_lib::ConfigFileRegistry::defaults(); + return match registry { + std::result::Result::Ok(value) => std::result::Result::Ok(ksp_config_lib::ConfigDocumentEngine::new(bootstrap, value)), + std::result::Result::Err(error) => std::result::Result::Err(error), + }; +} + +fn assert_component(composite: &ksp_config_lib::ResolvedConfigComposite, component_id: &str, file_id: &str, profile_id: &str) { + let component = composite.component(component_id); + assert!(component.is_some(), "missing component {component_id}"); + if let std::option::Option::Some(component) = component { + assert_eq!(component.resolved().file_id().as_str(), file_id); + assert_eq!(component.resolved().profile_id(), profile_id); + assert_eq!(component.resolved().selection_source(), ksp_config_lib::ConfigProfileSelectionSource::Composite); + } +} + +#[test] +fn pre_003_default_composite_selects_trace_devnet_logging_transport_and_store() { + let engine = workspace_engine(); + assert!(engine.is_ok(), "workspace Config engine should be constructible: {engine:?}"); + let engine = match engine { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + let file_id = ksp_config_lib::ConfigFileId::new(ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_BACKFILL_DESK); + assert!(file_id.is_ok(), "Backfill Desk composite file_id should be valid: {file_id:?}"); + let file_id = match file_id { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + let composite = engine.load_resolved_composite(&file_id, std::option::Option::None); + assert!(composite.is_ok(), "committed Backfill Desk composite should resolve: {composite:?}"); + if let std::result::Result::Ok(composite) = composite { + assert_eq!(composite.profile_id(), "devnet"); + assert_component(&composite, "logging", ksp_config_lib::FILE_ID_STD_LOGGING, "supertrace"); + assert_component(&composite, "transport", ksp_config_lib::FILE_ID_STD_TRANSPORT, "devnet_public"); + assert_component(&composite, "store", ksp_config_lib::FILE_ID_STD_STORE, "devnet"); + let logging = composite.component("logging"); + assert!(logging.is_some(), "Backfill Desk composite should expose Logging"); + if let std::option::Option::Some(logging) = logging { + assert_eq!(logging.resolved().profile().get("default_filter").and_then(serde_json::Value::as_str), std::option::Option::Some("trace")); + } + } +} + +#[test] +fn pre_003_named_profiles_keep_transport_and_store_network_selection_paired() { + let engine = workspace_engine(); + assert!(engine.is_ok(), "workspace Config engine should be constructible: {engine:?}"); + let engine = match engine { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + let file_id = ksp_config_lib::ConfigFileId::new(ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_BACKFILL_DESK); + let file_id = match file_id { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + for (profile_id, logging_profile, transport_profile, store_profile) in + [("mainnet", "console_info", "mainnet_public", "mainnet"), ("testnet", "console_info", "publicnode_testnet", "testnet")] + { + let composite = engine.load_resolved_composite(&file_id, std::option::Option::Some(profile_id)); + assert!(composite.is_ok(), "Backfill Desk composite profile {profile_id} should resolve: {composite:?}"); + if let std::result::Result::Ok(composite) = composite { + assert_eq!(composite.profile_id(), profile_id); + assert_component(&composite, "logging", ksp_config_lib::FILE_ID_STD_LOGGING, logging_profile); + assert_component(&composite, "transport", ksp_config_lib::FILE_ID_STD_TRANSPORT, transport_profile); + assert_component(&composite, "store", ksp_config_lib::FILE_ID_STD_STORE, store_profile); + } + } +} diff --git a/crates/ksp-app-backfill-desk/tests/desktop_contract.rs b/crates/ksp-app-backfill-desk/tests/desktop_contract.rs index d5c768d..72e1bb6 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: 1 +// version: 2 //! Structural desktop contract checks for the Backfill Desk scaffold. @@ -24,6 +24,16 @@ fn read_text(path: &std::path::Path) -> String { }; } +fn read_json(path: &std::path::Path) -> serde_json::Value { + let source = read_text(path); + let parsed = serde_json::from_str(source.as_str()); + assert!(parsed.is_ok(), "unable to parse {}", path.display()); + return match parsed { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => serde_json::Value::Null, + }; +} + fn read_bytes(path: &std::path::Path) -> std::vec::Vec { let source = std::fs::read(path); assert!(source.is_ok(), "unable to read {}", path.display()); @@ -119,3 +129,52 @@ fn pre_002_shell_and_splash_use_shared_frontend_tooling() { assert!(splash.contains("Splash opacity animation started")); assert!(splash.contains("Backfill Desk splash frontend loaded")); } + +#[test] +fn pre_003_composite_packaging_follows_atomic_config_registry_contract() { + let root = app_root(); + let config = read_json(root.join("tauri.conf.json").as_path()); + let resources = config.get("bundle").and_then(|value| value.get("resources")).and_then(serde_json::Value::as_object); + assert!(resources.is_some()); + let resources = match resources { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + assert_eq!(resources.len(), 14); + for required in [ + "../../config/composite.ksp-app-backfill-desk.json", + "../../config/composite.ksp-app-solprices-desk.json", + "../../config/composite.ksp-app-wallet-desk.json", + "../../config/std.logging.json", + "../../config/std.offchain_transport.json", + "../../config/std.store.json", + "../../config/std.transport.json", + "../../config/std.wallet.json", + "../../config/schemas/composite.schema.json", + "../../config/schemas/std.logging.schema.json", + "../../config/schemas/std.offchain_transport.schema.json", + "../../config/schemas/std.store.schema.json", + "../../config/schemas/std.transport.schema.json", + "../../config/schemas/std.wallet.schema.json", + ] { + assert!(resources.contains_key(required), "missing packaged Config resource {required}"); + } + let tauri = read_text(root.join("src/tauri.rs").as_path()); + assert!(tauri.contains("prepare_packaged_runtime")); + assert!(tauri.contains("resource_dir")); + assert!(tauri.contains("set_current_dir")); +} + +#[test] +fn pre_003_bootstrap_uses_composite_logging_without_opening_transport_or_store() { + let root = app_root(); + let bootstrap = read_text(root.join("src/bootstrap.rs").as_path()); + assert!(bootstrap.contains("FILE_ID_COMPOSITE_KSP_APP_BACKFILL_DESK")); + assert!(bootstrap.contains("COMPOSITE_COMPONENT_ID_LOGGING")); + assert!(bootstrap.contains("COMPOSITE_COMPONENT_ID_TRANSPORT")); + assert!(bootstrap.contains("COMPOSITE_COMPONENT_ID_STORE")); + assert!(bootstrap.contains("LogFilterLevel::Trace")); + for forbidden in ["HttpTransportPool", "Store::open", "BackfillJobRuntime", "BackfillRequest"] { + assert!(!bootstrap.contains(forbidden), "pre.003 opens later runtime surface {forbidden}"); + } +} diff --git a/crates/ksp-app-backfill-desk/tests/desktop_security.rs b/crates/ksp-app-backfill-desk/tests/desktop_security.rs index e13821c..3652192 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: 1 +// version: 2 //! Security and dependency-boundary checks for the Backfill Desk scaffold. @@ -113,3 +113,17 @@ fn pre_002_frontend_instrumentation_avoids_business_or_secret_payloads() { assert!(!main.contains(forbidden)); } } + +#[test] +fn pre_003_keeps_config_only_composition_boundary() { + let root = app_root(); + let manifest = read_text(root.join("Cargo.toml").as_path()); + assert!(manifest.contains("ksp-config-lib.workspace = true")); + for forbidden in ["ksp-job-backfill-lib", "ksp-onchain-transport-lib", "ksp-store-lib", "ksp-store-api", "ksp-store-postgres-lib"] { + assert!(!manifest.contains(forbidden), "pre.003 opens a later-layer dependency: {forbidden}"); + } + let bootstrap = read_text(root.join("src/bootstrap.rs").as_path()); + assert!(bootstrap.contains("LogFilterLevel::Trace")); + assert!(!bootstrap.contains("HttpTransportPool")); + assert!(!bootstrap.contains("Store::open")); +} diff --git a/crates/ksp-app-config-desk/tauri.conf.json b/crates/ksp-app-config-desk/tauri.conf.json index d733515..d4be766 100644 --- a/crates/ksp-app-config-desk/tauri.conf.json +++ b/crates/ksp-app-config-desk/tauri.conf.json @@ -52,6 +52,7 @@ "icons/favicon.ico" ], "resources": { + "../../config/composite.ksp-app-backfill-desk.json": "config/composite.ksp-app-backfill-desk.json", "../../config/composite.ksp-app-solprices-desk.json": "config/composite.ksp-app-solprices-desk.json", "../../config/composite.ksp-app-wallet-desk.json": "config/composite.ksp-app-wallet-desk.json", "../../config/std.logging.json": "config/std.logging.json", diff --git a/crates/ksp-app-config-desk/tests/desktop_contract.rs b/crates/ksp-app-config-desk/tests/desktop_contract.rs index 253dc48..765f598 100644 --- a/crates/ksp-app-config-desk/tests/desktop_contract.rs +++ b/crates/ksp-app-config-desk/tests/desktop_contract.rs @@ -1,5 +1,5 @@ // file: crates/ksp-app-config-desk/tests/desktop_contract.rs -// version: 11 +// version: 12 //! Desktop build/shell contract audits for Config Desk. @@ -110,7 +110,11 @@ fn pre_018_packaged_runtime_bundles_config_resources_and_activates_shared_writab let resources = tauri.pointer("/bundle/resources").and_then(serde_json::Value::as_object); assert!(resources.is_some(), "packaged Config resources map must exist"); if let std::option::Option::Some(resources) = resources { - assert_eq!(resources.len(), 13); + assert_eq!(resources.len(), 14); + assert_eq!( + resources.get("../../config/composite.ksp-app-backfill-desk.json").and_then(serde_json::Value::as_str), + std::option::Option::Some("config/composite.ksp-app-backfill-desk.json"), + ); assert_eq!( resources.get("../../config/composite.ksp-app-solprices-desk.json").and_then(serde_json::Value::as_str), std::option::Option::Some("config/composite.ksp-app-solprices-desk.json"), diff --git a/crates/ksp-app-solprices-desk/tauri.conf.json b/crates/ksp-app-solprices-desk/tauri.conf.json index ffe398e..aca4a3a 100644 --- a/crates/ksp-app-solprices-desk/tauri.conf.json +++ b/crates/ksp-app-solprices-desk/tauri.conf.json @@ -52,6 +52,7 @@ "icons/favicon.ico" ], "resources": { + "../../config/composite.ksp-app-backfill-desk.json": "config/composite.ksp-app-backfill-desk.json", "../../config/composite.ksp-app-solprices-desk.json": "config/composite.ksp-app-solprices-desk.json", "../../config/composite.ksp-app-wallet-desk.json": "config/composite.ksp-app-wallet-desk.json", "../../config/std.logging.json": "config/std.logging.json", diff --git a/crates/ksp-app-solprices-desk/tests/desktop_contract.rs b/crates/ksp-app-solprices-desk/tests/desktop_contract.rs index bf30b86..d3d34a8 100644 --- a/crates/ksp-app-solprices-desk/tests/desktop_contract.rs +++ b/crates/ksp-app-solprices-desk/tests/desktop_contract.rs @@ -1,5 +1,5 @@ // file: crates/ksp-app-solprices-desk/tests/desktop_contract.rs -// version: 10 +// version: 11 //! Desktop scaffold, shared-template and Config packaging contract audits for SOL Prices Desk `0.2.12`. @@ -85,13 +85,17 @@ fn pre_002_package_is_mixed_lib_bin_and_frontend_is_scaffold_only() { } #[test] -fn pre_004_packaging_contains_current_thirteen_config_resources() { +fn pre_004_packaging_contains_current_fourteen_config_resources() { let root = app_root(); let tauri = read_json(root.join("tauri.conf.json").as_path()); let resources = tauri.pointer("/bundle/resources").and_then(serde_json::Value::as_object); assert!(resources.is_some()); if let std::option::Option::Some(resources) = resources { - assert_eq!(resources.len(), 13); + assert_eq!(resources.len(), 14); + assert_eq!( + resources.get("../../config/composite.ksp-app-backfill-desk.json").and_then(serde_json::Value::as_str), + std::option::Option::Some("config/composite.ksp-app-backfill-desk.json"), + ); assert_eq!( resources.get("../../config/composite.ksp-app-solprices-desk.json").and_then(serde_json::Value::as_str), std::option::Option::Some("config/composite.ksp-app-solprices-desk.json"), diff --git a/crates/ksp-app-wallet-desk/tauri.conf.json b/crates/ksp-app-wallet-desk/tauri.conf.json index 50b3477..23b1907 100644 --- a/crates/ksp-app-wallet-desk/tauri.conf.json +++ b/crates/ksp-app-wallet-desk/tauri.conf.json @@ -52,6 +52,7 @@ "icons/favicon.ico" ], "resources": { + "../../config/composite.ksp-app-backfill-desk.json": "config/composite.ksp-app-backfill-desk.json", "../../config/composite.ksp-app-solprices-desk.json": "config/composite.ksp-app-solprices-desk.json", "../../config/composite.ksp-app-wallet-desk.json": "config/composite.ksp-app-wallet-desk.json", "../../config/std.logging.json": "config/std.logging.json", diff --git a/crates/ksp-app-wallet-desk/tests/desktop_contract.rs b/crates/ksp-app-wallet-desk/tests/desktop_contract.rs index 231c16a..199ca20 100644 --- a/crates/ksp-app-wallet-desk/tests/desktop_contract.rs +++ b/crates/ksp-app-wallet-desk/tests/desktop_contract.rs @@ -1,5 +1,5 @@ // file: crates/ksp-app-wallet-desk/tests/desktop_contract.rs -// version: 31 +// version: 32 //! Desktop build, shell and Config-status contract audits for Wallet Desk. @@ -432,7 +432,11 @@ fn pre_018_packaged_runtime_bundles_config_resources_and_keeps_wallet_desk_versi let resources = tauri.pointer("/bundle/resources").and_then(serde_json::Value::as_object); assert!(resources.is_some(), "packaged Wallet Desk Config resources map must exist"); if let std::option::Option::Some(resources) = resources { - assert_eq!(resources.len(), 13); + assert_eq!(resources.len(), 14); + assert_eq!( + resources.get("../../config/composite.ksp-app-backfill-desk.json").and_then(serde_json::Value::as_str), + std::option::Option::Some("config/composite.ksp-app-backfill-desk.json"), + ); assert_eq!( resources.get("../../config/composite.ksp-app-solprices-desk.json").and_then(serde_json::Value::as_str), std::option::Option::Some("config/composite.ksp-app-solprices-desk.json"), diff --git a/crates/ksp-app-wallet-desk/tests/release_compliance.rs b/crates/ksp-app-wallet-desk/tests/release_compliance.rs index fe92280..08184ce 100644 --- a/crates/ksp-app-wallet-desk/tests/release_compliance.rs +++ b/crates/ksp-app-wallet-desk/tests/release_compliance.rs @@ -1,5 +1,5 @@ // file: crates/ksp-app-wallet-desk/tests/release_compliance.rs -// version: 10 +// version: 11 //! Release-wide deterministic compliance canaries for Wallet Desk. @@ -199,7 +199,7 @@ fn packaged_resources_include_only_registered_config_sources_and_schemas() { std::option::Option::Some(value) => value, std::option::Option::None => return, }; - assert_eq!(resources.len(), 13); + assert_eq!(resources.len(), 14); for (source, destination) in resources { let destination = destination.as_str(); assert!(destination.is_some(), "resource destination must be textual"); diff --git a/crates/ksp-config-lib/src/lib.rs b/crates/ksp-config-lib/src/lib.rs index fe68215..afe95b1 100644 --- a/crates/ksp-config-lib/src/lib.rs +++ b/crates/ksp-config-lib/src/lib.rs @@ -1,5 +1,5 @@ // file: crates/ksp-config-lib/src/lib.rs -// version: 21 +// version: 22 #![warn(missing_docs)] #![deny(unreachable_pub)] @@ -151,6 +151,8 @@ pub use self::registry::ConfigFileId; pub use self::registry::ConfigFileKind; /// Registry of KSP-known logical Config files and their replaceable physical filenames. pub use self::registry::ConfigFileRegistry; +/// Default physical filename for the Backfill Desk composite configuration document. +pub use self::registry::DEFAULT_COMPOSITE_KSP_APP_BACKFILL_DESK_FILENAME; /// Default physical filename for the SOL Prices Desk composite configuration document. pub use self::registry::DEFAULT_COMPOSITE_KSP_APP_SOLPRICES_DESK_FILENAME; /// Default physical filename for the Wallet Desk composite configuration document. @@ -177,6 +179,8 @@ pub use self::registry::DEFAULT_STD_TRANSPORT_SCHEMA_FILENAME; pub use self::registry::DEFAULT_STD_WALLET_FILENAME; /// Default physical filename for the standard Wallet JSON Schema document. pub use self::registry::DEFAULT_STD_WALLET_SCHEMA_FILENAME; +/// Logical file identifier for the Backfill Desk composite configuration document. +pub use self::registry::FILE_ID_COMPOSITE_KSP_APP_BACKFILL_DESK; /// Logical file identifier for the SOL Prices Desk composite configuration document. pub use self::registry::FILE_ID_COMPOSITE_KSP_APP_SOLPRICES_DESK; /// Logical file identifier for the Wallet Desk composite configuration document. diff --git a/crates/ksp-config-lib/src/registry.rs b/crates/ksp-config-lib/src/registry.rs index 3eda52b..206e950 100644 --- a/crates/ksp-config-lib/src/registry.rs +++ b/crates/ksp-config-lib/src/registry.rs @@ -1,8 +1,10 @@ // file: crates/ksp-config-lib/src/registry.rs -// version: 13 +// version: 14 /// Bootstrap argument used to replace a known Config filename mapping. pub const ARG_FILE_MAP: &str = "--filemap"; +/// Default physical filename for the Backfill Desk composite configuration document. +pub const DEFAULT_COMPOSITE_KSP_APP_BACKFILL_DESK_FILENAME: &str = "composite.ksp-app-backfill-desk.json"; /// Default physical filename for the SOL Prices Desk composite configuration document. pub const DEFAULT_COMPOSITE_KSP_APP_SOLPRICES_DESK_FILENAME: &str = "composite.ksp-app-solprices-desk.json"; /// Default physical filename for the Wallet Desk composite configuration document. @@ -29,6 +31,8 @@ pub const DEFAULT_STD_TRANSPORT_SCHEMA_FILENAME: &str = "std.transport.schema.js pub const DEFAULT_STD_WALLET_FILENAME: &str = "std.wallet.json"; /// Default physical filename for the standard Wallet JSON Schema document. pub const DEFAULT_STD_WALLET_SCHEMA_FILENAME: &str = "std.wallet.schema.json"; +/// Logical file identifier for the Backfill Desk composite configuration document. +pub const FILE_ID_COMPOSITE_KSP_APP_BACKFILL_DESK: &str = "cfg.composite.ksp-app-backfill-desk"; /// Logical file identifier for the SOL Prices Desk composite configuration document. pub const FILE_ID_COMPOSITE_KSP_APP_SOLPRICES_DESK: &str = "cfg.composite.ksp-app-solprices-desk"; /// Logical file identifier for the Wallet Desk composite configuration document. @@ -160,6 +164,16 @@ pub struct ConfigFileRegistry { impl ConfigFileRegistry { /// Creates the registry containing the KSP default file mappings known in the current release. pub fn defaults() -> ksp_core_lib::Result { + let backfill_composite = ConfigFileDescriptor::new( + FILE_ID_COMPOSITE_KSP_APP_BACKFILL_DESK, + ConfigFileKind::Config, + DEFAULT_COMPOSITE_KSP_APP_BACKFILL_DESK_FILENAME, + std::option::Option::Some(FILE_ID_SCHEMA_COMPOSITE), + ); + let backfill_composite = match backfill_composite { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; let solprices_composite = ConfigFileDescriptor::new( FILE_ID_COMPOSITE_KSP_APP_SOLPRICES_DESK, ConfigFileKind::Config, @@ -271,6 +285,7 @@ impl ConfigFileRegistry { std::result::Result::Err(error) => return std::result::Result::Err(error), }; return crate::build_registry([ + backfill_composite, solprices_composite, wallet_composite, composite_schema, diff --git a/crates/ksp-config-lib/tests/public_api.rs b/crates/ksp-config-lib/tests/public_api.rs index d69d1c5..8f50ddd 100644 --- a/crates/ksp-config-lib/tests/public_api.rs +++ b/crates/ksp-config-lib/tests/public_api.rs @@ -1,5 +1,5 @@ // file: crates/ksp-config-lib/tests/public_api.rs -// version: 26 +// version: 27 //! Integration tests for the public `ksp-config-lib` bootstrap, registry, JSON/profile/composite, environment-resolution, sensitivity, //! Logging/Transport/Store adapters and management contracts. @@ -80,21 +80,22 @@ fn registry_descriptor_inventory_is_available_from_crate_root() { assert!(registry.is_ok(), "public registry should remain constructible: {registry:?}"); if let std::result::Result::Ok(registry) = registry { let descriptors: std::vec::Vec<&ksp_config_lib::ConfigFileDescriptor> = registry.descriptors().collect(); - assert_eq!(descriptors.len(), 13); - assert_eq!(descriptors[0].file_id().as_str(), ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_SOLPRICES_DESK); - assert_eq!(descriptors[1].file_id().as_str(), ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_WALLET_DESK); - assert_eq!(descriptors[2].file_id().as_str(), ksp_config_lib::FILE_ID_STD_LOGGING); - assert_eq!(descriptors[3].file_id().as_str(), ksp_config_lib::FILE_ID_STD_OFFCHAIN_TRANSPORT); - assert_eq!(descriptors[4].file_id().as_str(), ksp_config_lib::FILE_ID_STD_STORE); - assert_eq!(descriptors[5].file_id().as_str(), ksp_config_lib::FILE_ID_STD_TRANSPORT); - assert_eq!(descriptors[6].file_id().as_str(), ksp_config_lib::FILE_ID_STD_WALLET); - assert_eq!(descriptors[7].file_id().as_str(), ksp_config_lib::FILE_ID_SCHEMA_COMPOSITE); - assert_eq!(descriptors[8].file_id().as_str(), ksp_config_lib::FILE_ID_SCHEMA_STD_LOGGING); - assert_eq!(descriptors[9].file_id().as_str(), ksp_config_lib::FILE_ID_SCHEMA_STD_OFFCHAIN_TRANSPORT); - assert_eq!(descriptors[10].file_id().as_str(), ksp_config_lib::FILE_ID_SCHEMA_STD_STORE); - assert_eq!(descriptors[11].file_id().as_str(), ksp_config_lib::FILE_ID_SCHEMA_STD_TRANSPORT); - assert_eq!(descriptors[12].file_id().as_str(), ksp_config_lib::FILE_ID_SCHEMA_STD_WALLET); - let schema_file_id = descriptors[6].schema_file_id(); + assert_eq!(descriptors.len(), 14); + assert_eq!(descriptors[0].file_id().as_str(), ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_BACKFILL_DESK); + assert_eq!(descriptors[1].file_id().as_str(), ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_SOLPRICES_DESK); + assert_eq!(descriptors[2].file_id().as_str(), ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_WALLET_DESK); + assert_eq!(descriptors[3].file_id().as_str(), ksp_config_lib::FILE_ID_STD_LOGGING); + assert_eq!(descriptors[4].file_id().as_str(), ksp_config_lib::FILE_ID_STD_OFFCHAIN_TRANSPORT); + assert_eq!(descriptors[5].file_id().as_str(), ksp_config_lib::FILE_ID_STD_STORE); + assert_eq!(descriptors[6].file_id().as_str(), ksp_config_lib::FILE_ID_STD_TRANSPORT); + assert_eq!(descriptors[7].file_id().as_str(), ksp_config_lib::FILE_ID_STD_WALLET); + assert_eq!(descriptors[8].file_id().as_str(), ksp_config_lib::FILE_ID_SCHEMA_COMPOSITE); + assert_eq!(descriptors[9].file_id().as_str(), ksp_config_lib::FILE_ID_SCHEMA_STD_LOGGING); + assert_eq!(descriptors[10].file_id().as_str(), ksp_config_lib::FILE_ID_SCHEMA_STD_OFFCHAIN_TRANSPORT); + assert_eq!(descriptors[11].file_id().as_str(), ksp_config_lib::FILE_ID_SCHEMA_STD_STORE); + assert_eq!(descriptors[12].file_id().as_str(), ksp_config_lib::FILE_ID_SCHEMA_STD_TRANSPORT); + assert_eq!(descriptors[13].file_id().as_str(), ksp_config_lib::FILE_ID_SCHEMA_STD_WALLET); + let schema_file_id = descriptors[7].schema_file_id(); assert!(schema_file_id.is_some(), "public Wallet descriptor should preserve schema association"); if let std::option::Option::Some(schema_file_id) = schema_file_id { assert_eq!(schema_file_id.as_str(), ksp_config_lib::FILE_ID_SCHEMA_STD_WALLET); @@ -163,6 +164,8 @@ fn composite_schema_and_provenance_contracts_are_available_from_crate_root() { assert_eq!(descriptor.kind(), ksp_config_lib::ConfigFileKind::Schema); } } + assert_eq!(ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_BACKFILL_DESK, "cfg.composite.ksp-app-backfill-desk"); + assert_eq!(ksp_config_lib::DEFAULT_COMPOSITE_KSP_APP_BACKFILL_DESK_FILENAME, "composite.ksp-app-backfill-desk.json"); assert_eq!(ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_SOLPRICES_DESK, "cfg.composite.ksp-app-solprices-desk"); assert_eq!(ksp_config_lib::DEFAULT_COMPOSITE_KSP_APP_SOLPRICES_DESK_FILENAME, "composite.ksp-app-solprices-desk.json"); assert_ne!(ksp_config_lib::ConfigProfileSelectionSource::Composite, ksp_config_lib::ConfigProfileSelectionSource::Explicit); diff --git a/crates/ksp-config-lib/unit_tests/fixtures/composite.ksp-app-backfill-desk.json b/crates/ksp-config-lib/unit_tests/fixtures/composite.ksp-app-backfill-desk.json new file mode 100644 index 0000000..11a2914 --- /dev/null +++ b/crates/ksp-config-lib/unit_tests/fixtures/composite.ksp-app-backfill-desk.json @@ -0,0 +1,66 @@ +{ + "format_version": 1, + "default_profile": "devnet", + "profiles": [ + { + "profile_id": "devnet", + "documents": [ + { + "component_id": "logging", + "file_id": "cfg.std.logging", + "profile_id": "supertrace" + }, + { + "component_id": "transport", + "file_id": "cfg.std.transport", + "profile_id": "devnet_public" + }, + { + "component_id": "store", + "file_id": "cfg.std.store", + "profile_id": "devnet" + } + ] + }, + { + "profile_id": "mainnet", + "documents": [ + { + "component_id": "logging", + "file_id": "cfg.std.logging", + "profile_id": "console_info" + }, + { + "component_id": "transport", + "file_id": "cfg.std.transport", + "profile_id": "mainnet_public" + }, + { + "component_id": "store", + "file_id": "cfg.std.store", + "profile_id": "mainnet" + } + ] + }, + { + "profile_id": "testnet", + "documents": [ + { + "component_id": "logging", + "file_id": "cfg.std.logging", + "profile_id": "console_info" + }, + { + "component_id": "transport", + "file_id": "cfg.std.transport", + "profile_id": "publicnode_testnet" + }, + { + "component_id": "store", + "file_id": "cfg.std.store", + "profile_id": "testnet" + } + ] + } + ] +} diff --git a/crates/ksp-config-lib/unit_tests/registry.rs b/crates/ksp-config-lib/unit_tests/registry.rs index ec1ac66..bcbe38b 100644 --- a/crates/ksp-config-lib/unit_tests/registry.rs +++ b/crates/ksp-config-lib/unit_tests/registry.rs @@ -1,5 +1,5 @@ // file: crates/ksp-config-lib/unit_tests/registry.rs -// version: 11 +// version: 12 #[test] fn descriptors_expose_complete_registry_in_deterministic_file_id_order() { @@ -7,27 +7,27 @@ fn descriptors_expose_complete_registry_in_deterministic_file_id_order() { assert!(registry.is_ok(), "default registry should be valid: {registry:?}"); if let std::result::Result::Ok(registry) = registry { let descriptors: std::vec::Vec<&crate::ConfigFileDescriptor> = registry.descriptors().collect(); - assert_eq!(descriptors.len(), 13); - assert_eq!(descriptors[0].file_id().as_str(), crate::FILE_ID_COMPOSITE_KSP_APP_SOLPRICES_DESK); - assert_eq!(descriptors[0].filename(), std::path::Path::new(crate::DEFAULT_COMPOSITE_KSP_APP_SOLPRICES_DESK_FILENAME)); + assert_eq!(descriptors.len(), 14); + assert_eq!(descriptors[0].file_id().as_str(), crate::FILE_ID_COMPOSITE_KSP_APP_BACKFILL_DESK); + assert_eq!(descriptors[0].filename(), std::path::Path::new(crate::DEFAULT_COMPOSITE_KSP_APP_BACKFILL_DESK_FILENAME)); assert_eq!(descriptors[0].schema_file_id().map(crate::ConfigFileId::as_str), std::option::Option::Some(crate::FILE_ID_SCHEMA_COMPOSITE)); - assert_eq!(descriptors[1].file_id().as_str(), crate::FILE_ID_COMPOSITE_KSP_APP_WALLET_DESK); - assert_eq!(descriptors[1].filename(), std::path::Path::new(crate::DEFAULT_COMPOSITE_KSP_APP_WALLET_DESK_FILENAME)); - assert_eq!(descriptors[2].file_id().as_str(), crate::FILE_ID_STD_LOGGING); - assert_eq!(descriptors[3].file_id().as_str(), crate::FILE_ID_STD_OFFCHAIN_TRANSPORT); - assert_eq!(descriptors[4].file_id().as_str(), crate::FILE_ID_STD_STORE); - assert_eq!(descriptors[5].file_id().as_str(), crate::FILE_ID_STD_TRANSPORT); - assert_eq!(descriptors[6].file_id().as_str(), crate::FILE_ID_STD_WALLET); - assert_eq!(descriptors[6].filename(), std::path::Path::new(crate::DEFAULT_STD_WALLET_FILENAME)); - assert_eq!(descriptors[6].schema_file_id().map(crate::ConfigFileId::as_str), std::option::Option::Some(crate::FILE_ID_SCHEMA_STD_WALLET)); - assert_eq!(descriptors[7].file_id().as_str(), crate::FILE_ID_SCHEMA_COMPOSITE); - assert_eq!(descriptors[8].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_LOGGING); - assert_eq!(descriptors[9].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_OFFCHAIN_TRANSPORT); - assert_eq!(descriptors[10].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_STORE); - assert_eq!(descriptors[11].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_TRANSPORT); - assert_eq!(descriptors[12].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_WALLET); - assert!(descriptors[0..7].iter().all(|descriptor| return descriptor.kind() == crate::ConfigFileKind::Config)); - assert!(descriptors[7..13].iter().all(|descriptor| return descriptor.kind() == crate::ConfigFileKind::Schema)); + assert_eq!(descriptors[1].file_id().as_str(), crate::FILE_ID_COMPOSITE_KSP_APP_SOLPRICES_DESK); + assert_eq!(descriptors[2].file_id().as_str(), crate::FILE_ID_COMPOSITE_KSP_APP_WALLET_DESK); + assert_eq!(descriptors[3].file_id().as_str(), crate::FILE_ID_STD_LOGGING); + assert_eq!(descriptors[4].file_id().as_str(), crate::FILE_ID_STD_OFFCHAIN_TRANSPORT); + assert_eq!(descriptors[5].file_id().as_str(), crate::FILE_ID_STD_STORE); + assert_eq!(descriptors[6].file_id().as_str(), crate::FILE_ID_STD_TRANSPORT); + assert_eq!(descriptors[7].file_id().as_str(), crate::FILE_ID_STD_WALLET); + assert_eq!(descriptors[7].filename(), std::path::Path::new(crate::DEFAULT_STD_WALLET_FILENAME)); + assert_eq!(descriptors[7].schema_file_id().map(crate::ConfigFileId::as_str), std::option::Option::Some(crate::FILE_ID_SCHEMA_STD_WALLET)); + assert_eq!(descriptors[8].file_id().as_str(), crate::FILE_ID_SCHEMA_COMPOSITE); + assert_eq!(descriptors[9].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_LOGGING); + assert_eq!(descriptors[10].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_OFFCHAIN_TRANSPORT); + assert_eq!(descriptors[11].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_STORE); + assert_eq!(descriptors[12].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_TRANSPORT); + assert_eq!(descriptors[13].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_WALLET); + assert!(descriptors[0..8].iter().all(|descriptor| return descriptor.kind() == crate::ConfigFileKind::Config)); + assert!(descriptors[8..14].iter().all(|descriptor| return descriptor.kind() == crate::ConfigFileKind::Schema)); } } @@ -362,27 +362,41 @@ fn absolute_fixture_path() -> std::path::PathBuf { } #[test] -fn defaults_register_generic_composite_schema_and_both_runtime_composites() { +fn defaults_register_generic_composite_schema_and_three_runtime_composites() { let registry = crate::ConfigFileRegistry::defaults(); let schema_id = crate::ConfigFileId::new(crate::FILE_ID_SCHEMA_COMPOSITE); + let backfill_id = crate::ConfigFileId::new(crate::FILE_ID_COMPOSITE_KSP_APP_BACKFILL_DESK); let solprices_id = crate::ConfigFileId::new(crate::FILE_ID_COMPOSITE_KSP_APP_SOLPRICES_DESK); let wallet_id = crate::ConfigFileId::new(crate::FILE_ID_COMPOSITE_KSP_APP_WALLET_DESK); assert!(registry.is_ok(), "default registry should be valid: {registry:?}"); assert!(schema_id.is_ok(), "composite schema file_id should be valid: {schema_id:?}"); + assert!(backfill_id.is_ok(), "Backfill Desk composite runtime file_id should be valid: {backfill_id:?}"); assert!(solprices_id.is_ok(), "SOL Prices Desk composite runtime file_id should be valid: {solprices_id:?}"); assert!(wallet_id.is_ok(), "Wallet Desk composite runtime file_id should be valid: {wallet_id:?}"); - if let (std::result::Result::Ok(registry), std::result::Result::Ok(schema_id), std::result::Result::Ok(solprices_id), std::result::Result::Ok(wallet_id)) = - (registry, schema_id, solprices_id, wallet_id) + if let ( + std::result::Result::Ok(registry), + std::result::Result::Ok(schema_id), + std::result::Result::Ok(backfill_id), + std::result::Result::Ok(solprices_id), + std::result::Result::Ok(wallet_id), + ) = (registry, schema_id, backfill_id, solprices_id, wallet_id) { let schema = registry.descriptor(&schema_id); + let backfill = registry.descriptor(&backfill_id); let solprices = registry.descriptor(&solprices_id); let wallet = registry.descriptor(&wallet_id); assert!(schema.is_ok(), "generic composite schema should be registered: {schema:?}"); + assert!(backfill.is_ok(), "Backfill Desk runtime composite should be registered: {backfill:?}"); assert!(solprices.is_ok(), "SOL Prices Desk runtime composite should be registered: {solprices:?}"); assert!(wallet.is_ok(), "Wallet Desk runtime composite should be registered: {wallet:?}"); - if let (std::result::Result::Ok(schema), std::result::Result::Ok(solprices), std::result::Result::Ok(wallet)) = (schema, solprices, wallet) { + if let (std::result::Result::Ok(schema), std::result::Result::Ok(backfill), std::result::Result::Ok(solprices), std::result::Result::Ok(wallet)) = + (schema, backfill, solprices, wallet) + { assert_eq!(schema.kind(), crate::ConfigFileKind::Schema); assert_eq!(schema.filename(), std::path::Path::new(crate::DEFAULT_COMPOSITE_SCHEMA_FILENAME)); + assert_eq!(backfill.kind(), crate::ConfigFileKind::Config); + assert_eq!(backfill.filename(), std::path::Path::new(crate::DEFAULT_COMPOSITE_KSP_APP_BACKFILL_DESK_FILENAME)); + assert_eq!(backfill.schema_file_id(), std::option::Option::Some(&schema_id)); assert_eq!(solprices.kind(), crate::ConfigFileKind::Config); assert_eq!(solprices.filename(), std::path::Path::new(crate::DEFAULT_COMPOSITE_KSP_APP_SOLPRICES_DESK_FILENAME)); assert_eq!(solprices.schema_file_id(), std::option::Option::Some(&schema_id)); diff --git a/deltas/0.3.7/pre.003.md b/deltas/0.3.7/pre.003.md new file mode 100644 index 0000000..9e7b216 --- /dev/null +++ b/deltas/0.3.7/pre.003.md @@ -0,0 +1,83 @@ + + + +# Delta 0.3.7-pre.003 — Config composite et packaging Backfill Desk + +## Base + +`0.3.7-pre.002` validée par le gate opérateur fourni : audits Rust/Markdown, `cargo check --workspace`, Clippy et tests `ksp-app-backfill-desk` propres. + +## Objectif + +Introduire le composite Config de Backfill Desk, brancher son bootstrap Config/Logging et le packaging release, tout en conservant Transport, Store et Backfill runtime fermés. Pendant le développement du Desk, rendre son tracing volontairement verbeux via `supertrace` sur le profil `devnet` et `trace` pour le fallback local. + +## Fichiers ajoutés + +- `config/composite.ksp-app-backfill-desk.json` ; +- `crates/ksp-config-lib/unit_tests/fixtures/composite.ksp-app-backfill-desk.json` ; +- `crates/ksp-app-backfill-desk/tests/config_composition.rs` ; +- `deltas/0.3.7/pre.003.md`. + +## Fichiers modifiés + +- `Cargo.toml` ; +- `crates/ksp-config-lib/src/lib.rs` ; +- `crates/ksp-config-lib/src/registry.rs` ; +- `crates/ksp-config-lib/tests/public_api.rs` ; +- `crates/ksp-config-lib/unit_tests/registry.rs` ; +- `crates/ksp-app-backfill-desk/src/bootstrap.rs` ; +- `crates/ksp-app-backfill-desk/src/constants.rs` ; +- `crates/ksp-app-backfill-desk/src/errors.rs` ; +- `crates/ksp-app-backfill-desk/src/lib.rs` ; +- `crates/ksp-app-backfill-desk/src/tauri.rs` ; +- `crates/ksp-app-backfill-desk/tauri.conf.json` ; +- `crates/ksp-app-backfill-desk/tests/desktop_contract.rs` ; +- `crates/ksp-app-backfill-desk/tests/desktop_security.rs` ; +- `crates/ksp-app-config-desk/tauri.conf.json` ; +- `crates/ksp-app-config-desk/tests/desktop_contract.rs` ; +- `crates/ksp-app-solprices-desk/tauri.conf.json` ; +- `crates/ksp-app-solprices-desk/tests/desktop_contract.rs` ; +- `crates/ksp-app-wallet-desk/tauri.conf.json` ; +- `crates/ksp-app-wallet-desk/tests/desktop_contract.rs` ; +- `crates/ksp-app-wallet-desk/tests/release_compliance.rs` ; +- `docs/plans/028-V0_3_7_BACKFILL_DESK_PLAN.md` ; +- `docs/validation/024-V0_3_7_BACKFILL_DESK.md`. + +## Fichiers supprimés + +Aucun. + +## Décisions + +- Le composite Backfill Desk possède `devnet`, `mainnet` et `testnet`, avec `devnet` par défaut. +- `devnet` sélectionne Logging `supertrace`; le fallback local de Backfill Desk utilise `LogFilterLevel::Trace` pendant le développement. Aucun niveau des autres Desk n'est modifié. +- Le bootstrap valide les composants Logging/Transport/Store mais ne résout que Logging en `pre.003`; Transport et Store restent fermés jusqu'aux tranches prévues. +- `prepare_packaged_runtime()` traverse tout le registre Config. L'ajout du composite porte ce registre à 14 descriptors : les quatre Desk embarquent donc atomiquement les 14 resources Config/schema. +- La présence de documents Wallet/offchain dans le bundle Backfill satisfait uniquement le contrat de packaging partagé ; elle ne crée ni dépendance Rust ni capability frontend correspondante. + +## Validations exécutées + +- `python3 scripts/audit_rust_workspace_rules.py` : propre, zéro candidat d'export ; +- `python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.3.7` : propre, 270 tableaux / 150 fichiers ; +- parsing JSON : 7 manifests/resources contrôlés ; +- parsing TOML : `Cargo.toml` racine et manifeste Backfill Desk contrôlés ; +- validation JSON Schema : composite source et fixture conformes à `composite.schema.json` ; +- profils composite : `devnet/mainnet/testnet` et sélections Logging/Transport/Store exacts ; +- packaging : 14/14 resources Config/schema identiques dans les quatre Desk ; +- frontend : dépendances minimales et `devDependencies` du gabarit préservées ; +- tracing développement : `supertrace` via le composite `devnet`, `Trace` en fallback ; +- frontière `pre.003` : aucune dépendance/runtime Transport, Store ou Backfill ouverte. +- diff `pre.002 -> pre.003` : 4 fichiers ajoutés, 22 fichiers modifiés, aucune suppression. + +## Validations non exécutées + +- `cargo fmt --all` ; +- `cargo check --workspace` ; +- `cargo clippy --workspace --all-targets` ; +- suites Cargo de `ksp-config-lib` et des quatre Desk. + +L'exécutable `cargo`/`rustfmt` n'est pas disponible dans le sandbox d'assemblage. + +## Questions ouvertes + +Aucune question bloquante. Le niveau de tracing verbeux est explicitement temporaire pour la phase de développement et devra être réévalué avant la publication stable. 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 655587e..53a1246 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 @@ -148,7 +148,7 @@ Au bootstrap, l'application charge le composite, résout Logging/Transport/Store Les rôles présentés à l'UI sont dérivés des snapshots/settings Transport, dédupliqués, puis validés côté Rust comme capables de sélectionner à la fois `getSignaturesForAddress` et `getTransaction`. Le rôle `default` est actuellement le seul rôle des profils standard, mais il n'est pas codé en dur comme contrat du Desk. -Les resources bundle minimales sont le composite Backfill Desk, `std.logging.json`, `std.transport.json`, `std.store.json` et leurs schemas, plus `composite.schema.json`. Les documents Wallet/offchain ne sont pas embarqués sans besoin démontré. +Le packaging Config suit le contrat atomique déjà détenu par `ksp-config-lib::prepare_packaged_runtime()` : il traverse l'intégralité de `ConfigFileRegistry::defaults()`. L'ajout du composite Backfill Desk fait donc passer le registre de 13 à 14 descriptors et impose que les quatre applications Desk qui utilisent ce packaging embarquent les 14 resources Config/schema du registre. Backfill Desk embarque ainsi également les documents Wallet/offchain sans les consommer au runtime ; leur présence satisfait le contrat de packaging partagé et n'ouvre aucune dépendance, permission frontend ou responsabilité métier supplémentaire. ## 8. Dependency map @@ -235,7 +235,7 @@ Créer package lib+bin, shell/splash/tracing/capability, frontend minimal, ports ### pre.003 — Config composite et packaging -Ajouter composite Backfill Desk, resources minimales, bootstrap Config/Logging et canaries packaging/composition sans encore ouvrir réseau/Store réel. +Ajouter le composite Backfill Desk et l'enregistrer dans Config, mettre à jour atomiquement les quatre bundles Desk vers les 14 resources du registre, puis brancher le bootstrap Config/Logging et ses canaries sans encore ouvrir Transport/Store réel. Pendant le développement de l'application, `devnet` sélectionne le profil Logging `supertrace` et le fallback local Backfill Desk reste au niveau `trace`; ce choix est local au Desk et devra être resserré avant publication stable. ### pre.004 — Transport readiness diff --git a/docs/validation/024-V0_3_7_BACKFILL_DESK.md b/docs/validation/024-V0_3_7_BACKFILL_DESK.md index b6a3542..7cd2d6a 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 @@ -95,7 +95,7 @@ Le journal opérateur fourni rapporte `cargo clean`, `cargo fmt`, audits Rust/Ma - [ ] Package Rust lib + bin conforme. - [ ] Ports Vite/HMR 1436/1437 stricts. - [ ] Splash/main + tracing conformes. -- [ ] Composite Backfill Desk packagé et valide. +- [X] Composite Backfill Desk packagé et valide. - [ ] Transport readiness et role inventory sûrs. - [ ] Store readiness et cohérence réseau. - [ ] Quatre scopes + commitments + bornes mappés. @@ -135,3 +135,33 @@ Contraintes de gabarit vérifiées statiquement : mêmes assets splash/logo/DOS - [ ] typecheck npm complet / build Tauri : non exécutés dans le sandbox. Le contrôle TypeScript local est syntaxique avec le compilateur global disponible ; il ne remplace pas le typecheck complet avec dépendances npm et bindings TS-RS générés. Toute correction révélée par le gate opérateur reste dans la lane `pre.002-fix.NNN`. + +## 10. pre.003 — composite Config et packaging + +Le composite `cfg.composite.ksp-app-backfill-desk` est enregistré dans `ksp-config-lib` avec `devnet` par défaut. Il sélectionne `supertrace + devnet_public + devnet`, tandis que `mainnet` et `testnet` conservent des couples Transport/Store cohérents. Le bootstrap Backfill Desk valide les trois composants requis mais ne résout encore que Logging : aucun `HttpTransportPool`, `Store::open`, `BackfillRequest` ou `BackfillJobRuntime` n'est ouvert dans cette tranche. + +Le niveau de développement est volontairement verbeux pour cette application : `devnet` utilise `supertrace`, et le fallback Logging propre à Backfill Desk utilise `LogFilterLevel::Trace`. Les autres applications Desk ne changent pas de profil Logging. + +La découverte du contrat réel de `prepare_packaged_runtime()` a réconcilié le plan : la fonction traverse tout `ConfigFileRegistry::defaults()`. Le registre compte désormais 14 descriptors, de sorte que Config Desk, Wallet Desk, SOL Prices Desk et Backfill Desk embarquent toutes les 14 resources Config/schema. Les documents Wallet/offchain présents dans le bundle Backfill ne sont pas consommés par son runtime et n'élargissent ni ses capabilities ni ses dépendances Rust. + +### Gate pre.002 opérateur acquis + +- [X] audits Rust et Markdown propres ; +- [X] `cargo check --workspace` propre ; +- [X] `cargo clippy --workspace --all-targets` propre ; +- [X] `cargo test -p ksp-app-backfill-desk` : 12 unitaires + 5 desktop contract + 4 desktop security + 1 public API, aucun échec. + +### Gate statique local pre.003 + +- [X] composite Backfill Desk ajouté au source Config et aux fixtures Config ; +- [X] registre Config porté à 14 descriptors avec exports publics associés ; +- [X] profils `devnet/mainnet/testnet` et références Logging/Transport/Store couplés conformément au plan ; +- [X] les quatre `tauri.conf.json` Desk embarquent le registre Config complet de 14 resources ; +- [X] release packaging Backfill Desk appelle `prepare_packaged_runtime()` avant l'initialisation applicative ; +- [X] bootstrap Backfill valide Logging/Transport/Store puis ne résout que Logging ; +- [X] niveau de développement Backfill Desk verrouillé à `supertrace` via Config et `trace` en fallback ; +- [X] aucune dépendance `ksp-job-backfill-lib`, `ksp-onchain-transport-lib`, `ksp-store-lib` ou backend Store ajoutée au manifeste Backfill Desk ; +- [X] audit Rust workspace propre, zéro candidat d'export ; +- [X] audit Markdown propre : 270 tableaux / 150 fichiers ; +- [X] parsing JSON/TOML, validation JSON Schema et audit structurel Config/package propres ; +- [ ] `cargo fmt/check/clippy/test` pre.003 : à exécuter par l'opérateur, `cargo`/`rustfmt` absents du sandbox.