AttachmentMcpTools exposes add_task_attachment (text or base64),
list_task_attachments, and remove_task_attachment on the external MCP
endpoint, so an agent can prepare reference files (plans, scripts) on a task
that will run later. Re-attaching the same name overwrites; add/remove refuse
on a running task.
TaskRunner appends attached files (absolute paths) to the run prompt as the
read-only Reference files section. Task and list deletes now remove the
on-disk attachment dir eagerly, and a startup AttachmentOrphanRecovery sweep
drops any attachments/<taskId>/ whose task no longer exists (covers list
cascade and planning-discard paths).
TaskAttachmentEntity (+config, cascade FK), TaskAttachmentRepository, and an
AttachmentStore that writes files under ~/.todo-app/attachments/<taskId>/ with
a path-traversal guard and a 5 MB cap. TaskPromptComposer gains an optional
read-only 'Reference files' section. Migration AddTaskAttachments.
GetConflictsAsync/GetMergeConflicts (+ MergeConflicts/ConflictFileContent/ConflictFileDto/ConflictHunkDto DTOs and the now-orphaned GitService.ShowStageAsync) were superseded by the segment-based GetMergeConflictDocuments path and had no production callers. Removes the IWorkerClient member, both test fakes, the lingering test, and updates the Worker/Ui/Data CLAUDE.md surface notes.
Replace the whole-file conflict model with line-level hunks, the
foundation for the full in-app merge editor.
- ConflictMarkerParser: parses git conflict markers (incl. diff3 base)
into ordered stable/conflict MergeSegments; exact round-trip + Compose
- GitService.MergeNoFfAsync passes -c merge.conflictStyle=diff3 so the
working tree carries the merge base in conflict markers
- TaskMergeService.GetConflictDocumentsAsync: reads each conflicted file,
parses into segments, flags binary files
- hub GetMergeConflictDocuments + DTOs (MergeConflictDocumentsDto/
ConflictDocumentDto/MergeSegmentDto), IWorkerClient + both fakes
- tests: 8 parser unit tests + a real-git integration test asserting
line-level hunks with a diff3 base
Optional, opt-in (online_inbox.enabled, default false → zero network).
Worker-side reconcile loop: pull web-created tasks down as Idle, push the
list catalog and the Idle backlog mirror up. Auth behind IOnlineAuthProvider
(StaticTokenAuthProvider default; ZitadelAuthProvider stubbed for Phase 2).
DPAPI refresh-token store. 35 tests, no real network/Zitadel/Claude.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
AddTask, planning CreateChildTask, and SuggestImprovement now accept an
optional alias-validated model (haiku/sonnet/opus; blank = inherit) so the
model is chosen at creation time instead of a follow-up set_task_config call.
The planning, system, and improvement prompts instruct Claude to pick the
cheapest capable model (haiku < sonnet < opus).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- UnifiedDiffParser detects added/deleted/renamed/binary files; diff
modal shows a file list, binary/empty placeholders, and can diff a
merged task by commit range after its worktree is gone
- DetailsIslandViewModel flags children needing attention (failed,
cancelled, awaiting review, or with roadblocks) on the parent
- GitService gains worktree head-commit/range support; planning chain,
merge orchestration, and session manager tweaks with updated tests
- refresh app/installer/worker icons
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add WaitingForChildren to the status tables, document the single parent
lifecycle (planning + improvement) and approve-merges-the-whole-unit across
the root, Worker, and Data CLAUDE.md files.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Collapse TryCompleteParentAsync (planning -> Done) and
TryAdvanceImprovementParentAsync (improvement -> WaitingForReview) into one
TryAdvanceParentAsync that surfaces any WaitingForChildren parent for review
once all children are terminal. Planning parents no longer auto-complete.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The startup-race hardening added a global unique index on lists.name, but
duplicate list names are legitimate and the index broke 8 Worker tests that
seed same-named lists. The seeder race is already handled by the atomic
INSERT...WHERE NOT EXISTS, so the index is redundant. Keep the de-dup migration
step, remove the unique index from config, migration and model snapshot.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add SqliteForeignKeyInterceptor (DbConnectionInterceptor) registered via
OnConfiguring so every IDbContextFactory-created context runs
PRAGMA foreign_keys=ON, not only the MigrateAndConfigure context.
- DefaultListsSeeder: replace TOCTOU read-then-insert with atomic
INSERT … SELECT … WHERE NOT EXISTS — one SQLite writer lock, no race.
- AppSettingsRepository.GetAsync: catch DbUpdateException on the
get-or-create path and re-read so concurrent startup cannot throw.
- Migration 20260609000000_UniqueListName: de-duplicates empty list rows
(startup-race leftovers) then adds a UNIQUE index on lists.name.
- ForeignKeyTests: verifies ON DELETE SET NULL (blocked_by_task_id) is
enforced on a fresh DbContext with no manual PRAGMA call.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Parallel task starts called 'git worktree add' simultaneously; git's shared
.git/worktrees metadata mutation isn't concurrency-safe and one add failed with
'failed to read .git/worktrees/<other>/commondir'. Serialize adds behind a
process-wide gate plus a bounded retry on the transient error.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ReviewFilter matched Status==Done && active worktree, but a successful run
lands a task in WaitingForReview, so the Review virtual list was always empty.
Match WaitingForReview instead; update VirtualFilterTests accordingly.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolved sub-tasks are no longer appended to the prompt. Extracted into a
shared TaskPromptComposer so the UI's description preview can render the same
'what Claude gets' text.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add MaxTurns to ListRepository.SetConfigAsync upsert branch and
TaskRepository.UpdateAgentSettingsAsync; fix positional CancellationToken
call in ConfigMcpTools. Covered by MaxTurnsRoundTripTests (2 tests).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
list_task_lists returns two different ID formats: dashed UUIDs (e.g. "caed660e-109f-4e2a-b055-2c2722bf6fb7") and compact 32-char hex (e.g. "5c2cafcb33f044069ac324ac3fd84a16"). Mixing formats makes equality checks, logging, and lookups error-prone.
Fix: pick one canonical format (recommend dashed UUID) and normalize on write + migrate existing records. Ensure all ID-returning tools emit the same f
ClaudeDo-Task: fa8b69e0-6f8d-41d7-9a41-88db1360544d