///|
/// Validated application configuration profiles.
/// Keeping policy validation in the library prevents silently accepting
/// impossible windows or unsafe thresholds from CLI and service callers.
pub(all) enum ConfigurationEnvironment {
ConfigurationDevelopment
ConfigurationProduction
ConfigurationBenchmark
ConfigurationResearch
} derive(FromJson, ToJson, Debug, Eq)
///|
pub(all) struct ApplicationConfig {
mut environment : ConfigurationEnvironment
hrv : HrvConfig
analysis : AnalysisOptions
ingest : WearableIngestConfig
gate : QualityGatePolicy
load : LoadModelConfig
forecast : ForecastConfig
subject_retention_days : Int
mut deterministic : Bool
} derive(FromJson, ToJson, Debug, Eq)
///|
pub(all) struct ConfigIssue {
key : String
severity : String
message : String
observed : Double
expected : Double
} derive(FromJson, ToJson, Debug, Eq)
///|
pub(all) struct ConfigValidation {
valid : Bool
issues : Array[ConfigIssue]
normalized : ApplicationConfig
} derive(FromJson, ToJson, Debug, Eq)
///|
pub fn ApplicationConfig::default() -> ApplicationConfig {
{
environment: ConfigurationProduction,
hrv: HrvConfig::default(),
analysis: AnalysisOptions::default(),
ingest: WearableIngestConfig::default(),
gate: QualityGatePolicy::default(),
load: LoadModelConfig::default(),
forecast: ForecastConfig::default(),
subject_retention_days: 365,
deterministic: true,
}
}
///|
pub fn application_config_for(
environment : ConfigurationEnvironment,
) -> ApplicationConfig {
let base = ApplicationConfig::default()
match environment {
ConfigurationDevelopment => {
base.environment = ConfigurationDevelopment
base
}
ConfigurationBenchmark => {
base.environment = ConfigurationBenchmark
base
}
ConfigurationResearch => {
base.environment = ConfigurationResearch
base
}
ConfigurationProduction => base
}
}
///|
fn config_issue(
key : String,
severity : String,
message : String,
observed : Double,
expected : Double,
) -> ConfigIssue {
{ key, severity, message, observed, expected }
}
///|
pub fn validate_application_config(
config : ApplicationConfig,
) -> ConfigValidation {
let issues = []
if config.ingest.minimum_quality < 0.0 || config.ingest.minimum_quality > 1.0 {
issues.push(
config_issue(
"ingest.minimum_quality",
"error",
"quality floor must be in [0,1]",
config.ingest.minimum_quality,
0.70,
),
)
}
if config.gate.minimum_samples < 1 {
issues.push(
config_issue(
"gate.minimum_samples",
"error",
"minimum sample count must be positive",
config.gate.minimum_samples.to_double(),
32.0,
),
)
}
if config.gate.minimum_clean_ratio < 0.0 ||
config.gate.minimum_clean_ratio > 1.0 {
issues.push(
config_issue(
"gate.minimum_clean_ratio",
"error",
"clean ratio must be in [0,1]",
config.gate.minimum_clean_ratio,
0.70,
),
)
}
if config.analysis.sample_rate_hz <= 0.0 {
issues.push(
config_issue(
"analysis.sample_rate_hz",
"error",
"sample rate must be positive",
config.analysis.sample_rate_hz,
4.0,
),
)
}
if config.analysis.segment_size < 2 || config.analysis.segment_hop < 1 {
issues.push(
config_issue(
"analysis.windows",
"error",
"segment size and hop must be positive",
config.analysis.segment_size.to_double(),
60.0,
),
)
}
if config.load.acute_window_days < 1 ||
config.load.chronic_window_days < config.load.acute_window_days {
issues.push(
config_issue(
"load.windows",
"error",
"chronic window must be at least the acute window",
config.load.chronic_window_days.to_double(),
28.0,
),
)
}
if config.forecast.horizon < 0 || config.forecast.minimum_history < 1 {
issues.push(
config_issue(
"forecast.history",
"error",
"forecast horizon and history must be non-negative and positive",
config.forecast.horizon.to_double(),
7.0,
),
)
}
if config.subject_retention_days < 0 {
issues.push(
config_issue(
"subject_retention_days",
"error",
"retention cannot be negative",
config.subject_retention_days.to_double(),
365.0,
),
)
}
{
valid: issues.filter(issue => issue.severity == "error").length() == 0,
issues,
normalized: config,
}
}
///|
pub fn configuration_environment_name(
environment : ConfigurationEnvironment,
) -> String {
match environment {
ConfigurationDevelopment => "development"
ConfigurationProduction => "production"
ConfigurationBenchmark => "benchmark"
ConfigurationResearch => "research"
}
}
///|
pub fn configuration_is_strict(environment : ConfigurationEnvironment) -> Bool {
environment == ConfigurationProduction ||
environment == ConfigurationBenchmark
}
///|
pub fn config_validation_csv(validation : ConfigValidation) -> String {
let grid = [["key", "severity", "message", "observed", "expected"]]
for issue in validation.issues {
grid.push([
issue.key,
issue.severity,
issue.message,
issue.observed.to_string(),
issue.expected.to_string(),
])
}
to_csv(grid)
}
///|
pub fn config_validation_message(validation : ConfigValidation) -> String {
if validation.valid {
"application configuration is valid"
} else {
"\{validation.issues.length().to_string()} configuration issues require attention"
}
}
///|
pub fn config_feature_vector(config : ApplicationConfig) -> Array[Double] {
[
config.ingest.minimum_quality,
config.ingest.maximum_gap_seconds,
config.gate.minimum_samples.to_double(),
config.gate.minimum_clean_ratio,
config.analysis.sample_rate_hz,
config.analysis.segment_size.to_double(),
config.analysis.segment_hop.to_double(),
config.load.acute_window_days.to_double(),
config.load.chronic_window_days.to_double(),
config.forecast.horizon.to_double(),
config.forecast.minimum_history.to_double(),
config.subject_retention_days.to_double(),
if config.deterministic {
1.0
} else {
0.0
},
]
}
///|
pub fn config_is_deterministic(config : ApplicationConfig) -> Bool {
config.deterministic
}
///|
pub fn config_quality_floor(config : ApplicationConfig) -> Double {
config.ingest.minimum_quality.min(config.gate.minimum_signal_quality)
}
///|
pub fn config_window_days(config : ApplicationConfig) -> Array[Int] {
[config.load.acute_window_days, config.load.chronic_window_days]
}
///|
pub fn config_requires_frequency(config : ApplicationConfig) -> Bool {
config.gate.require_frequency
}
///|
pub fn config_requires_nonlinear(config : ApplicationConfig) -> Bool {
config.gate.require_nonlinear
}
///|
pub fn config_maximum_gap(config : ApplicationConfig) -> Double {
config.ingest.maximum_gap_seconds
}
///|
pub fn config_forecast_horizon(config : ApplicationConfig) -> Int {
config.forecast.horizon
}
///|
pub fn config_retention_seconds(config : ApplicationConfig) -> Double {
config.subject_retention_days.to_double() * 86400.0
}
///|
pub fn config_summary(config : ApplicationConfig) -> String {
"\{configuration_environment_name(config.environment)}: quality=\{config_quality_floor(config).to_string()} windows=\{config.load.acute_window_days.to_string()}/\{config.load.chronic_window_days.to_string()} forecast=\{config.forecast.horizon.to_string()}"
}
///|
pub fn config_validation_has_errors(validation : ConfigValidation) -> Bool {
validation.issues.any(issue => issue.severity == "error")
}
///|
pub fn config_validation_warning_count(validation : ConfigValidation) -> Int {
validation.issues.filter(issue => issue.severity == "warning").length()
}
///|
pub fn config_validation_error_keys(
validation : ConfigValidation,
) -> Array[String] {
validation.issues
.filter(issue => issue.severity == "error")
.map(issue => issue.key)
}
///|
pub fn config_environment_is_safe(config : ApplicationConfig) -> Bool {
let validation = validate_application_config(config)
validation.valid &&
config_quality_floor(config) >= 0.50 &&
config.ingest.maximum_gap_seconds > 0.0 &&
config.analysis.sample_rate_hz > 0.0
}
///|
pub fn config_for_ci() -> ApplicationConfig {
let config = application_config_for(ConfigurationBenchmark)
config
}
///|
pub fn config_for_cli() -> ApplicationConfig {
let config = application_config_for(ConfigurationProduction)
config
}
///|
pub fn config_for_tests() -> ApplicationConfig {
let config = application_config_for(ConfigurationDevelopment)
config
}
///|
pub fn config_has_valid_windows(config : ApplicationConfig) -> Bool {
config.analysis.segment_size >= config.analysis.segment_hop &&
config.analysis.segment_hop > 0 &&
config.load.chronic_window_days >= config.load.acute_window_days &&
config.load.acute_window_days > 0
}
///|
pub fn config_has_valid_bounds(config : ApplicationConfig) -> Bool {
config.ingest.minimum_quality >= 0.0 &&
config.ingest.minimum_quality <= 1.0 &&
config.gate.minimum_clean_ratio >= 0.0 &&
config.gate.minimum_clean_ratio <= 1.0 &&
config.forecast.lower_bound <= config.forecast.upper_bound
}
///|
pub fn config_is_benchmark_ready(config : ApplicationConfig) -> Bool {
config.environment == ConfigurationBenchmark &&
config.deterministic &&
config_has_valid_windows(config) &&
config_has_valid_bounds(config)
}
///|
pub fn config_is_production_ready(config : ApplicationConfig) -> Bool {
config.environment == ConfigurationProduction &&
config_environment_is_safe(config) &&
config.subject_retention_days > 0
}
///|
pub fn config_quality_policy_line(config : ApplicationConfig) -> String {
"\{config.gate.minimum_samples.to_string()} samples, clean=\{config.gate.minimum_clean_ratio.to_string()}, signal=\{config.gate.minimum_signal_quality.to_string()}"
}
///|
pub fn config_ingest_policy_line(config : ApplicationConfig) -> String {
"gap=\{config.ingest.maximum_gap_seconds.to_string()}s, duration=\{config.ingest.maximum_duration_seconds.to_string()}s, source=\{config.ingest.source_name}"
}
///|
pub fn config_load_policy_line(config : ApplicationConfig) -> String {
"acute=\{config.load.acute_window_days.to_string()}d, chronic=\{config.load.chronic_window_days.to_string()}d, caution=\{config.load.caution_ratio.to_string()}"
}
///|
pub fn config_forecast_policy_line(config : ApplicationConfig) -> String {
"horizon=\{config.forecast.horizon.to_string()}, history=\{config.forecast.minimum_history.to_string()}, alpha=\{config.forecast.ewma_alpha.to_string()}"
}
///|
pub fn config_policy_lines(config : ApplicationConfig) -> Array[String] {
[
config_summary(config),
config_quality_policy_line(config),
config_ingest_policy_line(config),
config_load_policy_line(config),
config_forecast_policy_line(config),
if config_requires_frequency(config) {
"frequency stage required"
} else {
"frequency stage optional"
},
if config_requires_nonlinear(config) {
"nonlinear stage required"
} else {
"nonlinear stage optional"
},
]
}
///|
pub fn config_environment_line(config : ApplicationConfig) -> String {
configuration_environment_name(config.environment)
}
///|
pub fn config_retention_days(config : ApplicationConfig) -> Int {
config.subject_retention_days
}
///|
pub fn config_quality_floor_is_safe(config : ApplicationConfig) -> Bool {
config_quality_floor(config) >= 0.50 && config_quality_floor(config) <= 1.0
}
///|
pub fn config_analysis_window_is_safe(config : ApplicationConfig) -> Bool {
config.analysis.segment_size >= 16 &&
config.analysis.segment_hop >= 1 &&
config.analysis.segment_hop <= config.analysis.segment_size
}
///|
pub fn config_allows_streaming(config : ApplicationConfig) -> Bool {
config.ingest.maximum_duration_seconds > 0.0 &&
config.ingest.maximum_gap_seconds > 0.0 &&
config.ingest.source_name.length() > 0
}
///|
pub fn config_readiness_line(config : ApplicationConfig) -> String {
let validation = validate_application_config(config)
if !validation.valid {
config_validation_message(validation)
} else if config_is_benchmark_ready(config) {
"configuration is benchmark-ready"
} else if config_is_production_ready(config) {
"configuration is production-ready"
} else {
"configuration is valid for exploratory use"
}
}
///|
pub fn config_issue_count(config : ApplicationConfig) -> Int {
validate_application_config(config).issues.length()
}
///|
pub fn config_is_valid(config : ApplicationConfig) -> Bool {
validate_application_config(config).valid
}
///|
pub fn config_is_strictly_valid(config : ApplicationConfig) -> Bool {
config_is_valid(config) &&
config_has_valid_windows(config) &&
config_has_valid_bounds(config)
}
///|
pub fn config_status(config : ApplicationConfig) -> String {
if config_is_strictly_valid(config) {
"valid"
} else {
"invalid"
}
}