Files
ClaudeDo/docs/superpowers/specs/2026-04-21-settings-modal-design.md
Mika Kuns b4741137d0 docs(settings-modal): add design spec
Approved design for a general-settings modal behind the user-footer ⋯ button: Claude defaults, worktree defaults + maintenance actions, About section.
2026-04-21 13:32:58 +02:00

5.9 KiB
Raw Blame History

Settings Modal — Design

Date: 2026-04-21 Status: Approved for planning

Goal

Add a general-settings modal reachable from the button in the user footer of the Lists island (ListsIslandView.axaml:68). The modal exposes app-wide defaults for Claude runs, worktree behavior, and maintenance actions (cleanup / force-remove worktrees), plus a read-only "About" section with paths.

Scope

In scope

  • Claude defaults: instructions, model, max turns, permission mode
  • Worktree defaults: strategy, central root, auto-cleanup toggle + days
  • Worktree maintenance actions: cleanup finished, force-remove all
  • About section: version, data/logs/config paths with "Open in Explorer"
  • Single settings row persisted in SQLite
  • SignalR surface for read/update + maintenance
  • ClaudeArgsBuilder merge behavior for per-task overrides

Out of scope

  • Worker-side infrastructure settings (hub URL, auto-start worker) — stays in worker.config.json
  • Per-task "inherit defaults" toggle (always inherit; task values override per rule below)
  • Any UI-layer tests (project has none today)

Architecture

Persistence

New single-row entity AppSettingsEntity (Id = 1) in SQLite. Access via new AppSettingsRepository with GetAsync / UpdateAsync. Seeded by a new EF migration AddAppSettings.

Fields:

Column Type Seed default
DefaultClaudeInstructions text ""
DefaultModel string sonnet
DefaultMaxTurns int 30
DefaultPermissionMode string acceptEdits
WorktreeStrategy string sibling
CentralWorktreeRoot string? null
WorktreeAutoCleanupEnabled bool false
WorktreeAutoCleanupDays int 7

Rationale for DB over worker.config.json: transactional writes from the UI, no file-watcher dance, and the Worker already uses IDbContextFactory<ClaudeDoDbContext>.

Merge rules for per-task overrides

ClaudeArgsBuilder gains a dependency on AppSettingsRepository and merges at build time:

  • Instructions: global + "\n\n" + task (skip separator if either side empty)
  • Model / MaxTurns / PermissionMode: task ?? global (task value wins when set)

No TaskEntity schema change.

SignalR surface (new hub methods)

Method Returns Notes
GetAppSettings() AppSettingsDto Single row
UpdateAppSettings(dto) void Full-row replace
CleanupFinishedWorktrees() int removedCount Skips Active
ResetAllWorktrees() { removed, tasksAffected } Fails if any task is Running

Maintenance logic lives in a new WorktreeMaintenanceService in the Worker; the hub stays thin. Service uses existing GitService + WorktreeRepository.

Running-task guard: ResetAllWorktrees() checks for any Running tasks before touching anything. If present, returns an error — the modal surfaces "Cannot force-remove: N task(s) still running. Cancel them first."

Affected worktrees after force-remove are marked Discarded in WorktreeRepository.

UI

Entry point

ListsIslandView.axaml:68 ⋯ button binds to a new OpenSettingsCommand on IslandsShellViewModel. Command resolves SettingsModalViewModel and shows SettingsModalView via the existing modal pattern (TaskCompletionSource<bool> on save/cancel — same as WorktreeModalView / DiffModalView).

Layout

Single scrollable modal, ~560 px wide, matches existing modal chrome (header eyebrow, monospace labels, close affordance). No tabs.

Sections (top to bottom):

  1. CLAUDE DEFAULTS — instructions textarea (6 lines), model picker, max-turns numeric, permission-mode picker
  2. WORKTREES — strategy picker, central-root folder picker, auto-cleanup toggle + days, then the two maintenance buttons
  3. ABOUT — version (read-only), data folder / logs folder / worker.config path, each with "Open in Explorer" icon button

Footer: [ Cancel ] [ Save ], right-aligned. Save button disabled while the form is invalid.

Destructive action UX

  • Cleanup finished — single click, inline result line under the button ("Removed 3 worktree(s)."), auto-clears ~4 s
  • Force-remove all — click reveals an inline confirm row: "Remove ALL N worktrees? Uncommitted work will be lost." with red Remove All and neutral Cancel. Two-click confirm, no typed string (matches the delete-task confirm already in the app)

Both run against the worker over SignalR and leave the modal open on completion.

Open-in-Explorer

Uses Process.Start("explorer.exe", path). Windows-only is acceptable (app ships Windows-only via WPF installer).

Validation

Modal-side only, block Save:

  • Max turns: integer 1200
  • Auto-cleanup days: integer 1365 (required only when toggle is on)
  • Central root: required when Strategy = Central; must be an existing directory
  • Instructions: no length cap

Invalid fields get a red eyebrow with the specific error text.

Testing (ClaudeDo.Worker.Tests)

  • AppSettingsRepositoryTests — round-trip Get / Update on real SQLite
  • ClaudeArgsBuilderTests — four merge cases: both empty, only global, only task, both set (prepend + separator behavior)
  • WorktreeMaintenanceServiceTests — real git worktree fixtures:
    • cleanup skips Active, removes Merged / Discarded / Kept
    • force-remove fails while any task is Running
    • force-remove succeeds otherwise and flips affected worktrees to Discarded

No UI-layer tests (project has none today).

Build order (high level)

  1. Data: entity + configuration + migration + repository
  2. Worker: WorktreeMaintenanceService + ClaudeArgsBuilder wiring + hub methods + DTO
  3. UI: SignalR client methods on WorkerClient, SettingsModalViewModel, SettingsModalView
  4. Wire ⋯ button on ListsIslandViewIslandsShellViewModel.OpenSettingsCommand
  5. Tests (Worker.Tests)

Detailed step-by-step sequencing belongs in the implementation plan, not here.