Commit Graph
447 Commits
Author SHA1 Message Date
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
mika kuns 3e07536ee9 feat(worker): warn on near-duplicate titles in add_task/batch_add_tasks
add_task and batch_add_tasks now report up to 3 open (non-terminal)
tasks in the same list with a strongly overlapping title, so a
parallel agent can notice and mention a likely duplicate instead of
silently creating one. The task is always created regardless. Uses a
cheap normalized-word overlap heuristic (no embeddings/LLM call),
robust to German umlaut/digraph spelling variants. Breaking change:
AddTask now returns AddTaskResult { task, possibleDuplicates } instead
of a bare TaskRefDto; BatchAddTaskResult gained a PossibleDuplicates
field.
2026-08-06 11:24:17 +02:00
mika kuns 75561930d7 chore(claude-do): RepoImportFolders auf den WorkerHub-Pfad umziehen (Ui schrei
## Befund (am Code verifiziert 2026-08-06)

`AppSettingsEntity.RepoImportFolders` (`src/ClaudeDo.Data/Models/AppSettingsEntity.cs:26`, JSON-Array als TEXT) ist **nicht** Teil des `AppSettingsDto` in `src/ClaudeDo.Worker/Hub/WorkerHub.cs:32-49` und wird weder von `GetAppSettings` (~:375-393) noch von `UpdateAppSettings` (~:396-424) transportiert.

Stattdessen liest und schreibt die Ui das Feld dire

ClaudeDo-Task: 64fbe15d-ae7e-4d81-b8c1-045baa7e3c87
2026-08-06 11:18:00 +02:00
mika kuns f106c890b3 fix(ui): refresh the run session id live, close the stale handoff pane
A task selected before its run started kept LatestRunSessionId null, so the
roadblock reply box and Continue stayed dead until it was re-selected.

The handoff left the Phase 1-2 tile open so its last message could be read, but
its process is gone by then and the terminal renders empty -- a dead
placeholder. It is closed on handoff now.

Also drops the WARN flood from worktree cleanup (already-unregistered worktree
and already-deleted branch are normal outcomes, not failures) and replaces the
fixed sleep in UsageGate_TransitionLogging_FiresOncePerChange with the polling
helper that already sits three lines below it in the same file.
2026-08-06 10:20:54 +02:00
mika kuns 091aca521f fix(merge): apply the verify gate to worktree-less approvals and report it everywhere
ApproveAndMergeAsync short-circuits to Done whenever a task has no active
worktree -- which is exactly how a list-handler task works, since it commits
straight into the list working dir. The verify command was skipped for the run
that lands the most on the target branch at once; the loaded command was even
discarded at the destructuring. It now runs under the same per-repo gate as the
merge path before the task may reach Done.

The two merge entry points that did not know verify_failed reported it as
"Unknown status: verify_failed" (merge modal, dropping the command output) and
as a generic Failed (worktrees batch, claiming the merge never happened).
2026-08-06 10:20:53 +02:00
mika kuns b6791265d1 fix(planning): recover the planning session id from the on-disk transcript
The interactive planning TUI never reports its claude session id back, so
planning_session_id stayed NULL and UpdatePlanningSessionIdAsync had no caller
at all -- ResumeAsync always threw "No Claude session ID captured yet".

Resume now looks the id up in the transcript Claude Code writes to
~/.claude/projects/<encoded cwd>/<sessionId>.jsonl for the planning worktree
(newest file wins; a worktree hosts exactly one session) and persists it, so
the next resume is a plain DB read. No transcript -> a clear "cannot resume"
instead of resuming the wrong session.
2026-08-06 09:10:50 +02:00
mika kuns ad2d91f658 Merge claudedo/ccd650a8d2b04a7092e81ed07c16dbe0 2026-08-05 22:46:37 +02:00
mika kuns cdfd0614dd Merge claudedo/616befd0f8a64dcb9e3dac9d6499de16 2026-08-05 22:45:32 +02:00
mika kuns 1d258a3e2c Merge claudedo/123b0241b5e94b69bfa592fde89c51aa 2026-08-05 22:44:47 +02:00
mika kuns adc0eaebb1 Merge claudedo/01556d1f8af54fe2afa4bced3a209121 2026-08-05 22:41:44 +02:00
mika kuns 860201017c fix(worker): broadcast TaskUpdated on queue-claimed task start
QueuePicker's raw-SQL Queued->Running claim bypasses TaskStateService.StartRunningAsync,
the only other place that broadcasts TaskUpdated on this transition, so the task-list
badge stayed on "Queued" until the run finished. Send TaskUpdated for alreadyClaimed
dispatches in TaskRunner.RunAsync/ContinueAsync to close that gap.
2026-08-05 20:53:55 +02:00
mika kuns d43b5fcefc feat(worker-mcp): raise wait_for_task_change timeout, expose queue slot state
MaxTimeoutSeconds was 170s against runs that take tens of minutes, forcing
a dozen full-context wait rounds per long-running batch. Raise it to 900s
and raise MCP_TOOL_TIMEOUT in lockstep (ClaudeProcess + every
InteractiveLaunchSpecService launch spec) to 930000ms so the client
connection actually stays open that long instead of aborting first.

Add get_queue_state (QueueStateMcpTools): configured vs. effective
parallel-slot count (via QueueService.GetSlotCountsAsync, extracted from
the former GetEffectiveMaxParallelAsync), active slots with taskId +
startedAt including the run_task_now override slot, and queued tasks in
pick order -- so a caller can observe queue occupancy instead of inferring
it from maxParallelExecutions.
2026-08-05 20:47:57 +02:00
mika kuns a768bc4163 feat(worker): add get_effective_run_config MCP tool
Adds a read-only get_effective_run_config(taskId) tool that reports the
model/max-turns/effort/permission-mode/agent-path/system-prompt/skills a
task will actually run with, each tagged with its source (task/list/
preset/global), plus max-turns' raw requested value and clamp status.

Extracted the model/max-turns/agent-path resolution out of
TaskRunner.ResolveConfigAsync into EffectiveRunConfigResolver so the run
path and the new reporting tool share one codepath instead of risking
drift, per docs/explore-notes/worker-task-pipeline.md's max-turns trap.
2026-08-05 20:39:35 +02:00
mika kuns ecba12997a perf(worker): stop echoing task description from writing MCP tools
update_task, update_task_status, add_task, add_subtask, set_my_day,
abort_merge, review_task, and their batch variants now return a lean
TaskRefDto (id/listId/title/status/sortOrder/isMyDay) instead of the
full TaskDto. Those tools were re-sending the caller's own description
text back on every call, wasting a large share of session context on
list-handler-style runs. get_task/list_tasks/batch_get_tasks are
untouched and still return the full DTO.
2026-08-05 20:37:30 +02:00
mika kuns 4ef01274f9 feat(list-handler): hand off to a fresh session after Phase 2
The merge-helper ("Let Claude handle it") system prompt now calls a new
handoff_list_handler MCP tool once every surviving task is enhanced,
instead of continuing into Phases 3-5 in the same session -- avoiding
paying for Phases 0-2's dedupe/rewrite context on every polling round of
the run/review/merge phases.

The tool broadcasts HandoffRequested; Mission Control opens a second
ConPTY tile for the SAME handler task id (no new task, HandlerBaseCommit
untouched) running a fresh handoff brief that starts at Phase 3. The
original tile stays open. Adds PromptKind.MergeHelperHandoff,
InteractiveLaunchSpecService.BuildForMergeHelperHandoffAsync, and the
GetMergeHelperHandoffLaunchSpec hub method.
2026-08-05 20:27:39 +02:00
mika kuns bdee731376 Merge branch 'claudedo/f359858ac98a439593e459df9c5d0a5d' 2026-08-05 16:49:08 +02:00
Mika Kuns d15aa27707 fix:Improve Claude Mds 2026-08-05 16:46:02 +02:00
mika kuns 2700c3d817 fix(usage): stop 429s with an activity-dependent poll cadence + manual refresh
The usage monitor polled the undocumented OAuth usage endpoint every 60s and
earned 429s. It now polls every 5 min while any task is Running and every
15 min while idle (usage_poll_interval_active_seconds / _idle_seconds, both
clamped to >= 60; the old single usage_poll_interval_seconds key is gone).

A 429 comes back as UsageRateLimitedException carrying Retry-After and adds
exponential backoff on top, capped at 30 min and never shorter than the normal
cadence; the strike count resets on the first success. The schedule arithmetic
is the pure static UsagePollSchedule.NextDelay.

Since the idle cadence is slow on purpose, WorkerHub.RefreshUsage drives
UsageMonitorService.RefreshNowAsync behind a Refresh now button in the Usage
Monitor modal: an out-of-band poll that pushes the loop's next-due time out so
no double poll follows, with a 10s cooldown so click-spam can't earn a 429.

Staleness now measures against the slower (idle) interval so an idle worker
isn't flagged stale just for not polling.
2026-08-05 16:40:34 +02:00
mika kuns f6cb8250bb fix(tests): pass FakeTranscriptUsageReader to TaskRunner in FailureDiagnosisTests
The cache-token task added an ITranscriptUsageReader ctor param to TaskRunner
while the failure-diagnosis task added this test file. Both branches were green
in isolation; the merged result did not compile.
2026-08-05 16:15:50 +02:00
mika kuns 79a1403834 Merge branch 'claudedo/87105f5ec4f44af4ae6089cd2e153e3c' 2026-08-05 16:10:37 +02:00
mika kuns 91eb2996c8 Merge branch 'claudedo/2de2f008758640b3a75e95719b1555bf' 2026-08-05 16:07:10 +02:00
mika kuns 19003e68b6 Merge claudedo/38394081d47048fea82317c6c52e01a5 2026-08-05 16:05:19 +02:00
mika kuns 2a217336e8 Merge claudedo/1b599d6711914658b9857b3df996f6e0 2026-08-05 16:03:14 +02:00
mika kuns b153869216 fix(prompts): stop on-disk prompt overrides from freezing forever
EnsureExists blindly seeded ~/.todo-app/prompts/*.md with the bundled
default and never revisited it, so any file created by opening the
Files settings tab shadowed every later default change permanently
(SuggestImprovement/AskUser sections never reached real runs since
2026-06-04). PromptFiles now hashes what a file was seeded/saved with
in prompts/.defaults.json: Classify() tells missing/current-default/
known-past-default/edited apart, ReconcileStaleDefaults() drops files
that only ever matched a superseded default, and real edits are left
alone and surfaced in the Files tab with a diff + reset-to-default
action. QuarantineOrphans() moves stale-named leftovers (agent.md,
planning.md) into prompts/_orphans instead of silently deleting them.
Wired as a Worker startup sweep (PromptFileRecovery) alongside the
existing OrphanRecovery/AttachmentOrphanRecovery services.
2026-08-05 15:59:03 +02:00
mika kuns 2ebdadff08 fix(worker): surface the real reason a Claude run failed instead of a generic exit-code message
StreamAnalyzer now reads subtype/terminal_reason/errors from the CLI's result
event, and TaskRunner builds a specific ErrorMarkdown from them: max_turns names
the turn budget and points at set_task_config + requeue, api_error passes
through the provider's own message (which carries the reset time), and any
other terminal_reason is appended to the previous generic text instead of
staying invisible. Falls through to the old "exited with code N and no result"
text when there's no terminal_reason at all (a real crash).
2026-08-05 15:55:09 +02:00
mika kuns a201d3f43d chore(claude-do): UsageGate: Parallelitaet stufenweise drosseln statt erst bei
## Kontext: Limits sind Fenster, nicht Summen

Die Runs laufen ueber das Claude-Abo. Limits greifen pro 5h-Fenster und pro 7 Tage. Nicht die Wochensumme tut weh, sondern dass ein Agent-Burst ein Fenster leerraeumt, in dem Mika selbst interaktiv arbeiten will.

## Messgrundlage (alle Transcripts unter ~/.claude/projects)

Agent-Runs sind ueber die ganze Historie nur **18,4 %** des Account-Verbrauch

ClaudeDo-Task: 87105f5e-c4f4-4af4-ae60-89cd2e153e3c
2026-08-05 15:53:15 +02:00
mika kuns 7d3d6d7b54 fix(worker): record real raw token usage per run, not the uncached remainder
task_runs.tokens_in only ever held the API's uncached "input_tokens" field
(off by a factor of ~400,000 on a resumed session), and tokens_out summed
only the last result event instead of the whole session. TaskRunner now
reads each run's cache-read/cache-write/input/output totals from the
session transcript via a new ITranscriptUsageReader.ReadSessionTotalsAsync,
storing the delta against prior runs on the same session so a --resume
doesn't double-count. New task_runs.cache_read_tokens/cache_write_tokens
columns; the Session tab now shows the raw total (what actually counts
against the 5h/7d usage limit) with a breakdown tooltip.
2026-08-05 15:44:20 +02:00
mika kuns 08ac8bf7b1 feat(worker): clamp max-turns to a configurable ceiling
Runaway sessions were the single biggest cost driver: model_presets was
never persisted (stayed code-only), default_max_turns shipped at 100, and
ResolveMaxTurns had no upper bound, so a task/list override could run
hundreds of turns unchecked.

- TaskRunner.ResolveMaxTurns now clamps the resolved value to
  AppSettings.MaxTurnsCeiling (new column, default 80) and logs a warning
  with task id / requested / effective value when it clamps.
- default_max_turns default lowered from 100 to 40 (entity, EF config,
  and the seeded row via the new AddMaxTurnsCeiling migration).
- AppSettingsRepository.GetAsync backfills model_presets with the
  shipping defaults on first read instead of leaving the column null.
- Settings > General's per-model preset table and the task/list agent
  editor now show a hint when a set max-turns value exceeds the ceiling.
2026-08-05 15:40:02 +02:00
mika kuns 7cbd4e66ae feat(ui): add usage pill to footer and mission control header
Adds the IWorkerClient/WorkerClient usage surface (GetUsageSnapshot,
GetModelUsage, GetTaskUsage, UsageUpdated event) and a shared
UsagePillViewModel hosted once in IslandsShellViewModel (footer) and
once in MissionControlViewModel (header), showing "5h X% · 7d Y%"
with warn/blocked/stale states via existing design tokens. The
OpenMonitorCommand is wired but currently a no-op, pending the usage
monitor modal.
2026-08-05 13:16:29 +02:00
mika kuns d4cd202460 feat(worker): expose usage/model-usage hub surface and persist run model
Adds GetUsageSnapshot/GetModelUsage/GetTaskUsage to WorkerHub (backed by a
shared UsageSnapshotBuilder), a UsageUpdated broadcast fired after every
UsageMonitorService poll cycle, and records the resolved model on each
task_runs row so per-model/per-task usage can be reported from history.
2026-08-05 12:37:50 +02:00
mika kuns 1aaa40b894 Merge branch 'claudedo/0b2fbb48d44c41558c21d3464c0bd5c2' 2026-08-05 12:11:55 +02:00
mika kuns 3e7126b3f2 Merge branch 'claudedo/9e3071992eca4eb79057d2c675cc57ca' 2026-08-05 12:05:18 +02:00
mika kuns 10e561f336 feat(worker): record merge commit SHA and add revert_merge tool
Persists the merge commit SHA on WorktreeEntity for every successful
single-task and unit merge, and adds a TaskMergeService.RevertMergeAsync
+ revert_merge MCP tool that undoes a merged task's merge via
`git revert -m 1` (never reset/rewrite, since the target checkout is
shared). Rejects cleanly when there's no recorded SHA, the repo is
mid-merge/mid-revert, or the target has foreign uncommitted changes;
a conflicting revert aborts immediately. Also exposes the new
mergeCommit field via get_task_worktree.
2026-08-05 11:46:51 +02:00
mika kuns 32c019bd5d Merge claudedo/20c78c9558cf4c4fa598a00c7cac374f 2026-08-05 11:42:05 +02:00
mika kuns 65db1cdefa feat(planning): let CreateChildTask set maxTurns on child tasks
Planning sessions could already steer a subtask's model but not its turn
budget, so a visibly large subtask would still die at the global default
turn limit. maxTurns is optional (default null = inherit list/global
default, matching model); 0/negative values are rejected as invalid input,
consistent with the existing model-alias validation.
2026-08-05 11:33:16 +02:00
mika kuns 42c70fb28c Merge branch 'claudedo/99732497092746d193b80b8296804374' 2026-08-05 11:21:12 +02:00
mika kuns c9ba1e2645 feat(worker): add post-merge verification gate for list merges
Per-list optional VerifyCommand (list_config.verify_command) runs via
VerifyCommandRunner in the list's working dir right after a successful
merge/continue-merge, before the task is allowed to reach Done. A
non-zero exit or timeout leaves the merge in place but keeps the task
out of Done and reports StatusVerifyFailed with an output excerpt
through MergeResultDto/review_task; no command configured behaves
exactly as before. Merges against the same repo are now serialized
per working dir so a running verify can't be interrupted by a second
merge landing mid-build. Adds the field to the List Settings modal
(en/de localized) and covers success/failure/timeout in
TaskMergeServiceTests + VerifyCommandRunnerTests.
2026-08-05 11:19:22 +02:00
mika kuns 394febadeb feat(worker): add preview_merge and preview_merge_set MCP tools
Give an autonomous reviewer a non-destructive merge-tree preview
(status/conflicts/changedFileCount/behind) for a task's worktree branch,
plus a file-overlap check across a batch of tasks so same-file collisions
between sibling branches are visible before merging. MergeHelperDefault's
Phase 4 now calls preview_merge_set before merging a batch.
2026-08-05 11:15:43 +02:00
mika kuns 1ee21b560d Merge task branch for: Worker: UsageGate — Queue ab Schwelle pausieren 2026-08-05 11:11:24 +02:00
mika kuns 8f8c2a65b2 feat(claude-do): Worker: UsageGate — Queue ab Schwelle pausieren
> **Stand 2026-08-05 (List-Handler):** Der Roadblock aus dem letzten Lauf ist erledigt. Beide Voraussetzungen sind jetzt auf `main` gemerged: die `app_settings`-Schwellen `UsageGateFiveHourPct`/`UsageGateSevenDayPct` (Merge-Commit `b1efcdc`) und `UsageState`/`IUsageClient`/`UsageMonitorService` unter `src/ClaudeDo.Worker/Usage/` (Merge-Commit `b126a21`). Dein Worktree ist frisch von diesem `main`

ClaudeDo-Task: 06a7cc32-6ab7-4758-98f4-bee77149b2bf
2026-08-05 11:10:30 +02:00
mika kuns 6c5acd09b9 Merge claudedo/0020cd0c4696431996a158f8e1b91cba 2026-08-05 11:05:36 +02:00
mika kuns a7b88098ab Merge claudedo/81e378014c31459c99eb2b140c36ada8 2026-08-05 11:04:53 +02:00
mika kuns ee84a75bd7 Merge claudedo/05827da5ecde413e9a2fe8edd45c24a8 2026-08-05 11:03:56 +02:00
mika kuns c871f35513 chore(worker): external MCP tools return explicit results instead of empty responses
set_task_config/set_list_config now echo which fields were set vs cleared, get_list_config/get_task_config return an explicit found=false instead of null, and delete_list/run_task_now/reset_failed_task/remove_task_attachment return a confirmation record — matching the found/ok convention already used by batch_get_tasks and get_task_log.
2026-08-05 10:58:28 +02:00