Add configuration loader for sermones-runtime #3

Closed
opened 2026-08-01 19:58:45 -04:00 by lyssieth · 0 comments
Owner

Implement the configuration loader declared in crates/sermones-runtime/src/lib.rs and docs/glossary.md:

Persist on disk and config load are planned but not yet implemented.

The runtime crate's lib.rs declares "the configuration loader" among its responsibilities; the module does not exist. ErrorKind::ConfigInvalid already lives in crates/sermones-core/src/error.rs.

Checklist

  • Choose a config format and file location (OS-conventional user-state dir; TOML is the project's accepted config format per docs/architecture/stack.md — pick and document)
  • Required keys for v1: provider.base_url (overrides SERMONES_BASE_URL), provider.model (overrides SERMONES_MODEL), possibly provider.api_key
  • UI keys: ui.theme (system / light / dark), window state if earned — the settings screen (#9) writes these through set_config
  • Typed Config struct (Facet-derived) on the runtime boundary; loader returns Result<Config, SermonError> with kind = ConfigInvalid on parse failure and the offending path in context
  • IPC: expose get_config / set_config (or read-only at first) on the bridge so the UI can show the current provider/model
  • Env-var overrides (SERMONES_BASE_URL, SERMONES_MODEL) continue to take precedence over file values during the dev loop
  • Tests with a tempdir; no live filesystem state

Refs:

  • docs/architecture/stack.md — TOML accepted, no YAML/JSON config, Facet for serialisation
  • docs/architecture/errors.mdConfigInvalid is the canonical error kind
  • crates/sermones/src/lib.rs — env-var override convention used in run_gui
  • crates/sermones-runtime/src/lib.rs — module docstring declares this as the runtime's job

Resolution

Landed in 2315556 (main): Config / ProviderConfig / UiConfig / Theme in sermones-core's src/config.rs, ConfigStore in crates/sermones-runtime/src/config/ over $XDG_CONFIG_HOME/sermones/config.toml (fallback ~/.config/...), atomic tmp+rename writes. ConfigInvalid on parse failure with the offending path in context["path"]; io failures are IoError with path. Bridge: get_config (synchronous, in-memory; corrupt startup config falls back to defaults and surfaces via GetConfigResult.errors) and set_config (fire-and-forget persist, failures on error_raised). Typed getConfig / setConfig helpers in frontend/src/lib/bridge.ts. 9 new tempdir tests; 461/461 workspace tests passing, lint clean.

Deviations (decided up front):

  • Location: the config dir (XDG_CONFIG_HOME), not the user-state dir the checklist mentioned — config is config, state is state.
  • Env overrides removed, not kept: precedence is file > built-in defaults. The checklist's "env vars take precedence" item is ticked as decided and implemented the other way.
  • Window state not earned — only provider.* and ui.theme land; provider.api_key is reserved (nothing reads it yet).
  • App.svelte does not call the new surface yet; that comes with the settings screen (#9).
Implement the configuration loader declared in `crates/sermones-runtime/src/lib.rs` and `docs/glossary.md`: > Persist on disk and config load are planned but not yet implemented. The runtime crate's `lib.rs` declares "the configuration loader" among its responsibilities; the module does not exist. `ErrorKind::ConfigInvalid` already lives in `crates/sermones-core/src/error.rs`. ## Checklist - [x] Choose a config format and file location (OS-conventional user-state dir; TOML is the project's accepted config format per `docs/architecture/stack.md` — pick and document) - [x] Required keys for v1: `provider.base_url` (overrides `SERMONES_BASE_URL`), `provider.model` (overrides `SERMONES_MODEL`), possibly `provider.api_key` - [x] UI keys: `ui.theme` (system / light / dark), window state if earned — the settings screen (#9) writes these through `set_config` - [x] Typed `Config` struct (`Facet`-derived) on the runtime boundary; loader returns `Result<Config, SermonError>` with `kind = ConfigInvalid` on parse failure and the offending path in `context` - [x] IPC: expose `get_config` / `set_config` (or read-only at first) on the bridge so the UI can show the current provider/model - [x] Env-var overrides (`SERMONES_BASE_URL`, `SERMONES_MODEL`) continue to take precedence over file values during the dev loop - [x] Tests with a tempdir; no live filesystem state Refs: - `docs/architecture/stack.md` — TOML accepted, no YAML/JSON config, `Facet` for serialisation - `docs/architecture/errors.md` — `ConfigInvalid` is the canonical error kind - `crates/sermones/src/lib.rs` — env-var override convention used in `run_gui` - `crates/sermones-runtime/src/lib.rs` — module docstring declares this as the runtime's job ## Resolution Landed in `2315556` (main): `Config` / `ProviderConfig` / `UiConfig` / `Theme` in `sermones-core`'s `src/config.rs`, `ConfigStore` in `crates/sermones-runtime/src/config/` over `$XDG_CONFIG_HOME/sermones/config.toml` (fallback `~/.config/...`), atomic tmp+rename writes. `ConfigInvalid` on parse failure with the offending path in `context["path"]`; io failures are `IoError` with path. Bridge: `get_config` (synchronous, in-memory; corrupt startup config falls back to defaults and surfaces via `GetConfigResult.errors`) and `set_config` (fire-and-forget persist, failures on `error_raised`). Typed `getConfig` / `setConfig` helpers in `frontend/src/lib/bridge.ts`. 9 new tempdir tests; 461/461 workspace tests passing, lint clean. Deviations (decided up front): - **Location:** the config dir (`XDG_CONFIG_HOME`), not the user-state dir the checklist mentioned — config is config, state is state. - **Env overrides removed**, not kept: precedence is file > built-in defaults. The checklist's "env vars take precedence" item is ticked as *decided and implemented the other way*. - **Window state not earned** — only `provider.*` and `ui.theme` land; `provider.api_key` is reserved (nothing reads it yet). - `App.svelte` does not call the new surface yet; that comes with the settings screen (#9).
lyssieth added this to the v1 milestone 2026-08-01 19:59:00 -04:00
lyssieth removed their assignment 2026-08-01 19:59:14 -04:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Blocks
Reference
lyssieth/sermones#3
No description provided.