Files
ClaudeDo/src/ClaudeDo.Data/CLAUDE.md
T

10 KiB
Raw Blame History

ClaudeDo.Data

Shared data layer: models, repositories, SQLite infrastructure, and git operations.

Models

  • TaskEntity — Id, ListId, Title, Description, Status (Idle|Queued|Running|WaitingForChildren|WaitingForReview|Done|Failed|Cancelled), PlanningPhase (None|Active|Finalized — parent-only), BlockedByTaskId (nullable FK to predecessor in a chain), ScheduledFor, Result, ReviewFeedback (nullable; reviewer's rejection comment, consumed and cleared by the runner on the next re-run), LogPath, timestamps, CommitType, Model / SystemPrompt / AgentPath / MaxTurns (nullable overrides), IsStarred, IsMyDay, IsManual (reminder only the user can do — automation skips it), Notes, ParentTaskId, PlanningSessionId, PlanningSessionToken, PlanningFinalizedAt, CreatedBy, HandlerBaseCommit / HandlerHeadCommit (nullable; review range for a worktree-less "list handler" host task — Mission Control's "Let Claude handle it" — which commits straight into the list's working dir instead of a per-task worktree: HandlerBaseCommit is stamped to the list repo's HEAD when the host task is created, HandlerHeadCommit when it's submitted for review; the Worker's SubmitTaskForReview/GetTaskDiff and the Ui's DetailsIslandViewModel/MergeSectionViewModel fall back to this pair whenever Worktree is null). Legacy values Manual/Planning/Planned/Draft/Waiting were retired; existing rows backfill automatically via the RetireLegacyTaskStatus migration.
  • ListEntity — Id, Name, WorkingDir, DefaultCommitType, CreatedAt, IsManual (reminder list — tasks created here default to IsManual)
  • ListConfigEntity — ListId (PK, 1:1 with list), Model, SystemPrompt, AgentPath, MaxTurns, SessionSkills, VerifyCommand (all nullable). VerifyCommand is an optional post-merge gate (see ClaudeDo.Worker/CLAUDE.md → TaskMergeService): null/blank = today's behavior, no gate.
  • WorktreeEntity — TaskId (PK, 1:1 with task), Path, BranchName, BaseCommit, HeadCommit, DiffStat, MergeCommit (nullable; SHA of the merge commit this worktree's branch produced on the target branch, stamped by TaskMergeService the moment a merge/continue-merge succeeds — the only thing that makes revert_merge possible without heuristically searching git log; null for any worktree merged before this field existed), State (Active|Merged|Discarded|Kept)
  • TaskRunEntity — per-run record (session_id, tokens, turns, result, structured output, exit code, log path, nullable Model — the model the run actually executed with, column model)
  • PrimeScheduleEntity — Id, Days ([Flags] PrimeDays weekday bitmask, stored as days_of_week int), TimeOfDay, Enabled, LastRunAt, PromptOverride, CreatedAt. Recurs on the selected weekdays; no date range.
  • DailyNoteEntity — Id, Date (DateOnly), Text, SortOrder, CreatedAt → table daily_notes
  • WeekReportEntity — Id, StartDate/EndDate (DateOnly), Markdown, GeneratedAt → table week_reports, unique index on (start_date, end_date)
  • TaskAttachmentEntity — Id, TaskId (FK to tasks, ON DELETE CASCADE), FileName, ByteSize, CreatedAt → table task_attachments
  • AppSettingsEntity also carries ReportExcludedPaths (string?, JSON array of excluded path prefixes, column report_excluded_paths), StandupWeekday (int DayOfWeek, default Wednesday, column standup_weekday), and DailyPrepMaxTasks (int, default 5, column daily_prep_max_tasks — hard cap on how many open tasks the daily-prep / "Prime Claude" feature may place in MyDay) , ModelPresets (string?, JSON array of ModelPreset rows, column model_presets), and UsageGateFiveHourPct / UsageGateSevenDayPct (int, defaults 80/90, columns usage_gate_five_hour_pct / usage_gate_seven_day_pct — percentage of the 5h/7d Claude usage window at which the autonomous queue pauses; 0 = gate off; AppSettingsRepository.UpdateAsync clamps both to 0..100)
  • ModelPresets / ModelPreset — per-model run defaults (Model, Effort, MaxTurns): one row per ModelRegistry.Aliases entry, supplying the global effort and max-turns defaults. Parse/Serialize normalize (unknown models dropped, missing aliases filled from Defaults, effort validated, turns clamped 1200) and never throw — a malformed settings row must not stop a run. For(presets, model, fallbackMaxTurns = 30) always returns a usable row: model is resolved through ModelRegistry.TryNormalizeAlias first, so a full CLI model id (e.g. claude-sonnet-4-6) still hits its alias's preset row instead of missing every lookup and falling through; only a model that normalizes to nothing recognized falls back to a synthesized row (EffortRegistry.DefaultLevel + fallbackMaxTurns — callers pass AppSettings.DefaultMaxTurns here so that setting has a real effect instead of a hardcoded number). Ship defaults: haiku medium/20, sonnet high/30, opus high/40, fable high/25.
  • ModelRegistry.TryNormalizeAlias — non-throwing counterpart to NormalizeAlias for the run path: exact alias match, then substring match against a full model id, else null. Never throws, unlike NormalizeAlias (which stays the strict, throwing validator for add_task/planning model input).
  • EffortRegistry — the --effort levels (low|medium|high|xhigh|max) + NormalizeLevel (blank → null = don't pass the flag)
  • SubtaskEntity, AppSettingsEntity, AgentInfo — existing helpers / settings / record for scanned agent files

Repositories

All repositories use EF Core LINQ queries via ClaudeDoDbContext. The atomic Queued -> Running claim lives in the Worker's QueuePicker (uses FromSqlRaw), not here.

  • TaskRepository — CRUD, planning helpers (CreateChildAsync, SetPlanningStartedAsync, DiscardPlanningAsync, UpdateChildAsync), UpdateAgentSettingsAsync (model / system-prompt / agent-path overrides). Status-mutation primitives MarkRunningAsync / MarkDoneAsync / MarkFailedAsync / FlipAllRunningToFailedAsync are internal and called only by TaskStateService in the worker. CreateChildAsync produces children with Status=Idle, PlanningPhase=None; once their parent's PlanningPhase becomes Finalized, the chain coordinator queues them.
  • ListRepository — CRUD, GetConfigAsync / SetConfigAsync (upsert) / DeleteConfigAsync for list_config
  • WorktreeRepository — CRUD, UpdateHeadAsync, SetStateAsync, SetMergedAsync (atomically sets State=Merged and stamps MergeCommit in one update — the only writer of MergeCommit)
  • TaskRunRepository, SubtaskRepository, AppSettingsRepository
  • DailyNoteRepositoryListByDayAsync, ListBetweenAsync, AddAsync, UpdateAsync, DeleteAsync
  • WeekReportRepositoryGetByRangeAsync, UpsertAsync
  • TaskAttachmentRepositoryAddAsync, UpdateAsync, GetAsync(taskId, fileName), ListByTaskIdAsync, DeleteAsync(taskId, fileName), DeleteAllForTaskAsync

Infrastructure

  • ClaudeDoDbContext — EF Core DbContext; configured with WAL mode and foreign keys via UseSqlite options
  • IDbContextFactory — registered in DI; used by singleton consumers (e.g. Worker hosted service)
  • Paths — expands ~ and %USERPROFILE%, resolves relative paths. App root: ~/.todo-app
  • AppSettings — loads ~/.todo-app/ui.config.json (DbPath, SignalRUrl)
  • AttachmentStore — dependency-free file store; default root ~/.todo-app/attachments/<taskId>/. SaveAsync enforces a 5 MB cap and path-traversal/containment guard. Also exposes DeleteFile, DeleteTaskDir, TaskDir, Root, and EnumerateTaskIds (used by the worker orphan sweep). Attachment files live outside git worktrees intentionally.

Git

  • GitService — async wrapper around git CLI (ProcessStartInfo, no shell). Worktree ops (add — serialized to avoid a commondir race —, remove, prune, list paths for branch), branch ops (current, list local, checkout, delete), staging/commit (status porcelain, add-all, add-path, commit via stdin), diffs (working tree, branch vs base, commit range base..head — used to show a merged task's diff after the worktree is gone —, per-file, diff-stat, committed files, has-changes), merge (ff-only, no-ff, abort, mid-merge detection, conflicted files), revert (RevertMergeCommitAsyncgit revert --no-edit -m 1 <sha>, reverts a merge commit against its first parent; RevertAbortAsync; IsMidRevertAsyncREVERT_HEAD presence, mirrors IsMidMergeAsync's MERGE_HEAD), PreviewMergeAsync (non-destructive mergeability check via git merge-tree --write-tree), CountChangedFilesAsync, rev-parse, is-git-repo. Revert never resets/rewrites — it always produces a new commit, because the working directory it operates on is shared with other concurrent sessions.

Schema

Tables: lists, tasks, worktrees, list_config, task_runs, subtasks, app_settings, prime_schedules, daily_notes, week_reports, task_attachments. Managed by EF Core migrations in the Migrations/ folder. The tasks table holds status, planning_phase (default none), and blocked_by_task_id (FK to tasks.id, ON DELETE SET NULL). Migration WeeklyReport added daily_notes, week_reports, and the two new app_settings columns. Migration DailyPrepMaxTasks added the daily_prep_max_tasks column to app_settings (no new tables). Migration AddTaskAttachments created the task_attachments table. Migration AddModelPresetsAndManualFlag added app_settings.model_presets plus the is_manual flags on tasks and lists. Migration AddHandlerCommitRange added the nullable handler_base_commit/handler_head_commit columns to tasks. Migration AddUsageGateAndRunModel added app_settings.usage_gate_five_hour_pct/usage_gate_seven_day_pct (defaults 80/90) and the nullable task_runs.model column. Migration AddWorktreeMergeCommit added the nullable worktrees.merge_commit column. Migration AddVerifyCommand added the nullable list_config.verify_command column. TaskRepository.DeleteAsync and ListRepository.DeleteAsync also delete the on-disk attachment dir(s) via an optional AttachmentStore ctor param (defaults to the production store).

Conventions

  • Enum <-> string mapping via EF Core ValueConverter (configured in IEntityTypeConfiguration<T>)
  • Entity configurations live in the Configuration/ folder
  • Primary keys are init-only strings (GUIDs assigned at creation)
  • All methods are async with CancellationToken where applicable