Commit Graph
583 Commits
Author SHA1 Message Date
mika kuns bc959f25b3 feat(worker): approve in a company repo verifies in the worktree, pushes and opens a draft PR 2026-10-01 12:05:33 +02:00
mika kuns 7f7f237656 feat(worker): company unit roots start from a fetched origin base, local base fast-forwarded best-effort 2026-10-01 11:59:11 +02:00
mika kuns 78f4d182e8 feat(worker): Azure DevOps client and PAT store, ported from pr-create.ps1 2026-10-01 11:55:11 +02:00
mika kuns 76f260a9a5 feat(git): fetch/push/ff primitives with auth env, no task trailer in company repos 2026-10-01 11:51:28 +02:00
mika kuns acd188bca9 fix(worker): block approve of a planning parent with an unfinished child, surface git stderr on ff failure 2026-10-01 11:36:45 +02:00
mika kuns 9da522b0c9 feat(worker): fast-forward finished children into the parent's unit branch
Planning parents get their own worktree on Queue plan, children fork from the
unit head and are absorbed via merge --ff-only before Done (ff failure -> Failed,
interactive child approve -> absorb instead of merge to the target).
2026-10-01 11:25:47 +02:00
mika kuns cd5b27c063 fix(planning): return to the list base branch after integration cleanup, not a guessed task branch 2026-10-01 10:55:12 +02:00
mika kuns 69c7803bdf feat(worker): use the list base branch as worktree base and default merge target 2026-10-01 10:46:21 +02:00
mika kuns 3fe1feadf9 feat(data): per-list base branch, preserved by every config writer 2026-10-01 10:39:49 +02:00
mika kuns 8911408bca refactor(worker): detect untracked worktrees by path instead of branch prefix 2026-10-01 10:37:37 +02:00
mika kuns 814b083a4e feat(worker): name task branches <type>/<number>-<slug> 2026-10-01 10:35:52 +02:00
mika kuns 4a9cff5543 feat(worker): branch type and number rules for task branches 2026-10-01 10:30:51 +02:00
mika kuns faa91e0661 feat(worker): detect company repos by origin remote pattern 2026-10-01 10:28:54 +02:00
mika kuns 2b40ea2d2b fix(mcp): register ticket import tools in the external MCP container
The external MCP server has its own DI container; TicketMcpTools could not
resolve TicketImportService there, so both tools failed at runtime. Also
surface a skipped import in the prime outcome and treat a sweep where every
project board fails as an error instead of an empty backlog.
2026-10-01 08:49:31 +02:00
mika kuns 9fc2e26edb feat(hub): add RunTicketImportNow and ticket fallback list endpoints 2026-10-01 08:41:12 +02:00
mika kuns 915f2c8352 feat(prime): ImportTickets runs the ticket import before filling MyDay 2026-10-01 08:39:38 +02:00
mika kuns 57c30c4a18 feat(mcp): add get_ticket_import_candidates and import_ticket tools 2026-10-01 08:37:44 +02:00
mika kuns 47583ab1fe feat(tickets): sweep all project boards for assigned tickets and tasks 2026-10-01 08:36:11 +02:00
Mika Kuns 44ec89b0e6 chore(claude-do): Worktrees sollten nach dem Task benannt werden
Durch die GUID namen kann ein User die Worktrees keinem Task zuordnen, und in Rider Müllen mir die Worktrees die BRanches voll. Also sollten wir einfach den Task titel oder Nummer stattdessen nehmen

ClaudeDo-Task: 63f08d944d3341c081267158ef4bbb3e
2026-09-30 10:49:07 +02:00
mika kuns 8a31a67c74 feat(report): write weekly report as short bullets with haiku 2026-09-30 10:05:24 +02:00
mika kuns 27d2b1d8ef fix(tickets): convert ticket HTML to markdown on import 2026-09-30 08:39:17 +02:00
mika kuns 52ba538339 test(worker): cover WorktreeUpdated broadcast on interactive review submission 2026-09-30 08:14:26 +02:00
mika kuns 352aaa525b fix(worker): DeleteTask aus der UI schlug immer mit generischem Serverfehler fehl
Der CancellationToken-Parameter auf der nicht-streamenden Hub-Methode liess SignalR jede Invocation vor dem Methodenrumpf abbrechen. Die Tests riefen den Hub direkt auf und sahen das nicht; neuer Reflection-Guard verbietet CancellationToken auf allen nicht-streamenden Hub-Methoden.
2026-09-28 13:02:47 +02:00
Mika Kuns d3db4455fa feat(ui): Mission Control Recent Sessions mit verschachtelten Handoffs
Neue Tabelle mission_control_sessions (Migration AddMissionControlSessions), befuellt vom
InteractiveLaunchSpecService bei Task-, Ad-hoc- und Handoff-Kacheln. Flyout im Mission-Control-Header
listet die letzten 14 Tage / 50 Sessions, Handoffs eingerueckt unter ihrer Ursprungs-Session, mit
Fortsetzen (--resume im gespeicherten cwd) und Brief anzeigen.
2026-09-28 12:11:21 +02:00
Mika Kuns 08d1797ead chore(claude-do): Mission Control: allgemeiner Session-Handoff an eine frische
## Ziel

Jede Mission-Control-Session (Ad-hoc UND Task-Session) soll ihre Arbeit an eine frische Session
uebergeben koennen: "gib das ab an eine neue Session". Die alte Kachel wird geschlossen, an
derselben Position oeffnet eine frische Session im selben cwd, die mit einem Brief startet, den
die abgebende Session selbst geschrieben hat.

Motivation: eine lange Session mit vollem/verrottetem Kontex

ClaudeDo-Task: 8b846deb-ad10-4243-9272-9aea2a01ec93
2026-09-28 11:57:37 +02:00
Mika Kuns 09bcd696dd feat(ui): dirty Ziel-Verzeichnis beim Oeffnen des Merge-Dialogs melden statt nach dem Fehlschlag
MergeTargetsDto/MergeTargets bekommen TargetDirty (via HasChangesAsync,
includeUntracked: false). Das Merge-Modal zeigt Warnung + Stash-Retry
sofort in InitializeAsync statt erst nach dem dirty_target-Fehlschlag;
der REVIEW-Block in der Detail-Pane zeigt denselben Hinweis neben
Approve. Der dirty_target-Rueckgabepfad bleibt als Absicherung bestehen.

Behebt Befund F6 aus docs/test-session-2026-09-16.md.
2026-09-16 15:43:39 +02:00
Mika KunsandClaude Sonnet 5 684b0115e5 feat(worker): preview_merge_set flags removed symbols and locale-key overlaps
Overlap detection was file-name-only: 9/15 tasks touching en.json/de.json in
run #316 all got flagged even though only one pair actually collided on a
key, and a removed method with no textual file conflict (#297 vs #286) went
unflagged entirely.

- removedSymbols per task entry: a line-pattern scan of `-` diff lines for
  C# method/property/field/type/event declarations (skips .axaml/.json).
- symbolWarnings at the set level: cross-references those names against
  every other task's `+` lines, catching same-symbol collisions across
  files that overlaps/subsets (both purely file-based) can't see.
- overlaps narrows to key-path level for .json files (base commit vs. task
  branch head, both sides parsed and flattened) so two tasks adding
  different locale keys to the same file no longer collide; unparsable
  JSON on either side falls back to the old file-level check.
- GitService.ShowFileAsync: `git show ref:path`, used to read a task
  branch's json content at its base/head commit.

Out of scope per the task: no Roslyn/parser, no AXAML analysis, and the
merge-helper prompt in PromptFiles.cs doesn't mention the new fields yet.

Build: dotnet build src/ClaudeDo.App (pulls in Ui+Data) + src/ClaudeDo.Worker, both clean.
Tests: dotnet test tests/ClaudeDo.Worker.Tests --filter "FullyQualifiedName~MergeMcpTools|FullyQualifiedName~ExternalMcpToolSchema" -- 51 passed, 0 failed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-16 15:43:38 +02:00
Mika KunsandClaude Sonnet 5 7452f6cfe4 fix(worker): preview_merge braucht einen runVerify-Parameter wie preview_merge_set
preview_merge baute bisher immer, sobald die Liste ein Verify-Kommando
konfiguriert hatte -- anders als das Geschwister-Tool preview_merge_set,
dessen runVerify-Parameter per Default false ist. Ein einzelnes preview_merge
im Merge-Helper-Recheck stiess dadurch einen ungewollten Scratch-Build an.

runVerify (Default false) jetzt symmetrisch bei preview_merge ergaenzt,
Description-Text und die Merge-Helper-Prompt-Doku angepasst.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-16 15:43:37 +02:00
Mika KunsandClaude Opus 5 c874bf7e52 fix(worker): depends_on-Nachfolger schon beim gelandeten Merge freigeben
Der Queue-Picker gatete eine task_dependencies-Kante auf status='done'.
review_task(skipVerify) laesst einen erfolgreich gemergten Task aber
bewusst auf WaitingForReview stehen, bis verify_merges einmal zentral
laeuft -- damit blockierte jeder Nachfolger ueber den gesamten Batch plus
Full-Suite-Verify hinweg, bei freien Slots.

Eine Kante gilt jetzt als erfuellt, wenn der Vorgaenger 'done' ist ODER
sein Merge gelandet ist (worktrees.state = 'merged'). TaskBlocking
(get_task/list_tasks/wait_for_task_change) und der UI-Wartehinweis folgen
derselben Regel, damit "blocked" nicht meldet, was der Picker claimt.

Abgeleitet: revert_merge/abort_merge setzen den Worktree auf Active und
blockieren wieder; reject_rerun/reject_park haben nie gemergt und bleiben
unerfuellt; ein Task ohne Worktree wird nur ueber 'done' erfuellt; ein
fehlgeschlagener verify_merges gibt Nachfolger frei, weil der Code so
oder so auf main liegt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 15:43:37 +02:00
mika kuns 251b70ce83 feat(merge): Stash-Option fuer uncommittete Aenderungen im Ziel-Tree
Changelog / changelog (push) Successful in 2s
Release / release (push) Successful in 43s
Ein dirty Ziel-Arbeitsverzeichnis gibt jetzt den eigenen Status
dirty_target zurueck statt blocked. Die UI bietet daraufhin einen
Retry an, der per git merge --autostash stasht, merged und die
Aenderungen danach wiederherstellt - opt-in pro Merge:

- Approve & Merge: Bestaetigungsdialog
- Merge-Modal: Button "Aenderungen stashen und mergen"
- MCP (merge_task/review_task) wirft weiterhin, kein autostash dort
2026-09-11 11:43:28 +02:00
mika kuns 1770b67713 fix(tickets): Import broadcastet TaskUpdated fuer neue Tasks
ImportTickets sendete nur ListUpdated. Die UI liest das als Aenderung der
Listen-Metadaten und patcht nur bestehende Zeilen, also tauchten die
importierten Tasks erst nach Schliessen und Neuoeffnen der Liste auf.
TicketImportResult liefert jetzt die angelegten Task-Ids, der Hub
broadcastet pro Id ein TaskUpdated - derselbe Pfad, den jeder andere
out-of-band Creator nutzt.
2026-09-10 15:39:58 +02:00
mika kuns 73e038edd9 fix(claude-do): merge GitService stderr matches are English-only, same locale bug
ClaudeDo-Task: 66b7ad7b-ffc4-4ebd-8b21-54bba28e5b04
2026-09-10 13:56:14 +02:00
mika kuns 5ecf6f750a fix(data): force English git output for locale-fragile stderr matches
RevParseHeadAsync and CommitAsync's identity-error detection match
English-only substrings against git stderr. Under a non-English OS/git
locale these silently fail to match, falling through to a generic
error. Force LC_ALL=C and LANGUAGE=C on the git ProcessStartInfo so
git always emits English output regardless of ambient locale.
2026-09-10 13:54:14 +02:00
mika kuns 29ea18c427 fix(ui): make diff-stat parsing locale-safe and dedupe the two parsers
TaskRowViewModel had its own ParseDiffStat expecting "+12"/"-3" tokens that
`git diff --stat` never emits, so the row badge always showed 0/0. Meanwhile
DetailsIslandViewModel's parser matched only the English words
"insertion"/"deletion", so it broke on a localized git (e.g. German Windows:
"Einfügung(en)"/"Löschung(en)").

Row and detail badge now share one parser, anchored on git's untranslated
(+)/(-) suffix instead of the language-dependent word before it, so it reads
correctly in any locale. Also corrected the Worker CLAUDE.md's branch-name
claim (`claudedo/{taskId[:8]}`) to match the actual full-GUID branch name.
2026-09-10 13:50:35 +02:00
mika kuns d2648f965c feat(worker): chain a normal parent's children on WaitingForChildren
A standalone parent (PlanningPhase.None) with existing Idle/Queued children
now runs itself first, then sequences its children instead of flat-enqueuing
all of them at once: SubmitForChildrenAsync calls
PlanningChainCoordinator.SetupChainAsync(enqueue: true) right after the
Running -> WaitingForChildren transition, mirroring the planning path but
firing immediately instead of waiting for an explicit "Queue plan". Planning
parents are left untouched (they queue via QueuePlanAsync), and a child
already Running makes SetupChainAsync throw, which is now caught and logged
so it can't fail the parent's own transition.
2026-09-10 13:36:08 +02:00
mika kuns 1a5728749b fix(claude-do): merge fix(worker): max_turns-Fehlertext nicht fest deutsch — konsi
ClaudeDo-Task: 6549e3b5-cee3-4aa4-ba3a-6240bab952e6
2026-09-10 13:35:45 +02:00
mika kuns 423d39e9fb fix(claude-do): merge fix(worker): WeekReportService durch PermissionModeResolver
ClaudeDo-Task: b4198327-6045-451c-abf1-dbabad88e91a
2026-09-10 13:34:20 +02:00
mika kuns 451c01848c fix(worker): start_list_handler ehrlich machen, wenn keine App verbunden ist
ConnectedClientTracker zaehlt verbundene UI-Clients (WorkerHub.OnConnectedAsync/
OnDisconnectedAsync). StartListHandler prueft ihn vor dem Broadcast und liefert
bei 0 verbundenen Clients Requested=false plus Reason statt unbedingtem true.
Tool-Beschreibung und external-mcp.md an das reale Verhalten angepasst.
2026-09-10 13:33:30 +02:00
mika kuns 0235b43bad fix(claude-do): merge fix(worker): Port-Konflikt beim Start laut melden statt stil
ClaudeDo-Task: 1eb4cfcd-a31e-4216-8c05-88cd662a211b
2026-09-10 13:33:28 +02:00
mika kuns 4cf601e423 feat(ui): surface depends_on blocking in status chip, chain label, and detail pane
Usability-Audit 2026-09-10 found three gaps versus the dependency-chain-display
spec: a Queued task blocked on an unmet task_dependencies edge rendered the same
chip as one about to run, a cross-list predecessor silently dropped the
"after #N" chip, and the detail pane showed nothing about a wait at all.

- TaskRowViewModel gains IsBlockedByDependency/DependencyWaitLabel (extrinsic,
  assigned by TasksIslandViewModel like the existing chain fields); IsQueued/
  IsWaiting/StatusChipClass/StatusLabel/StatusChipTooltip now account for it
  alongside the existing BlockedByTaskId planning-chain link.
- TasksIslandViewModel.ApplyDependencyWaitState computes the blocked flag and
  wait label per row; a new RefreshExternalDependencyCacheAsync backfills
  Number/Title/Status for predecessors not loaded into the current list's
  Items (reusing the already-open DbContext, no new hub method), so
  BuildAfterLabel/blocking both resolve a listenfremder Vorgänger correctly.
- New "waiting" chip style (IslandStyles.axaml) and locale keys (en+de);
  TaskHeaderBar shows a "Waiting on #N" line bound straight to the same row.

Tests: TasksIslandChainTests (blocked/unblocked chip, cross-list predecessor),
DetailsIslandDependencyWaitTests (new), TaskRowViewModelNotificationsTests
updated for the new notification set.
2026-09-10 13:32:36 +02:00
mika kuns 0fcfe7c524 fix(worker): translate max_turns failure text to English
TaskRunner.BuildFailureMarkdown returned a hardcoded German string for
TerminalReason == "max_turns", the only worker-generated string not in
English. Surfaced as the outcome/roadblock card for one of the most likely
first failures, looking broken to non-German users.
2026-09-10 13:22:30 +02:00
mika kuns 1c76852957 fix(worker): route WeekReportService permission-mode through PermissionModeResolver
Closes the last unrouted --permission-mode call site: haiku default models were
hardcoding raw "auto", which the CLI silently downgrades to interactive "default"
and hangs the report generation. Also drops the author's personal C:\Private
default exclude in favor of an empty default (config stays via ReportExcludedPaths).
2026-09-10 13:13:16 +02:00
mika kuns c9d4823f06 fix(worker): report startup port conflicts instead of crashing silently
Mutex was Local\ (per Windows session), so a second RDP/fast-user-switching
session bypassed the single-instance guard. A Kestrel bind failure on top of
that had no try/catch, so a second worker (or any process on 47821/47822)
died as an unhandled crash with nothing in the log to explain why the UI
never connects. Mutex is now Global\, bind failures and any last-resort
unhandled exception log a Fatal line and exit(1) instead of crashing raw.
2026-09-10 13:12:25 +02:00
mika kuns 1177463a82 feat(ui): triage model picker (opus/fable) in the merge helper start modal 2026-09-10 09:42:55 +02:00
mika kuns 7c7c33d677 feat(ui): PowerUse-Einstellungen nach Settings -> Ausfuehrung verschieben
Schalter, Zeitfenster und Handoff-Prozent lagen im Usage-Monitor und mussten
in SettingsModalViewModel nur load->save durchgeschleift werden, damit ein
Settings-Speichern sie nicht zurueckwirft. Jetzt gehoeren sie dem General-Tab,
der Monitor ist wieder nur Anzeige + Threshold-Drag, und "Jetzt aktualisieren"
sitzt als Icon in der Titelleiste (neuer ModalShell.HeaderActions-Slot).
2026-09-07 08:17:37 +02:00
mika kuns f70503d038 fix(worker): interaktive Session ohne Arbeitsverzeichnis im User-Verzeichnis starten
Eine manuelle Liste (Erinnerungen) hat kein WorkingDir. BuildForTaskAsync
warf dafuer bisher "no working directory configured", die interaktive
Session liess sich also nicht oeffnen. Jetzt faellt der Pfad auf das
User-Verzeichnis zurueck; Session-Skills werden dort bewusst nicht
geseedet, weil das Ziel das echte ~/.claude/skills waere.
2026-09-03 10:17:06 +02:00
mika kuns bd5aa265d1 feat(mission-control): Handler-Run per Knopf abbrechen
Ein "Abandon run"-Knopf an Handler-Panes fragt HandlerRoundState.Get(handlerTaskId)
nach den noch offenen (Queued/Running) Tasks des Laufs, bestaetigt mit Anzahl,
cancelt jeden ueber den bestehenden CancelTaskAsync-Pfad und schliesst danach die
Session -- ein Schritt statt Tile schliessen + Queue separat abbrechen. Ohne
offene Tasks (oder ohne Round-Eintrag) schliesst die Aktion direkt, ohne Dialog.
Nur an den beiden Handler-Panes sichtbar (IsListHandler), normale Task-/Ad-hoc-
Panes bleiben unveraendert.
2026-09-02 22:39:12 +02:00
mika kuns df3e7ba0c7 fix(ui): Dequeue-X auf blockierten Queue-Zeilen anzeigen und Kette konsistent halten
CanRemoveFromQueue schloss IsWaiting (queued+blocked) faelschlich aus, sodass ein
Kettenglied in der Mitte nicht entfernbar war. Zusaetzlich unblockt
TaskStateService.DequeueToIdleAsync jetzt den Nachfolger eines dequeueten
Kettenglieds, sonst bliebe er dauerhaft auf einen nicht mehr laufenden
Vorgaenger blockiert.
2026-09-02 22:34:50 +02:00
mika kuns d258ab4164 test(worker): start_list_handler ins erwartete MCP-Tool-Inventar aufnehmen
Changelog / changelog (push) Successful in 2s
Release / release (push) Successful in 42s
2026-09-02 21:57:16 +02:00
mika kuns fe8186514f feat(worker): Handler-Session vor dem Usage-Limit warnen und Handoff bis zum Reset zurueckhalten
Eine PowerUse-Handler-Session soll am 5h-Limit nicht mehr mitten im Merge
sterben, sondern vorher selbst sauber uebergeben:

- HandlerUsageGuard (neu) warnt die laufende Session genau einmal pro Runde,
  sobald FiveHour.Utilization den Schwellwert PowerUseHandoffPct erreicht.
  Die Warnung wird per HandlerUsageWarning-Broadcast an Mission Control
  geschickt und dort in das Pane der Handler-TaskId getippt.
- handoff_list_handler broadcastet den Handoff dann nicht sofort, sondern parkt
  ihn bis ResetsAt + 60 s; das Ergebnis traegt deferredUntil. Ohne ResetsAt
  wird wie bisher sofort gesendet.
- Der geparkte Handoff liegt im HandlerRoundState (handler-rounds.json) und
  wird vom Guard-Tick ausgeloest, also auch nach einem Worker-Neustart wieder
  eingeplant bzw. sofort gesendet, wenn er ueberfaellig ist.
- Review-Befund aus #272: HandlerRestartCoordinator wartet nicht mehr selbst
  bis ResetsAt, sondern parkt den Neustart im selben Slot. AutoRestartUsed wird
  erst gesetzt, wenn der Broadcast wirklich rausging - ein Worker-Absturz
  waehrend der Wartezeit laesst die Runde nicht mehr dauerhaft tot zurueck.
  Zurueckgehaltener Handoff und Auto-Neustart teilen sich einen Slot, also
  oeffnet nie beides dieselbe Runde. UsageLimitProofPct entfaellt zugunsten von
  PowerUseHandoffPct.
- PtyTerminalSession.SendLineAsync schreibt in die laufende Session. Gegen eine
  echte claude-Session verifiziert: Text und Enter muessen zwei getrennte
  Writes sein, die Enter-Bytes kommen von Terminal.GenerateKeyInput.
2026-09-02 21:35:29 +02:00