Commit Graph
482 Commits
Author SHA1 Message Date
mika kuns 5d362b6973 fix(worker): stop sibling worktrees from landing inside the repo tree
Path.GetDirectoryName returned workingDir itself instead of its parent
whenever list.WorkingDir ended in a directory separator, placing
.claudedo-worktrees inside the target repo's own working tree. Add
WorktreeRootResolver to normalize the trailing separator before deriving
the sibling root and to guard that the resulting worktree path never
lands inside workingDir, used by both WorktreeManager and
PlanningSessionManager. Add a startup sweep (LegacyWorktreeFolderRecovery)
that warns when a list's working dir already contains a leftover
.claudedo-worktrees folder from before the fix.
2026-08-10 11:58:34 +02:00
mika kuns 1b7f7f38ae feat(prime): wire prompt_override into the daily prep prompt
The prime_schedules.prompt_override column existed end-to-end but was
never populated or read. DailyPrepPrompt.BuildPrompt now takes an
optional override and appends it as an extra paragraph after the fixed
prompt (additive, never a replacement, so a user can't disable the
required get_daily_prep_candidates/set_my_day flow). PrimeRunner passes
schedule.PromptOverride through. The Prime tab in Settings now has a
multiline field per schedule wired to a new PromptOverride property on
PrimeScheduleRowViewModel (blank persists as null).
2026-08-10 11:57:54 +02:00
mika kuns 29c79e8b8c fix(worker): stage only resolved conflict paths in continue_merge
git add -A ran against the shared target checkout, sweeping in
whatever untracked or unrelated modified files a concurrent session
left there and committing them into an unrelated merge. Stage exactly
the paths that were reported conflicted and resolved instead.
2026-08-10 11:56:37 +02:00
mika kuns 6a2a19cc9e docs(worker): record the findings store and fix the git-test skip convention 2026-08-10 10:47:17 +02:00
mika kuns 696abd0a7a feat(ui): toggle whether the findings folder is committed 2026-08-10 10:43:12 +02:00
mika kuns f717742379 docs(runner): correct the findings-pointer comment 2026-08-10 10:33:58 +02:00
mika kuns 87397ecfa9 feat(runner): point autonomous runs at the findings index 2026-08-10 10:32:46 +02:00
mika kuns 519f64c02e feat(mcp): add save_finding to the in-task server 2026-08-10 10:27:13 +02:00
mika kuns b312095fb2 refactor(mcp): trim the save_finding description to the style budget 2026-08-10 10:21:30 +02:00
mika kuns 2d4288abca feat(mcp): add save_finding to the external server 2026-08-10 10:19:45 +02:00
mika kuns 9a09dd6186 feat(findings): resolve the target store from task or list 2026-08-10 10:13:29 +02:00
mika kuns 4d997a5f99 refactor(git): share the info/exclude writer with the findings store 2026-08-10 09:59:42 +02:00
mika kuns d3c2e2e7c8 feat(findings): add the .claudedo findings store 2026-08-10 09:55:54 +02:00
mika kuns df9e85a28b Merge branch 'worktree-phase1-reaktivitaet' 2026-08-07 11:01:54 +02:00
mika kuns 7eeb8f5086 feat(usage): split throttle thresholds per bucket, add draggable gauge markers 2026-08-07 11:01:45 +02:00
mika kuns b0c23a2bf6 Merge branch 'main' into worktree-phase1-reaktivitaet 2026-08-07 10:51:48 +02:00
mika kuns ac586797ce Revert "fix(data): give both processes a SQLite busy timeout"
This reverts commit f62dbb9239.
2026-08-07 10:11:52 +02:00
mika kuns 66630d5ce2 chore(worker): drop the unsubscribed RunCreated broadcast 2026-08-07 09:55:21 +02:00
mika kuns 3acb1cba8f fix(worker): broadcast TaskUpdated after online-inbox import 2026-08-07 09:46:43 +02:00
mika kuns a7a3545e2b fix(worker): broadcast WorktreeUpdated when a worktree is created 2026-08-07 09:40:56 +02:00
mika kuns c1184adc92 fix(worker): fail the task when a queue slot runner throws
RunInSlotAsync only logged an unexpected exception, leaving a task stuck
Running in the DB forever with the UI never notified (the raw-SQL queue
claim that put it there never broadcasts). Cancellation is handled
separately and left alone, since the cancel path already wrote the
terminal status.
2026-08-07 09:35:26 +02:00
mika kuns c792765ed3 refactor(mcp): rewrite external MCP tool descriptions for trigger clarity
Every tool description now leads with what the tool does AND when to reach for
it, since MCP clients rank tools by that text. Per-parameter prose moved onto
the parameters as [Description], exhaustive result-shape enumerations and
design/history rationale dropped, and the repeated boilerplate clauses
(lean-task-ref, batch cap, refused-while-Running) pulled into McpToolDocs,
which also documents the style for future tools.

Tool-level description text: 20494 -> 13605 chars (-34%); combined with the new
parameter descriptions 18517 (-10%).

Closes gaps that caused wrong calls rather than just verbose ones:
- list_task_attachments returns metadata only, no file content
- run_task_now shares continue_task's single override slot and throws when busy
- list_runs is ordered oldest-first and feeds get_run
- workingDir on create_list/update_list is an existing local git repo path,
  unvalidated until the first task run
- get_task_worktree's behind=0 also means the main ref was unreachable

Removes get_task_status_values: a whole tool entry for static reference text.
GetTask's description is now the canonical place for status meanings.
2026-08-07 09:25:45 +02:00
mika kuns f62dbb9239 fix(data): give both processes a SQLite busy timeout 2026-08-07 09:13:21 +02:00
mika kuns a7ff1b3a2f refactor(prompts): split the list-handler prompt per session phase
Both merge-helper ConPTY sessions loaded PromptKind.MergeHelper, so the
post-handoff session received the phase 0-2 dedupe/enhance instructions and was
told to ignore them by its brief alone.

Split into MergeHelperTriage (phases 0-2 + handoff) and MergeHelperExecute
(phases 3-5), so each session carries only its own phases. Consolidated the
generic ask-the-user rule to one place per prompt, scoped Phase 5's summary to
what the execute session actually knows, and moved the dedupe/enhance bilanz to
the triage handoff. Regression guards assert neither prompt carries the other's
phase headings and that the shared-checkout git rule stays in execute.
2026-08-06 21:00:41 +02:00
mika kuns 4a7b00ed53 fix(worker): strip trailing separator from the list repo in ConPTY launch args
The ConPTY host flattens LaunchSpec.Args into one Windows command line and quotes
each token, so a list working dir stored as "C:\repo\" produced the token
"C:\repo\" -- whose trailing backslash escapes its own closing quote. Everything
after it collapsed into --add-dir's variadic list, including
--append-system-prompt-file and the positional kickoff, so "Let Claude handle it"
opened a session with no prompt at all and the CLI warned that brief.md is not a
directory. Only user-supplied working dirs can carry a trailing separator; the
session dirs the worker builds never do.
2026-08-06 15:53:46 +02:00
mika kuns 109e85da83 fix(worker): honor RunCancellationRegistry.Register's return value at both dispatch sites
Register(taskId, cts) already refuses (and logs) a double-registration, but
both call sites discarded the bool and dispatched anyway under an
unregistered CTS. If the loser then unregistered the winner's CTS during
its own cleanup, TryCancel could silently no-op against a live process.

- OverrideSlotService.StartInSlot now fails RunNow/ContinueTask loudly
  (throws) when it loses the registration race instead of registering
  over — or silently proceeding despite losing to — the queue picker.
- QueueService's picker loop retries registration briefly (the loser's own
  claim-attempt-then-unregister resolves fast) before dispatching; if
  registration never resolves it marks the already-claimed row Failed
  instead of running it unregistered.
- RunCancellationRegistry.Unregister already had compare-and-remove
  semantics (TryRemove(KeyValuePair)), so a loser's cleanup could not have
  removed the winner's CTS once registration correctly failed.

Added regression tests exercising the real registry through both dispatch
paths: RunNow losing the registration race throws without disturbing the
winner, the picker's retry succeeds and TryCancel reaches the live run when
the loser unregisters in time, and the picker fails the task instead of
running unregistered when it never does.
2026-08-06 14:34:48 +02:00
mika kuns ddf68d66cc Merge claudedo/1f4f59b14e4d481d97e843b6b2014af5 2026-08-06 13:48:37 +02:00
mika kuns bd83cac57d Merge claudedo/973ea49ec0a642ea9b1ddcff9b71b6fe 2026-08-06 13:47:35 +02:00
mika kuns b2940bf1ff Merge claudedo/c4930a56575e45b2ae9be47b1c49eb39 2026-08-06 13:47:11 +02:00
mika kuns 7f03f04268 Merge branch 'claudedo/9c0bff67147148f3beeee180e70b5503' 2026-08-06 13:46:52 +02:00
mika kuns cc90600f72 Merge claudedo/81b054800dc4446598685fd04fefddb5 2026-08-06 13:45:15 +02:00
mika kuns 79f90a9a8e fix(worker,ui): block cancelling a task while its unit merge is draining
TaskStateService.CancelAsync allowed cancelling a WaitingForReview task
even while PlanningMergeOrchestrator was mid-drain on it: ApproveReview
awaits the whole multi-subtask merge synchronously, so a concurrent
CancelReview (UI or MCP) could flip the parent to Cancelled while the
orchestrator kept merging children onto the target branch, then
FinalizeParentDoneAsync would find the parent no longer WaitingForReview
and give up - leaving the merged diffs stranded with no rollback.

CancelAsync now rejects with a clear reason when HasActiveMerge(taskId)
is true. TaskStateService can't take PlanningMergeOrchestrator as a
direct constructor dependency (circular back to ITaskStateService), so
it takes a lazily-resolved Func<IActiveMergeState> instead, mirroring
the existing Func<ITaskStateService> cycle-break already used for
PlanningChainCoordinator.

UI polish: DetailsIslandViewModel.IsMergeDraining gates
CancelReviewCommand's CanExecute (same shape as
WorktreesOverviewModalViewModel.IsMerging), and the command's catch now
raises ErrorReported instead of swallowing the rejection silently.
2026-08-06 13:38:02 +02:00
mika kuns efee14780b fix(worker): filter the transliterated 'fuer' stopword, not the untransliterated 'fur'
NormalizeTitleWords transliterates umlauts before tokenizing ('für' ->
'fuer'), but TitleStopWords listed 'fur' -- a token the pipeline can
never produce -- so 'für' was never filtered as filler and could push
unrelated titles past the near-duplicate threshold.
2026-08-06 13:37:35 +02:00
mika kuns 774f9d3d13 fix(worker): claim Running before creating run resources
TaskRunner.RunAsync created the worktree (PrepareRunDirectoryAsync)
before claiming Running via StartRunningAsync. RunNow dispatches by
task id with no atomic claim of their own, so a Queued task racing
the queue picker's atomic SQL claim could hit WorktreeManager's
branch-collision self-heal, which force-removes and recreates the
winner's live worktree mid-run.

Move the claim before any resource creation and bail out immediately
when it's rejected. OverrideSlotService.RunNow also fast-rejects a
task already Running in the DB (defense in depth). RunCancellationRegistry
now refuses (and logs) a double registration instead of silently
overwriting the first CTS, so a losing dispatch's cleanup can no
longer unregister the winner's cancellation token.
2026-08-06 13:33:23 +02:00
mika kuns 1a988ff4fc feat(worker,data): persist interactive session id so a closed/aborted ConPTY session can be resumed
Generates the claude session id up front (--session-id <guid>) for a fresh interactive task
session and persists it to TaskEntity.InteractiveSessionId before launch, so an abort at any
point still leaves a resumable id. BuildForTaskAsync now resumes this task's own last
interactive session in preference to the latest autonomous run's session, but never across a
freshly (re)created worktree.
2026-08-06 13:27:52 +02:00
mika kuns 9e2a15e421 fix(worker,ui): route details-pane task delete through worker to advance blocked parents
Deleting a child from the details pane hard-deleted straight from the UI
process via TaskRepository, bypassing TaskStateService.TryAdvanceParentAsync
entirely. Deleting the last child of a WaitingForChildren parent that way
left it wedged forever. WorkerHub.DeleteTask now mirrors the MCP delete_task
tool (running-task guard, FK-friendly error, advance-parent call), and the
UI goes through it.

TryAdvanceParentAsync also short-circuited when zero children remained,
treating "no children left" as "nothing to evaluate" instead of "all done" -
removed the early return so an empty child list (vacuously) counts as all
terminal.
2026-08-06 13:25:49 +02:00
mika kuns 58741c2bd6 fix(worker): advance planning chains and parents on stale-Running recovery
RecoverStaleRunningAsync bulk-flipped Running rows to Failed via raw
ExecuteUpdate, skipping the chain/parent side effects every other terminal
transition triggers. After a worker crash mid-run of a planning/improvement
child, the chain successor's BlockedByTaskId was never cleared and a
WaitingForChildren parent could wedge forever with no event left to
re-check it. Now each recovered task runs the same OnChildTerminalAsync
side effects (chain advance + parent advance) as FailAsync, best-effort.
2026-08-06 13:16:58 +02:00
mika kuns 20bce9b424 Merge branch 'claudedo/75514ee68371402b814638290af47cca' 2026-08-06 12:11:38 +02:00
mika kuns 5d1d2d89d0 fix(ui): suppress auto-open conflict resolver during MCP-driven merges
review_task/continue_merge on a planning parent always leaves conflicts in
the tree, and the UI auto-opened the resolver on every PlanningMergeConflict
broadcast regardless of who started the merge -- so a running Claude session
resolving a unit-merge conflict could race a human editing the same shared
checkout in a resolver window neither of them asked for.

PlanningMergeOrchestrator.StartAsync now takes an externallyDriven flag (set
by ExternalMcpService's MCP-driven review_task path, left false for the UI's
ApproveReview) that rides along on the PlanningMergeConflict broadcast. The
UI only auto-opens the resolver when it's false; otherwise it shows a
persistent banner with a manual "Open resolver" button, cleared on
PlanningMergeAborted/PlanningCompleted. A new GetActiveExternalConflictsAsync
query (checked against GitService.IsMidMergeAsync rather than the in-memory
flag alone) lets the UI resync the banner on reconnect instead of trusting a
one-shot broadcast that isn't replayed after a restart.

The childless single-task conflict path was checked and needed no change --
it only broadcasts the generic TaskUpdated, never PlanningMergeConflict.
2026-08-06 12:04:52 +02:00
mika kuns 0358bcf8b2 Merge branch 'claudedo/001ee94a1d8d46b0906f7466ea6beb32' 2026-08-06 12:04:47 +02:00
mika kuns e59f6c2438 fix(worker): surface empty review ranges and blocked children over MCP
preview_merge/preview_merge_set now report isEmpty (ahead==0, or
HandlerBaseCommit==HandlerHeadCommit for a worktree-less list-handler
task) instead of leaving an empty branch indistinguishable from a small
one. preview_merge also stops throwing for worktree-less handler tasks,
falling back to their fixed commit range. review_task's parent approve
returns emptyChildren, naming the Done children whose review range
contributed nothing before the unit merge lands. TaskRefDto/TaskDto now
expose roadblockCount so a CLAUDEDO_BLOCKED child is identifiable over
MCP, since it still reaches Done per the unified parent model.
2026-08-06 11:54:46 +02:00
mika kuns abac5e5150 Merge claudedo/c97dcaafbb3542bda9c670e502ffffb6 2026-08-06 11:43:07 +02:00
mika kuns 7240b39f16 Merge claudedo/3a3e87648400492bba6e9643007703db 2026-08-06 11:41:55 +02:00
mika kuns 4a60c2bfa8 Merge branch 'claudedo/0733742e0da8407d8e2be1bc5886bf9b' 2026-08-06 11:40:47 +02:00
mika kuns f3e16412f4 Merge branch 'claudedo/c1d2a92d8d8c4705ac93d5f0b2cacaa0' 2026-08-06 11:38:32 +02:00
mika kuns 8bc7bc0c4f feat(ui): warn when the running worker predates the selected repo's merged HEAD
Stamps ClaudeDo.Worker's build with its exact git SHA (SourceRevisionId ->
InformationalVersion) and exposes it via a new GetWorkerBuildInfo hub call.
For the currently selected list, the shell compares that SHA against the
list's git HEAD (GitService.IsAncestorAsync) and shows a persistent footer
banner -- never auto-clearing, never shown on an unrelated repo or when the
ancestry can't be determined -- so "verified against a merge" claims aren't
silently made against a stale process. No auto-restart; the banner just
offers the existing RestartWorkerCommand.
2026-08-06 11:36:26 +02:00
mika kuns af16830060 feat(worker): add treatWaitingForChildrenAsBusy to wait_for_task_change
WaitingForChildren already counted as "changed" since it's outside Queued/Running,
so waiting on a planning parent returned immediately even though its children were
still running. The new opt-in flag (default false, unchanged behavior) keeps polling
through WaitingForChildren and only reports changed once the parent reaches
WaitingForReview or a terminal status.
2026-08-06 11:32:00 +02:00
mika kuns b54a133c16 fix(worker,ui): clean up three review leftovers from the list-handler run
Remove the redundant TaskUpdated broadcast in TaskRunner.ContinueAsync's
queue-claim path, consolidate InteractiveLaunchSpecService's seven
MCP_TOOL_TIMEOUT literals into one constant (fixing the merge-helper handoff
spec's stale 200000ms value), and surface OpenQuickClaudeSession's two
failure cases via ErrorReported/footer instead of a silent no-op, with a
less ambiguous icon.
2026-08-06 11:31:03 +02:00
mika kuns 8247a749a0 Merge claudedo/f9ee427d9f644715bafb55857ad8c3e8 2026-08-06 11:29:49 +02:00
mika kuns f8c48e2ed7 fix(worker): make list_tasks/batch_get_tasks lean by default
list_tasks on a list of ~100 verbosely-described tasks could return
390k+ characters in one call, blowing past the caller's token limit.
Both tools now default to lean TaskRefDto references (no
Description/Result) and take an includeDescription flag to opt back
into the full TaskDto payload — same flag-alongside-nullable-payload
idiom already used by BatchGetTaskResult/TaskConfigResult. get_task is
unchanged.
2026-08-06 11:25:32 +02:00