## Summary Extract the settings-conflict orchestration out of the runner (`bootstrap.ts`) into a pure, testable planner, leaving `bootstrap` a thin imperative shell. **Behavior-preserving refactor — no functional change.** Spun out of #744 (agent credential isolation), where the conflict-resolution logic landed. ## Problem `src/lib/agent/runner/shared/bootstrap.ts` (~lines 150–210, ~60 lines) currently mixes, inline in the runner: - classification of detected conflicts, - three near-identical `analytics.wizardCapture('settings conflict …', { level, keys })` calls (each repeating the `source === 'managed' ? 'org' : source` ternary), - `logToFile` narration, - the `backupAndFixClaudeSettings` call + the "backup failed → fall back to fail-closed" re-assignment, - the `getUI().showSettingsOverride(...)` call. The repo's own `wizard-development` skill names **"the runner is getting longer"** as a design smell, and says infra concerns should route to the narrowest surface. This block is the most tiring part of the credential path to read. ## Constraint (important) `src/lib/agent/claude-settings.ts` is **intentionally UI-free and dependency-light** — its header documents that it lives apart from the agent stack so `bin.ts` can run orphan recovery at process start without pulling in `getUI()`/the SDK. **Do not** move the UI/analytics calls into it. ## Proposed approach — split decision from I/O - **Pure planner in `claude-settings.ts`**: extend `classifySettingsConflicts` (or add `planConflictResolution(conflicts) → { backup, warn, block }`) — pure, no UI, trivially unit-testable. - **`bootstrap.ts` keeps the imperative shell**: the `backupAndFixClaudeSettings` call, the `getUI().showSettingsOverride` call, analytics — but collapse the repeated capture into one local helper, e.g. ```ts const report = (event: string, c: SettingsConflict) => analytics.wizardCapture(event, { level: c.source === 'managed' ? 'org' : c.source, keys: c.keys }); ``` ## Acceptance criteria - [ ] No behavior change; existing `settings-conflicts` + `claude-settings-backup` tests pass unchanged. - [ ] Planner is pure (no `getUI`, no fs side-effects) and unit-tested. - [ ] `claude-settings.ts` gains no UI/agent-stack imports. - [ ] `bootstrap.ts` conflict block is materially shorter and reads as "classify → act". ## Out of scope (separate, optional) Adjacent cleanups identified in the same review, not blocking this: - delete dead `@deprecated checkClaudeSettingsOverrides` (no callers), - dedupe the 3× `['settings.json','settings']` backup/restore/recover loop + `.wizard-backup` magic string, - share the `CLAUDE_CONFIG_DIR || ~/.claude` resolution (duplicated in `stored-login.ts` + `agent-interface.ts`).