Commit Graph
1598 Commits
Author SHA1 Message Date
mika kuns e55c84cda4 fix(ui): stop ClearCompletedAsync from mutating chain-display fields
ClassifyItems() layers ApplyChainGrouping on top of partitioning, which
writes ShowAsChainMember/ChainStep/ChainAfterLabel on every row.
ClearCompletedAsync only needed the completed-section count/rows, so it
got that mutation as an unwanted side effect of counting.

Split the pure overdue/open/completed partition into PartitionItems();
ClassifyItems() now composes it with the chain-grouping mutation for
Regroup's actual render path. ClearCompletedAsync uses PartitionItems
directly. Regroup's behavior is unchanged.
2026-08-11 18:32:59 +02:00
mika kuns d327a1007e fix(ui): reconcile the log visualizer tick instead of clearing Rows
The 4s reconcile tick called Apply(), which did Rows.Clear() + refill on
every tick -- resetting the user's scroll position and selection while
they're mid-read, exactly what the overlay exists to avoid.

Reconcile granularly instead: find the overlap between the previous and
latest snapshot (the ring buffer is append-mostly, evicting from the
front as entries age out of the 30-min window), trim rows for evicted
entries off the tail, and insert new entries at the head. A tick with no
change now leaves Rows untouched. The WarnErrorOnly toggle still goes
through the full Apply() rebuild, unchanged.
2026-08-11 18:32:39 +02:00
mika kuns 1861832b99 fix(ui): move the tasks list inset off the ListBox onto the panel
Padding on RowsListBox mirrored the ScrollViewer.Padding-excluded-from-
Extent trap fixed elsewhere today, so move the 10,4 inset onto the
VirtualizingStackPanel's Margin instead, matching the other scrollables.

Headless measurement showed Avalonia's Fluent ListBox template actually
forwards Padding onto PART_ItemsPresenter's margin already (content
side), so this particular control was never affected -- both variants
produce an identical Extent and a fully visible last row at max scroll.
Kept the change for consistency and corrected the CLAUDE.md gotcha to
reflect the verified behavior instead of assuming the same trap applied.
2026-08-11 18:32:30 +02:00
mika kuns 79b35801ae fix(ui): stop the reconcile ticks from destroying user state
The 4s reconcile tick was added to three long-lived surfaces. On two of
them it reloads by rebuilding every row instance, which silently threw
away whatever the user had built up since the overlay opened; on the
third it healed a row's data but left it in the wrong section.

- WorktreesOverview: LoadAsync clears Rows, SelectedCount, ConflictRows
  and BatchProgress, so batch-merge ticks, the highlighted row and a
  finished batch's outcome badges were wiped every four seconds --
  assembling a multi-worktree selection was impossible. Carry that state
  across the reload, keyed by task id, and re-point SelectedRow at the
  fresh instance (or clear it when the worktree is gone).
- MergeHelperSelection: the remember/restore had no re-entrancy guard, so
  a second tick entering between the first one's reload and its restore
  snapshotted rows already back at IsTickedByDefault and wrote that
  default back, re-ticking what the user had unticked. One tick at a time,
  and hold the row instances instead of a value snapshot so a tick landed
  during the await survives.
- TasksIsland: the tick deliberately never called Regroup because Phase 2b
  owned Rows in parallel. 2b has landed, so a healed task that went Done
  stayed in the Open section under a stale count, and a healed depends-on
  link never pulled its dependent under the chain head. Regroup when a
  patch moved a grouping input, gated on a cheap key so an idle tick stays
  free.
2026-08-11 16:39:20 +02:00
mika kuns eb66ae7b8b fix(ui): keep task rows without a chain relation flat
The indent column was hardcoded to 24px, so every row reserved the
gutter even with no child/chain relation. Restore Auto sizing with the
width back on the Border, which collapses to 0 while it is invisible.
Move the step badge into column 1 with a negative margin so its desired
width can never widen the Auto column (same on-screen position).
2026-08-11 15:27:35 +02:00
mika kuns 3c7871afe2 Merge claudedo/e908c51dc7c547f99ed99c491f286eeb 2026-08-11 15:23:12 +02:00
mika kuns b81d6e0b4f feat(ui): render dependency-chain rail, step badge, and after-chip in TaskRowView
Reuses the existing 24px indent track (now gated on ShowAsChild OR
ShowAsChainMember) instead of a second mechanism, adds a round step-number
badge centered on the rail line, and a dimmed "after X" chip for chain
members whose head isn't in view or that render as a planning child.
2026-08-11 15:14:39 +02:00
mika kuns 9977fe4c3e Merge claudedo/573224c1c8a74f62860c32331c234d1e 2026-08-11 14:49:08 +02:00
mika kuns 42279e0806 Merge claudedo/7ecf4a3b2149418390c8c69d8a52e5d1 2026-08-11 14:43:44 +02:00
mika kuns 18babe3dee feat(ui): compute dependency-chain step/grouping in TasksIslandViewModel
Slice 1 of dependency-chain display: TaskRowViewModel gains DependsOnTaskId
plus the extrinsic ShowAsChainMember/ChainStep/ChainAfterLabel contract for
Slice 2's rail/badge rendering. Regroup's ClassifyItems walks each row's
DependsOnTaskId chain per section, pulls dependents directly under their
head regardless of SortOrder, and falls back to a flat row + label when the
head isn't in the same section (mirrors the ParentInView precedent).
Planning children never join a chain group - parent indent wins - and only
ever carry the label.

No AXAML changes; that's Slice 2.
2026-08-11 14:38:16 +02:00
mika kuns e8e2fcec3f docs: update for task-numbers features (Slice 5)
Document task-number allocation in Data layer (TaskEntity.Number,
next_task_number counter, invariants, insert paths).

Update Worker docs to clarify TaskIdResolver wiring (#123 → GUID lookup),
Number in MCP payloads, and correct the 'Two hard conventions' statement
(only the first is test-enforced).

Bump external-mcp.md verified-against commit to 38af549 (Slice 4 merge)
and add new sections on task ID resolution and numbering.

Add Slice 4 visual verification items to open.md (row/detail number display,
worker-log messages).

Verified against:
- src/ClaudeDo.Data/TaskNumberAllocator.cs
- src/ClaudeDo.Data/Repositories/TaskRepository.cs (AddAsync line 20, CreateChildAsync line 276)
- src/ClaudeDo.Worker/External/TaskIdResolver.cs
- src/ClaudeDo.Worker/External/ExternalMcpService.cs (TaskDto/TaskRefDto DTOs)
- tests/ClaudeDo.Worker.Tests/External/ExternalMcpToolSchemaTests.cs (only first convention test-enforced)
2026-08-11 14:26:22 +02:00
mika kuns 0e12be480b Merge claudedo/b8004a65aba245bfbce192d10d90bb36 2026-08-11 13:54:17 +02:00
mika kuns 38af549a80 feat(ui,worker): surface task numbers in row/detail UI and worker log
Slice 4/5 of task-numbers: TaskRowViewModel.Number renders as a dimmed
"#123" before the row title; DetailsIslandViewModel.TaskIdBadge now
shows "#123" instead of the unusable "#T<guid-prefix>" handle; and the
curated WorkerLog business events in TaskRunner, TaskMergeService, and
TaskResetService prefix their quoted title with "#<Number>".
2026-08-11 13:51:20 +02:00
mika kuns a1aeb21481 Merge claudedo/3fe19f438f4446818304a3ae190b8763 2026-08-11 13:14:41 +02:00
mika kuns 2a3133efad feat(worker): accept #123 task numbers as MCP tool input
TaskIdResolver resolves a #123/bare-123 taskId parameter to its GUID
before any lookup, across every External/ MCP tool that takes a task
id, including the batch tools' id arrays (via delegation to the
already-resolving single-entity methods) and update_task's
dependsOnTaskId (empty string still passes through unchanged as the
clear-link sentinel). An unknown number throws a clear error instead
of a silent null. McpToolDocs.TaskNumberHint tells the agent to refer
to tasks as #<number> when reporting to the user, added to the
description of get_task, list_tasks, add_task, update_task_status and
review_task.
2026-08-11 13:10:49 +02:00
mika kuns 9660a29da4 Merge claudedo/42d038e771e249e790f38087cbf2be6f 2026-08-11 12:04:42 +02:00
mika kuns 106c964410 feat(worker): surface task numbers in MCP tool return payloads
Adds Number alongside every task id in External/'s DTOs -- the two
central mappers (ToDto/ToRefDto -> TaskDto/TaskRefDto) plus every
DTO that carries a bare task id and bypasses them (batch results,
queue state, wait-for-change, config, attachments, handoff,
lifecycle, merge-preview-set, worktree list). Input resolution
(#123 as an argument) stays for slice 3.
2026-08-11 11:59:51 +02:00
mika kuns 8da1a12389 fix(test): give Ui.Tests task seeds a unique Number
The task-numbers slice added a UNIQUE index on tasks.number. Ui.Tests seed
TaskEntity rows directly instead of going through TaskRepository, so every row
kept the default Number = 0 and 31 tests failed with
'UNIQUE constraint failed: tasks.number'.
2026-08-11 11:32:01 +02:00
mika kuns 5ab5a6c0a8 Merge claudedo/11da6ebbedbc4737b34878d77722106c 2026-08-11 10:52:33 +02:00
mika kuns 9e46c96b24 feat(data): add task numbers schema, allocator, and backfill migration
TaskEntity.Number is a global, monotonically increasing, never-reused
integer (displayed as #123), allocated from AppSettingsEntity.NextTaskNumber
via a single UPDATE...RETURNING statement rather than MAX(number)+1, which
would reissue a deleted task's number. Both insert paths (TaskRepository.
AddAsync and CreateChildAsync) route through the new TaskNumberAllocator,
with a bounded retry on a unique-index collision. One migration adds the
columns, backfills existing rows in creation order, and creates the unique
index afterwards. Data-layer only; MCP/UI wiring is later slices.
2026-08-11 10:49:06 +02:00
mika kuns 31a9e87b56 docs(specs): add task-numbers and dependency-chain display designs 2026-08-11 09:56:26 +02:00
mika kuns d885b4eb41 Merge claudedo/5e2a441113a34c2a9257b56abb0a605c 2026-08-11 09:41:19 +02:00
mika kuns c8244c67f3 feat(ui): wire list-handler nextPhase through the handoff hub event
HandoffRequested now carries nextPhase end to end (IWorkerClient ->
WorkerClient -> MissionControlViewModel -> GetMergeHelperHandoffLaunchSpecAsync),
and the outgoing tile is left open on handoff instead of being closed --
lookups that need the active pane for a task now use LastOrDefault since a
handler task's ConPtySessions can hold more than one pane.
2026-08-11 09:40:14 +02:00
mika kuns ee7003d964 Merge claudedo/a4834991067a479fabfb3226abcd4b9f 2026-08-11 09:28:22 +02:00
mika kuns f2609d186a feat(worker): wire list-handler phase parameter through handoff chain
Threads a nextPhase parameter (wait/merge/wait_final/merge_final, validated
by the new MergeHelperPhase) from handoff_list_handler through
HubBroadcaster/WorkerHub into InteractiveLaunchSpecService, which now picks
the next session's system prompt (MergeHelperWait/MergeHelperMerge) and
model (HandlerWaitAlias/HandlerMergeAlias) from it instead of hardcoding the
old two-phase Execute prompt -- this also fixes a build break left by the
prior prompt-split task, which removed PromptKind.MergeHelperExecute without
updating its only caller.

Also sets --model/--effort/--permission-mode explicitly for every list-handler
session (Triage included) via PermissionModeResolver instead of inheriting the
CLI's ambient model and hardcoding "auto", and adds Task to the merge-helper
allowlist so the Merge phase can delegate diff reviews to subagents.
2026-08-11 09:27:16 +02:00
mika kuns 6927805600 fix(ui): stop clipping the last log line in scrollable output
Avalonia 12 leaves ScrollViewer.Padding out of the scroll Extent, so at
maximum offset the content still overhangs the viewport by the padding
height — the last lines were cut off and unreachable even after
ScrollToEnd(). Move the inset onto the content (Margin on the inner
ItemsControl/StackPanel) and leave the ScrollViewer padding-free.

Affects the work console Output/Git/Session tabs, the reusable session
terminal (task + prep log) and the log visualizer.
2026-08-11 09:26:06 +02:00
mika kuns 1657a70962 Merge claudedo/46c16cb0e9934697b0865700baf0f244 2026-08-11 09:02:48 +02:00
mika kuns a311152b25 Merge claudedo/204a03ab377147be847c19bb2ce5b834 2026-08-11 08:54:13 +02:00
mika kuns 1203323102 Merge claudedo/033f104f2a0f44b1aac8ca54a73a8866 2026-08-11 08:54:05 +02:00
mika kuns e555bdb8a4 Merge claudedo/cda10d515837448c8a20d608dfe5e599 2026-08-11 08:53:57 +02:00
mika kuns d3b91b5214 feat(data): split list-handler prompt into triage/wait/merge roles
Adds HandlerTriageAlias/HandlerWaitAlias/HandlerMergeAlias to ModelRegistry and
replaces PromptKind.MergeHelperExecute with MergeHelperWait (phase 3 only) and
MergeHelperMerge (phases 4-5), so the list handler can run as three
cost-scoped sessions instead of two. Triage's Phase 2 now checks/sets a wide
verify command via get_list_config/set_list_config once per run. Merge now
merges before reruns, delegates diff review to a sonnet subagent, rejects
0-file diffs, tracks the merged-but-not-Done verify-gate outcome, and caps
reruns at one per task via handoff_list_handler's new nextPhase parameter.

InteractiveLaunchSpecService.cs still references the removed
PromptKind.MergeHelperExecute and fails to build -- wiring the Worker/UI side
onto the new roles is a follow-up task.
2026-08-11 08:52:28 +02:00
mika kuns 095d216bda feat(external-mcp): let set_list_config write the verify command
SetListConfig previously could only pass VerifyCommand through unchanged;
only the UI hub could set it. Add an optional verifyCommand parameter with
matching clear/merge/create-vs-delete handling, and split the list result
into ListConfigDto so the task-only SetTaskConfig/GetTaskConfig DTOs stay
untouched. Also corrects Worker/CLAUDE.md's claim that tasks can override
verify_command — it's list-only.
2026-08-11 08:52:03 +02:00
mika kuns d60ecb7f1b fix(ui): stop review-feedback field clipping placeholder and typed text
Shorten the review-prompt placeholder and localize it (session.reviewFeedbackPlaceholder,
en+de) instead of a hardcoded string that overran the narrow column. Switch the growing
TextBox's VerticalContentAlignment from Center to Top so multi-line input grows downward
instead of being clipped top/bottom, add an auto scrollbar past MaxHeight, and top-align
the prompt glyph and Resume button to match.
2026-08-11 08:50:45 +02:00
mika kuns 6a731beb53 fix(ui): add vertical spacing between task rows
Cards touched with Margin=0. Add margin on the inner task-row border
instead of the ListBoxItem style, so the drag/drop hit-test container
still covers the row and the drop-hint doesn't flicker between items.
2026-08-11 08:47:00 +02:00
mika kuns 11103d4d3e fix(runner): only fail a run on denied write tools with a worktree 2026-08-11 08:32:53 +02:00
mika kuns 1acd4c1734 fix(git): carry the pathspec into GetBranchDiffAsync's fallback 2026-08-11 08:32:53 +02:00
mika kuns d3ba279b54 fix(ui): stop the merge-helper tick from re-ticking unticked candidates 2026-08-11 08:32:52 +02:00
mika kuns 7106bf754f fix(mcp): keep wait_for_task_change waiting through a planning-chain block 2026-08-11 08:32:52 +02:00
mika kuns f31b1b4fb2 fix(mcp): stop set_list_config from clearing settings it doesn't expose 2026-08-11 08:32:52 +02:00
mika kuns d451edd367 fix(data): normalize a list working dir's trailing separator on write
A list working_dir stored as "C:\Dev\Tests\StaplerTracking\" broke every consumer
that puts it on a Windows command line: argv rules read \" as an escaped quote, so
the token never closes. "Open in terminal" passed wt.exe a starting directory of
C:\Dev\Tests\StaplerTracking" and it failed with 0x8007010b; the same data had
already corrupted the ConPTY list handler's arg list in August.

Paths.TrimTrailingSeparator is now the single helper (replacing the copies in
InteractiveLaunchSpecService and ClaudeHelpLauncher) and ListRepository applies it
on Add/Update, which covers every writer: UI create, repo import, hub UpdateList,
and MCP CreateList/UpdateList. OpenInTerminal also switches to ArgumentList so its
quoting is correct regardless of what's stored.
2026-08-10 17:12:46 +02:00
mika kuns 6d8cbacc9b fix(data): stop AddTaskDependency's table rebuild from dropping scope_globs
AddScopeOverlapFields, AddTaskDependency and AddFailureReason were each
generated off the same parent migration, so none of their Designer
snapshots knows the others' columns. AddTaskDependency calls AddForeignKey,
which on SQLite forces a full `tasks` table rebuild generated from that
migration's own frozen snapshot -- silently recreating the table without
`scope_globs`.

Result: the Worker died at startup with "no such column: t.scope_globs"
(Hosting failed to start), so it never bound the SignalR port and the App
could not connect. Fresh installs were affected too, not just existing DBs.

Add the missing ScopeGlobs property to the stale Designer snapshot so the
rebuild preserves the column. Verified by migrating a fresh temp DB and
diffing pragma_table_info('tasks') against the model snapshot: 39/39
columns.

Note: the test suite cannot catch this class of bug -- every fixture uses
Database.EnsureCreated(), which builds the schema from the model and never
applies the migration chain.
2026-08-10 17:00:24 +02:00
mika kuns bbfe40242c fix(ui): re-emit the flat list's section headers on a language change
Phase 2b-1 moved the group headers from {loc:Tr} bindings in TasksIslandView
into HeaderRow.Label, resolved once in Regroup(). RefreshLocalizedText only
updated CompletedHeader and the task rows, so after switching the language the
three section headers kept the old one. Re-run Regroup() there.

Not unit-tested: the only seam is the global static Loc.Current, and mutating
it would bleed into test classes running in parallel.
2026-08-10 16:26:16 +02:00
mika kuns 2fe40f3d65 Merge claudedo/923d3b07183c4ca8bcd15aed12f9df39 2026-08-10 16:21:01 +02:00
mika kuns ec6c230822 refactor(ui): flatten TasksIsland's task list into one virtualized ListBox
Replace the three ItemsControl sections (Overdue/Open/Completed) with a single
flat Rows collection (HeaderRow | TaskRowViewModel) bound to a ListBox +
VirtualizingStackPanel, so the realized container count stays bounded instead
of growing with the list (measured: ~7-8 realized containers at any scroll
position with 1000 source rows). Group headers become regular row entries,
each section is simply omitted from Rows when empty, and the Completed
section's Clear-completed action moves onto the header row itself.

SectionFor/ReorderAsync/FindNextInSameSection now read a row's section off
the nearest preceding HeaderRow in Rows instead of three separate bound
collections, and ScrollSelectedIntoView goes through ListBox.ScrollIntoView so
it still works for rows outside the realized window. Drag/reorder behavior,
grouping/counts, and the Clear-completed button are unchanged.

Auto-scroll-while-dragging and the cross-section reorder bug are explicitly
out of scope (follow-up tasks).
2026-08-10 16:18:55 +02:00
mika kuns 82881acc70 fix(ui): stop the reconcile tick on native close, index rows in the tick
Two follow-ups to the Phase 3 reconcile tick:

- LogVisualizer and WorktreesOverview only stopped their timer in the Close
  command, but ShowLogVisualizerAsync/ShowWorktreesOverviewAsync had no
  dlg.Closed fallback (unlike the MergeHelper picker). An Alt+F4 or owner
  close left a 4s timer polling the worker / re-running LoadAsync on an
  orphaned VM, accumulating one per open. Extracted StopReconcileTick on both
  VMs and wired it from Closed.
- The tick scanned Items linearly per id; at the 500-row cap that is ~250k
  string comparisons every few seconds. Index the rows once instead.
2026-08-10 16:05:37 +02:00
mika kuns 1b7b18fdfc Merge claudedo/45142ae6a226488192290835d6336c5a 2026-08-10 16:03:06 +02:00
mika kuns 1cad264967 feat(ui): add reconcile tick to self-heal lost worker broadcasts
TasksIslandViewModel now runs a periodic tick that diffs the flat Items
collection against SQLite and patches properties in place (capped at 500
rows, sharing the Phase 1 delta sequence guard so it never overtakes a
fresher broadcast). Never rebuilds rows or triggers Regroup/LoadForList, so
it stays decoupled from the parallel Phase 2b virtualization work. The same
cadence now refreshes the long-lived Worktrees Overview, Log Visualizer, and
Merge Helper selection overlays; short-lived modals are untouched.
2026-08-10 15:56:05 +02:00
mika kuns 514d6111fe fix(test): name the cancellationToken arg in the preview_merge_set subset test
Two branches merged into main independently: one added `runVerify` to
PreviewMergeSet ahead of `cancellationToken`, the other added the subset
test that passed the token positionally. Neither conflicted textually, so
the mismatch only surfaced as CS1503 at test-compile time.
2026-08-10 15:26:07 +02:00
mika kuns 66f7a8ae99 Merge branch 'claudedo/9f963a215bb34ca4a28050d4f39178d3' 2026-08-10 15:19:57 +02:00
mika kuns fb12d965fc Merge subtask
# Conflicts:
#	src/ClaudeDo.Data/Git/GitService.cs
#	src/ClaudeDo.Worker/Lifecycle/TaskMergeService.cs
2026-08-10 15:18:30 +02:00