Configuration Capability (Flow)¶
This document describes the end-to-end technical flow for configuration management across SmrtHub components.
Subsystem contract reference: README.Files/Subsystems/Configuration/README.md
Purpose and scope¶
- Explain how components locate config/state artifacts and decide roaming vs local scope.
- Describe the expected lifecycle: defaults, load, validate, migrate, save, observe.
- Provide failure-mode guidance for operators and developers.
Entry points¶
Typical initiators of configuration flows:
- Component startup: load configuration and state prior to starting long-running services.
- Operator actions: configuration updates (UI, CLI, or tool-driven) that must persist atomically.
- Background jobs: periodic refreshes or state snapshots (for example health/telemetry).
Participating Components/Subsystems¶
- Configuration Subsystem (contracts and canonical paths):
README.Files/Subsystems/Configuration/README.md - Logging Subsystem (where to observe failures):
README.Files/Subsystems/Logging/README.md - Core library (C#):
SmrtApps/src/Smrt.Config/README.md - Python runtime:
SmrtApps/PythonApp/python_core/**(uses the Python config path resolver) - Consumers: any component that owns config/state artifacts under the Operational Data Policy.
Sequence / flow¶
- Determine component slug and scope
-
Resolve the component slug (kebab-case) and choose scope (roaming vs local) based on the artifact’s intent (user profile vs machine/runtime).
-
Resolve canonical paths
- Compute the canonical directory and file name per Operational Data Policy.
-
Ensure the directory exists.
-
Seed defaults (first run only)
- If the config file is absent and the component ships defaults, copy defaults into the canonical location.
-
Defaults must not contain secrets.
-
Load and validate
- Load JSON into a strongly typed model or validated object.
- Handle common error cases: missing file, parse failure, schema mismatch.
-
When recoverable, fall back to defaults and log a clear diagnostic.
-
Migrate (when versioned)
- If
schemaVersionindicates an older version, apply a forward migration and persist the migrated result atomically. -
If forward migration is unsupported, fail fast or regenerate defaults (component-defined), and log the operator impact.
-
Persist updates atomically
- All updates are written atomically; if a previous copy exists, produce a single
.bak. -
For multi-process updates, enforce a single-writer guard (mutex/lock) before writing.
-
Observe and troubleshoot
- Use unified per-component logs to identify failures.
- Confirm on disk that artifacts exist under the canonical config directory for the component slug.
Contracts and data shapes¶
- Configuration is UTF-8 JSON with deterministic serialization.
- Secrets never appear in persisted JSON; only references/IDs.
- State JSON is treated like config with the same atomic and backup behaviors.
Failure modes and expected behaviors¶
- File missing: seed defaults (if available) or initialize to safe defaults; log at Information/Warning.
- Parse failure / corruption: attempt recovery, fall back to defaults, preserve
.bakwhen overwriting; log at Warning/Error with guidance. - Permission failure: fail fast with an actionable error (directory, scope, expected location).
- Schema mismatch: either migrate or fail with a clear operator message; do not silently ignore.
Observability & diagnostics¶
- Primary evidence is unified per-component logs (and the initiating component’s logs).
- See the Logging Subsystem doc for log locations and troubleshooting workflow:
README.Files/Subsystems/Logging/README.md. - Not documented yet: standardized event names for config load/validate/migrate failures.
Testing & validation expectations¶
- Unit tests for path resolution and filename conventions.
- Integration tests that exercise atomic write +
.bakbehavior. - Cross-runtime parity tests for shared slugs and artifact kinds.
- Manual smoke: edit a config file, verify the component reloads (if supported) or reads it on next start, and verify the error path when JSON is malformed.