Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NHyYupFdBiR4VfoUM7NjSM
3.1 KiB
3.1 KiB
Step 5 — Settings
Read 00-shared.md, the plan (§3, §4.2 "Config reload", §13 in full, §15,
§16, §17 "Settings schema" and "Settings writer"), src/config.rs in full
(every struct, validate, load, resolve_path, check_report, ENV_PREFIX,
ENV_SPLIT, the DAILY_EPUB_SECRET alias, the stale-key checks), the README
configuration table and config.example.toml, and the handoffs for steps 1–4.
This is plan §19 item 5.
Deliverables
src/web/dashboard/settings.rs:schema(config, file) -> Vec<SettingGroup>derived by walkingtoml::Value::try_from(Config::default())and the current config in parallel (§13.1) — oneSettingFieldper leaf withpath,group,kind,current,default,source(Default/File/Env(name)),help,restart_required. Kinds and the static enum table exactly as §13.1 item 3;SETTINGS_HELPseeded from the README table andconfig.example.tomlcomments, one entry per shipped key (test). Groups render in the §13.1 item 5 order; each group gets anidanchor equal to its dotted path (step 4 links to these).GET /dashboard/settings(§4.2 reload-on-mtime first; report a file that fails to load with the!banner and keep the previous config live),dashboard/settings.html:Secretfields as set/not set,Envfields disabled with the lock note, everything else as an input with the default beside it and a reset affordance; weights note.POST /dashboard/settingsimplements §13.2 exactly:toml_edit::DocumentMut, only changed fields written ("explicit beats implicit"), typed by kind,TextListas a multi-line array with trailing comma, field errors collected before any write, render →<path>.tmp.<pid>→ validate withConfig::load(Some(tmp))→ copy permissions → rename → swapstate.config; oneconfig_changesrow per changed key; flash with the restart note forrestart_requiredkeys;config_path == None→ 400.- Providers (§13.3): remove (refused when an
[llm]role names it) and add ([a-z0-9_]+, must not exist,ProviderConfig::default()with the chosen kind and placeholders) through the same validate-and-rename path, withconfig_changesrows. GET /dashboard/settings/history(§13.4),dashboard/settings_history.html.systemd/daily-epub.service: add/etc/daily-epubtoReadWritePaths(theSupplementaryGroupsline is step 6).- Tests (§17): every leaf of
Config::default()appears exactly once; every shipped key has help; secrets areSecretwith no value; env detection uses the derived name (set the env var inside the test with a unique key and restore it); enum options round-trip throughvalidate(); starting fromconfig.example.tomlchanging three keys preserves every comment and the order of untouched lines; a new key in an absent table creates the table;TextListmulti-line; an invalid value (e.g.deep_keep < shortlist_keep) is rejected and the file untouched; permissions preserved;config_changesrows; provider add/remove; removing a referenced provider refused; reload on mtime swaps the config.