Skip to content

Commit 22ccdd4

Browse files
cpcloudclaude
andauthored
refactor(config)!: make config sections orthogonal (#731) (#747)
Rearchitect the config system so each section is self-contained with no cross-section value inheritance. Kill the shared [llm] base section and replace it with independent [chat.llm] and [extraction.llm] sections. Move OCR TSV settings into their own [extraction.ocr.tsv] sub-section. - Remove all deprecated key migration code, env var renames, and backward-compatibility shims - Remove deprecated MICASA_CURRENCY env var; only MICASA_LOCALE_CURRENCY is supported - Remove redundant llmExtraContext field; use chatCfg.ExtraContext - Remove dead collectValues/allValues code from ShowConfig - Combine env source and deprecation comments in config --dump output - Rename persisted settings DB key from llm.model to chat.llm.model - Update all docs, VHS tapes, error messages, and codebase maps - Add AGENTS.md rules for resisting configuration and orthogonality BREAKING CHANGE: The [llm] config section and all deprecated env vars (MICASA_LLM_*, MICASA_EXTRACTION_MODEL, MICASA_EXTRACTION_TIMEOUT, MICASA_CURRENCY) have been removed. Use [chat.llm] and [extraction.llm] sections with their corresponding MICASA_CHAT_LLM_* and MICASA_EXTRACTION_LLM_* env vars instead. The persisted model DB key changed from llm.model to chat.llm.model; existing model preferences will reset on first use. Closes #731 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5dbac76 commit 22ccdd4

26 files changed

Lines changed: 1064 additions & 1839 deletions

.claude/codebase/types.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -121,16 +121,19 @@ Col* (e.g., ColID = "id", ColName = "name", ColDeletedAt = "deleted_at")
121121
## Config Types (internal/config/)
122122

123123
### Config (config.go)
124-
- LLM (provider, model, baseURL, apiKey, timeout, thinking, extraContext)
125-
- Chat/Extraction overrides (LLMChatOverride, LLMExtractionOverride)
124+
- Chat (Enable *bool, LLM ChatLLM)
125+
- ChatLLM (Provider, BaseURL, Model, APIKey, Timeout, Thinking, ExtraContext)
126+
- Extraction (MaxPages int, LLM ExtractionLLM, OCR)
127+
- ExtractionLLM (Enable *bool, Provider, BaseURL, Model, APIKey, Timeout, Thinking)
128+
- OCR (Enable *bool, TSV OCRTSV)
129+
- OCRTSV (Enable *bool, ConfidenceThreshold *int)
126130
- Documents (MaxFileSize ByteSize, CacheTTL Duration)
127-
- Extraction (MaxPages int, Enable *bool, LLMTimeout)
128-
- OCR (Enable *bool, ConfidenceThreshold int)
129131
- Locale (Currency string)
132+
Each pipeline section is self-contained; no cross-section inheritance.
130133

131134
### Defaults
132135
- Provider: "ollama", Model: "qwen3", BaseURL: "http://localhost:11434"
133-
- MaxPages: 20, CacheTTL: 30 days, LLMTimeout: 5m
136+
- MaxPages: 0, CacheTTL: 30 days, LLMTimeout: 5m, OCR TSV threshold: 70
134137

135138
## LLM Types (internal/llm/)
136139

.claude/commands/rev.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ Rebase onto the latest main, address PR review feedback, and fix failing CI.
55

66
## 1. Rebase onto main
77

8-
1. `git pull --rebase origin main`
9-
2. If there are conflicts, resolve them, `git add` the resolved files, and
8+
1. `git fetch origin main`
9+
2. `git rebase origin/main`
10+
3. If there are conflicts, resolve them, `git add` the resolved files, and
1011
`git rebase --continue`. Repeat until the rebase completes.
1112

1213
## 2. Address PR review feedback and fix CI (parallel)

AGENTS.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,6 @@ details; do not duplicate that detail here.
210210

211211
### Git and CI
212212

213-
- **Rebase with one command**: Use `git pull --rebase origin main` instead
214-
of separate `git fetch origin main` + `git rebase origin/main`.
215213
- **Reply to PR review comments**: After addressing a PR review comment,
216214
reply to the comment on GitHub (via `gh api .../replies`) explaining
217215
how it was addressed (commit hash, what changed, tests added). Do this
@@ -269,6 +267,19 @@ details; do not duplicate that detail here.
269267
complete backup. Never store app state outside SQLite.
270268
- **LLM is opt-in, not a crutch**: Every feature must work fully without
271269
the LLM. The LLM enhances; it does not substitute.
270+
- **Resist configuration**: Push back when the user asks to make something
271+
configurable. Most things should not be. Prefer sensible defaults,
272+
auto-detection, and convention over configuration. Every config knob is
273+
a maintenance burden, a documentation obligation, and a combinatorial
274+
testing surface. Only add configuration when there is a concrete,
275+
demonstrated need -- not a hypothetical one.
276+
- **Orthogonal configuration**: When configuration is warranted and agreed
277+
upon, each config value must interact predictably -- or preferably not
278+
at all -- with every other config value. No value in one section should
279+
silently affect values in another section. No inheritance chains, no
280+
cascading defaults across sections, no "this overrides that unless the
281+
other thing is set." If two pipelines need the same setting, they each
282+
get their own independent copy.
272283
- **Deterministic ordering requires tiebreakers**: Every `ORDER BY` that
273284
could tie MUST include a tiebreaker (typically `id DESC`).
274285
- **Audit new deps before adding**: Review source for security issues

cmd/micasa/main.go

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ type backupCmd struct {
4343
}
4444

4545
type configCmd struct {
46-
Key string `arg:"" optional:"" help:"Dot-delimited config key (e.g. llm.model, documents.max_file_size)."`
46+
Key string `arg:"" optional:"" help:"Dot-delimited config key (e.g. chat.llm.model, documents.max_file_size)."`
4747
Dump bool ` help:"Print the fully resolved config as TOML and exit."`
4848
}
4949

@@ -147,34 +147,35 @@ func (cmd *runCmd) Run() error {
147147
FilePickerDir: cfg.Documents.ResolvedFilePickerDir(),
148148
}
149149

150-
chatCfg := cfg.LLM.ChatConfig()
151-
opts.SetLLM(
152-
chatCfg.Provider,
153-
chatCfg.BaseURL,
154-
chatCfg.Model,
155-
chatCfg.APIKey,
156-
chatCfg.ExtraContext,
157-
chatCfg.Timeout,
158-
chatCfg.Thinking,
150+
chatLLM := cfg.Chat.LLM
151+
opts.SetChat(
152+
cfg.Chat.IsEnabled(),
153+
chatLLM.Provider,
154+
chatLLM.BaseURL,
155+
chatLLM.Model,
156+
chatLLM.APIKey,
157+
chatLLM.ExtraContext,
158+
chatLLM.TimeoutDuration(),
159+
chatLLM.Thinking,
159160
)
160161

161-
exCfg := cfg.LLM.ExtractionConfig()
162+
exLLM := cfg.Extraction.LLM
162163
extractors := extract.DefaultExtractors(
163164
cfg.Extraction.MaxPages,
164165
0, // pdftotext uses its own internal default timeout (30s)
165-
cfg.Extraction.IsOCREnabled(),
166+
cfg.Extraction.OCR.IsEnabled(),
166167
)
167168
opts.SetExtraction(
168-
exCfg.Provider,
169-
exCfg.BaseURL,
170-
exCfg.Model,
171-
exCfg.APIKey,
172-
exCfg.Timeout,
173-
exCfg.Thinking,
169+
exLLM.Provider,
170+
exLLM.BaseURL,
171+
exLLM.Model,
172+
exLLM.APIKey,
173+
exLLM.TimeoutDuration(),
174+
exLLM.Thinking,
174175
extractors,
175-
cfg.Extraction.IsEnabled(),
176-
cfg.Extraction.IsOCRTSV(),
177-
cfg.Extraction.OCRConfThreshold(),
176+
exLLM.IsEnabled(),
177+
cfg.Extraction.OCR.TSV.IsEnabled(),
178+
cfg.Extraction.OCR.TSV.Threshold(),
178179
)
179180

180181
model, err := app.NewModel(store, opts)

cmd/micasa/main_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,10 @@ func TestConfigCmd(t *testing.T) {
152152
t.Context(),
153153
bin,
154154
"config",
155-
"llm.model",
155+
"chat.llm.model",
156156
)
157157
out, err := cmd.CombinedOutput()
158-
require.NoError(t, err, "config llm.model failed: %s", out)
158+
require.NoError(t, err, "config chat.llm.model failed: %s", out)
159159
got := strings.TrimSpace(string(out))
160160
assert.NotEmpty(t, got)
161161
})

docs/content/blog/llm-plumbing.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ over at Mozilla.
3939

4040
Back to the tech.
4141

42-
The provider details are configured in your [micasa config](/docs/reference/configuration/#llm-section):
42+
The provider details are configured in your [micasa config](/docs/reference/configuration/#chatllm-section):
4343

4444
```toml
45-
[llm]
45+
[chat.llm]
4646
provider = "anthropic"
4747
model = "claude-sonnet-4-5-latest"
4848
api_key = "sk-..."
@@ -63,19 +63,19 @@ They used to share a model. Now they don't
6363
([#575](https://github.com/cpcloud/micasa/pull/575)):
6464

6565
```toml
66-
[llm]
66+
[chat.llm]
6767
provider = "ollama"
6868
model = "qwen3"
6969

70-
[llm.extraction]
70+
[extraction.llm]
7171
provider = "anthropic"
7272
model = "claude-haiku-4-5-latest"
7373
api_key = "sk-..."
7474
```
7575

7676
Chat runs locally, extraction runs on Anthropic. Or both local. Or both cloud.
77-
`[llm]` is the default; `[llm.chat]` and `[llm.extraction]` override whatever
78-
you want per-pipeline.
77+
Each pipeline has its own independent `[chat.llm]` and `[extraction.llm]`
78+
sections -- no inheritance, no cross-contamination.
7979

8080
## Picking models at runtime
8181

@@ -102,7 +102,7 @@ look before it writes.
102102
- **[Locale-aware currency](/docs/reference/configuration/#locale-section)** --
103103
EUR gets comma decimals and period grouping (`1.234,56`), GBP gets the pound
104104
sign, JPY drops decimal places. Auto-detected from your system locale or set
105-
via `MICASA_CURRENCY`.
105+
via `MICASA_LOCALE_CURRENCY`.
106106
([#467](https://github.com/cpcloud/micasa/pull/467))
107107
- **[Imperial/metric toggle](/docs/guide/house-profile/)** -- <kbd>U</kbd> switches
108108
between square feet and square meters. Defaults to metric unless your locale
@@ -136,9 +136,9 @@ go run github.com/cpcloud/micasa/cmd/micasa@latest --demo
136136
Or with a cloud provider:
137137

138138
```sh
139-
export MICASA_LLM_PROVIDER=anthropic
140-
export MICASA_LLM_MODEL=claude-haiku-4-5-latest
141-
export MICASA_LLM_API_KEY=sk-...
139+
export MICASA_CHAT_LLM_PROVIDER=anthropic
140+
export MICASA_CHAT_LLM_MODEL=claude-haiku-4-5-latest
141+
export MICASA_CHAT_LLM_API_KEY=sk-...
142142
go run github.com/cpcloud/micasa/cmd/micasa@latest --demo
143143
```
144144

docs/content/docs/guide/llm-chat.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,12 +211,12 @@ micasa connects over plain HTTP by default. Consider:
211211
- **Network exposure** -- anyone who can intercept traffic between you and a
212212
remote HTTP endpoint can read your home data
213213

214-
The LLM feature is entirely optional. If you never configure an `[llm]`
214+
The LLM feature is entirely optional. If you never configure a `[chat.llm]`
215215
section, no data is ever sent anywhere.
216216

217217
## Configuration
218218

219-
The chat requires an `[llm]` section in your config file. If no LLM is
219+
The chat requires a `[chat.llm]` section in your config file. If no LLM is
220220
configured, the chat overlay shows a helpful hint with the config path and
221221
a sample configuration.
222222

0 commit comments

Comments
 (0)