# Handoff — List-handler run on list "Claude do", 2026-08-05 > **✅ COMPLETED 2026-08-05 (follow-up session).** Everything in §1 and §2 is merged and `main` > verified green. Read §0 below before §1–§8 — §5's diagnosis turned out to be **wrong** and the > rest is now history. Still nothing pushed. Repo: `C:\Private\ClaudeDo` · List id: `5f973815-050a-4136-94f0-1506a5d4560a` · Branch: `main` (nothing pushed) --- ## 0. What the follow-up session did (and what §5 got wrong) **All merged, `main` green after every step** (Worker 811/811, Data 143/143, Ui 292/292, Localization 16/16, builds 0 new warnings): | Merged | Task | Merge commit | |---|---|---| | §1 #1 | `9e307199` revert_merge + merge-SHA persistence | `3e7126b` | | §1 #2 | `0b2fbb48` post-merge verification gate | (conflict-resolved) | | §1 #3 | `8c1c2130` roadblock reply box | `519ea5a` | | §2 #42 | `c1df5b9a` Hub surface for usage | `6e2158d` | | §2 #43 | `f74b44d9` Usage pill | `7661129` | | §2 #45 | `06068810` gate thresholds in settings | `5115cfc` | | §2 #44 | `82488d2a` Usage Monitor modal | `338fc39` | | §2 #46 | `9c8cffe0` docs | `5872666` | | parent | `439a4daf` Usage Monitor unit | approved → Done (empty unit merge; all children already `Merged`) | Plus one hand-fix on `main`: `677a4c1` — `UsagePillViewModelTests` never set `Loc.Current` (defaults to a key-echo localizer) and only passed because another test class happened to install a real `Localizer` first; #44's new tests changed the ordering and broke it on `main`. Classic "both branches green, `main` red". ### §5 is wrong — `"exited with code 1 and no result"` is NOT (only) a CLI crash It is a **catch-all** hiding at least three causes. The truth is in the run log's last NDJSON line (`{"type":"result", …}` → `terminal_reason` / `errors` / `result`): - **`max_turns`** — what actually killed #40 and #42. `app_settings.model_presets` is `NULL`, so `ModelPresets.Parse` falls back to the shipping defaults, and **sonnet's default is 30 turns**. `AppSettings.DefaultMaxTurns` (100) is only the fallback for an *unrecognized* alias, so it never applies. Every task without an explicit `maxTurns` override got 30 turns. → Fix used here: `set_task_config(taskId, model="sonnet", maxTurns=200)` before queueing. - **`api_error`** + `"You've hit your session limit · resets 1pm (Europe/Berlin)"` — the account's 5-hour limit, which took out #43's first attempt. Nothing to fix; wait for the reset, re-queue. - Genuine process death — the case §5 describes. §5's *operational* advice still holds and is what saved #42: **never `reset_failed_task`** on one of these; check the worktree, build/test it, then set the task `Queued` so the agent resumes its own session and commits. #42's worktree held a complete green implementation (773/773). Follow-up tasks: `ca6e55c0` (surface the real failure reason) is new; the turn-budget/presets side is already covered by the Idle task `2de2f008` (`b0317ec7` fixed only the unknown-alias half). ### Still open - **Visual passes** (nobody has looked at these in a running app): usage pill in the footer *and* the Mission Control header; Usage Monitor modal (gauges, tables, stale/blocked bands, dark/light); the roadblock reply box; the verify-command field in the List Settings modal. See `docs/open.md`. - **`~/.todo-app/prompts/planning.md` still shadows the compiled default** (§7.1) — unchanged. - **`wait_for_task_change` is merged but not in the running Worker**, so it isn't callable over MCP until the Worker is restarted. §3's sqlite poll was used instead. - One rough edge in the verify gate: if the verify command fails, the worktree has already been removed and its state set `Merged` while the task stays `WaitingForReview` — re-approving is then refused. Recovery is `update_task_status(..., "Done")` once `main` is fixed. Predecessor session ran the five-phase list handler over 11 briefed tasks and, along the way, absorbed the 9-child "Usage Monitor" unit. **Phases 0–3 are complete for the brief.** What is left is Phase 4 (review + merge) for three tasks, plus the Usage chain. --- ## 1. Do this first — three brief tasks sit in WaitingForReview Merge in **this order** (the order was chosen with the user and matters): | # | Task | Id | Note | |---|------|----|------| | 1 | Feat: Merge zurücknehmen — Merge-Commit festhalten + `revert_merge` | `9e307199-2eca-4eb7-9057-d2c675cc57ca` | Migration + new tool. Merge **before** #2 | | 2 | Feat: Verifikations-Gate nach dem Merge | `0b2fbb48-d44c-4155-8c21-d3464c0bd5c2` | Depends on #1's merge-SHA persistence; both edit `TaskMergeService.cs` | | 3 | Feat: Antwortfeld auf der Roadblock-Karte | `8c1c213004574c4fad6beb75b84b70d7` | UI + localization (en **and** de) | For each one: 1. `get_task_diff(taskId, stat=true)`, then the full diff if non-trivial. Sanity-check against the task description (they are long and precise — the acceptance criteria are the checklist). 2. `review_task(taskId, decision="approve", leaveConflictsInTree=true)`. 3. On conflict: open the files under the returned `repoPath`, resolve keeping **both** sides' intent, then `continue_merge(taskId)`. Conflicts are expected and normal here. 4. **After every merge, verify `main`** (see §4). This is non-negotiable — see §5. Expected conflicts: `TaskMergeService.cs` between #1 and #2; `src/ClaudeDo.Worker/CLAUDE.md` and `src/ClaudeDo.Data/CLAUDE.md` in nearly every merge (doc bullet lists — trivial, keep both sides' entries). ## 2. Then the Usage Monitor unit Parent `439a4daf166f4ab5b0fa415693d2c80d` ("Usage Monitor hinzufügen") is `WaitingForChildren` and has **no worktree of its own**. It has 9 children. Four are merged, one is in flight, four are Idle. | Child | Id | State | |---|---|---| | #38 Data: Usage-Gate-Schwellen + Modell-Spalte | `c1c999b6-b800-4b6b-a821-fbc028c15772` | merged `b1efcdc` | | #39 Worker: OAuth-Usage-Client + Poller | `f657e316-ad72-4f45-8036-460841fc8997` | merged `b126a21` | | #40 Worker: UsageGate | `06a7cc32-6ab7-4758-98f4-bee77149b2bf` | merged `1ee21b5` | | #41 Worker: TranscriptUsageReader | `840fdb98-1c0e-4219-8062-c8769233fc14` | merged `334cf1e` | | #42 Worker: Hub-Surface für Usage | `c1df5b9a-b911-4fe8-aab4-5876d9d85793` | **re-queued, in flight — read §5 before touching** | | #43 UI: Usage-Pill | `f74b44d9-7e48-4bfe-9d89-075e194d1fc9` | Idle — queue once #42 is merged | | #45 UI: Gate-Schwellen im Settings-Modal | `06068810-5b5c-4635-80dd-62eeba89fb8c` | Idle — queue once #42 is merged (parallel with #43) | | #44 UI: Usage-Monitor-Modal | `82488d2a-8ff7-41b8-b791-367959a8f827` | Idle — needs #42 **and** #43 merged | | #46 Docs: Usage Monitor | `9c8cffe0-8f7b-401e-a4f0-33b937047082` | Idle — last, after everything is merged | **The chain is strictly serial and you must respect it.** Every child forks from `main`, and each one's own description hard-requires the earlier ones. Queueing them all at once is exactly what produced the original roadblock: #40 ran, found its prerequisite types only on unmerged sibling branches, and returned `Done` having written **zero** code. So: merge a child → then queue the next → verify `main` → repeat. When the last child is merged the parent surfaces for review by itself; approve it to close the unit (it has no worktree, so it approves straight to Done). ## 3. Cheap status polling — important `list_tasks` and `batch_get_tasks` return full descriptions and **blow the token limit** on this list (`list_tasks` over 52 tasks = ~206,000 chars; that is literally one of the bugs this run fixed). Do not poll with them. Poll the DB read-only instead: ```bash PYTHONIOENCODING=utf-8 python - <<'EOF' import sqlite3 c=sqlite3.connect("file:C:/Users/mika.kuns/.todo-app/todo.db?mode=ro",uri=True) for i,s in c.execute("select id,status from tasks"): print(s, i) EOF ``` `list_worktrees` is also compact and safe. **New this run:** `wait_for_task_change(taskIds, timeoutSeconds)` is now merged and is the proper primitive — it returns as soon as any listed task leaves Queued/Running (server-clamped to 170 s). Prefer it over sleeping. ## 4. Verify main after every merge ```bash dotnet build src/ClaudeDo.Worker/ClaudeDo.Worker.csproj -c Release dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj -c Release dotnet test tests/ClaudeDo.Data.Tests/ClaudeDo.Data.Tests.csproj -c Release ``` For the UI/localization task (#3 above, and children #43–#45) also: ```bash dotnet build src/ClaudeDo.App/ClaudeDo.App.csproj -c Release dotnet test tests/ClaudeDo.Ui.Tests/ClaudeDo.Ui.Tests.csproj -c Release dotnet test tests/ClaudeDo.Localization.Tests/ClaudeDo.Localization.Tests.csproj -c Release ``` `.slnx` needs .NET 9 — build individual csproj files, `-c Release` (a running Worker locks Debug). Baseline as of this handoff: Worker **753/753**, Data **143/143**, build 0 warnings. ## 5. The trap that cost this run the most time Two children "failed" with `"Claude exited with code 1 and no result"`. **That is a CLI crash, not bad code.** In both cases the worktree held complete work that built with 0 warnings and passed the full suite (#40: 732/732, #42: 766/766) — the run just died before the auto-commit. - **Never `reset_failed_task` on such a task** — it discards the worktree and destroys the work. - Instead: `cd` into the worktree, `git status`, build + test it. If green, set the task `Queued`. The worktree is preserved and the agent resumes its own session (`--resume`), finds its work and commits it. That is how #40 was recovered. - #42 is mid-recovery right now via exactly this route. If it failed again, verify its worktree (`C:\Private\.claudedo-worktrees\claude-do\c1df5b9a-b911-4fe8-aab4-5876d9d85793`) before doing anything destructive. Second trap: git merges cleanly and the **compiler** still breaks. It happened again this run — `Usage/UsageModels.cs` was an add/add conflict, and `src/ClaudeDo.Worker/CLAUDE.md` merged "cleanly" into a file with the `Usage/` folder documented **twice**. Always read what a clean merge produced, and always run §4. ## 6. Phase 0–3 decisions already made (do not redo) Dedupe: four candidate pairs examined, **nothing cancelled**. Decisions: - `05827da5` ↔ `81e37801` — kept both, and `05827da5` was **re-scoped**: its "lean status query" half was removed because `81e37801`'s wait tool covers it. `05827da5` now owns only the brief-description rendering. Both are merged. - `a76d9547` ↔ `99732497` — kept both, `99732497` merged first. Done. - `0b2fbb48` ↔ `9e307199` — kept both, `9e307199` merges first. **This is item #1/#2 in §1.** - `20c78c95` ↔ `a76d9547`(b) — kept both, different actors. Done. Phase 2: all 11 tasks carry acceptance criteria, real file+line references and out-of-scope sections. Three that were one-liners were researched and rewritten after asking the user (ConPTY fix approach, maxTurns-only scope, roadblock reply-box design). Run config: `maxParallelExecutions` = **3**. No list config exists, so effective max turns was the global **100**; it was raised to **200** per-task on the five heaviest via `set_task_config`. `0b2fbb48`, `9e307199` and `8c1c2130` still carry that override. ## 7. Open follow-ups worth new tasks 1. **`~/.todo-app/prompts/planning.md` shadows the planning prompt.** `PromptFiles.EnsureExists` only writes a default when the file is absent, and that file exists (dated Jun 2). The `maxTurns` guidance merged in `65db1cd` therefore **does not reach real planning sessions** until that file is updated by hand. `system.md` and `agent.md` are shadowed too. `merge-helper-system.md`/`merge-helper-initial.md` do **not** exist, so this run's handler prompt changes are live. 2. **MCP task DTOs expose no parent/child link.** The 9-child Usage unit had to be reconstructed from `sortOrder` and creation timestamps. `get_task`/`list_tasks` should return `parentTaskId` / `blockedByTaskId`. 3. **Visual verification open** on: the ConPTY fix (open a tile on a task whose description contains `->`), and — once merged — the roadblock reply box and the verify-gate field in the list settings modal. 4. **`"exited with code 1 and no result"` is too common.** Three runs died that way today, two with finished work. Worth investigating whether the auto-commit step can be made to survive a late CLI crash. 5. Nothing has been **pushed**. `main` is 22 commits ahead of `8d7ba1e`. ## 8. Rules this session operated under - Drive merges through the MCP tools. Never raw `git merge` / `reset` / `checkout`. - Hand-resolve only markers the tools left behind, then `continue_merge`. When committing by hand is unavoidable, stage **explicit paths** — never `git add -A`: the main checkout is shared with other sessions. - For a parent/children unit merge, pass the **parent** id to `continue_merge` / `abort_merge`. - Ask the user on anything ambiguous, risky, or destructive. - Never `delete_task` to dedupe — `Cancelled` keeps it visible and resettable.