Adds a new wizard page (positioned right after Welcome, FreshInstall only)
that auto-runs EnvironmentCheckService on entry and shows one row per check
with status icon, localized title/message, and hint+help-link on
failure/unknown. A "Recheck" button re-runs it, guarded against re-entrancy.
IInstallerPage gets a BlocksNavigation default member; WizardViewModel's
Next button now binds to CanGoNext, which the current page can veto (used
here while a check run is in flight or a blocking Error+Failed result is
present — Warnings and Unknown results never block). The summary line names
the blocking checks so a disabled Next is self-explanatory.
Wires up DI for the check pipeline (IProcessRunner, IPortOwnerResolver,
per-run ClaudeCliLookup) and adds the checks.* / installer.systemCheck.*
locale keys in en.json + de.json.
Visual appearance is NOT verified — needs a manual pass in the running
installer.
Git/GitIdentity/Port/WriteAccess and Claude CLI/Version/Auth/PermissionModeAuto
checks plus the ExecutableResolver they depend on were built in two sibling
task branches that hadn't landed on main yet. Vendored the finished files in
from those branches (same content, verified building + tests green) so the
SystemCheckPage task has something to consume.
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.
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.
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.
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).
## 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
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.
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.
Replace the UniformGrid (all tiles forced to equal size) with a real
Grid + GridSplitters built in code-behind per pane count/column count,
so panes resize individually. The queue strip's fixed 210px column
becomes a drag-resizable Grid column (min 160px) that collapses to 0
when nothing is queued, replacing the old dock-based fixed width.
Adds a "Reading efficiently" section to SystemDefault covering
locate-before-read (Grep/Glob), targeted reads (offset/limit), avoiding
re-reads, and using an exploration subagent as a context firewall for
orientation questions. Based on measurement across 144 real sessions
showing context resend (Read especially) dominates token cost.
All three brief tasks and the nine-child Usage Monitor unit are merged and main
verified green. Correct section 5: "exited with code 1 and no result" is a
catch-all, not a CLI crash — max_turns (sonnet presets default to 30 because
app_settings.model_presets is NULL) and the account session limit both surface
through the same message.
Add a dedicated Usage Monitor & Gate section to Worker CLAUDE.md, wire
UsagePillViewModel/UsageMonitorModalViewModel and the new IWorkerClient
usage surface into Ui CLAUDE.md, and record open verification points
for the pill/modal visual pass and the real gate E2E in docs/open.md.
Data CLAUDE.md already covered the new entity columns and migration.
The pill tests asserted on real localized strings but never set Loc.Current,
which defaults to a key-echo localizer. They only passed because an unrelated
test class happened to install a real Localizer first; adding the usage-monitor
modal tests changed the ordering and the assertions started seeing raw keys.
Initialize Loc.Current in the constructor, matching every other UI test class.
RelayCommand<int> casts its object parameter directly rather than
converting it, so a string CommandParameter ("7"/"30") would throw
InvalidCastException at runtime. Split into two parameterless commands.
Adds a Usage Monitor modal (Worker menu + wired to the footer/Mission-Control
usage pill's Open command): dynamic gauges built from UsageSnapshotDto.Limits
with gate-threshold marks, a stale/blocked-gate band, and Models/Tasks tabs
backed by GetModelUsageAsync/GetTaskUsageAsync over a 7d/30d/custom range.
Macht die zwei Schwellen einstellbar. Setzt den Data-Subtask (Felder in `app_settings`) und den Hub-Subtask voraus.
**Zu bauen**
1. `AppSettingsDto` durchziehen: `WorkerHub` (`GetAppSettings` / `UpdateAppSettings`) und der UI-seitige Record in `WorkerClient.cs` bekommen `UsageGateFiveHourPct` und `UsageGateSevenDayPct`.
2. `GeneralSettingsTabViewModel`: zwei `[ObservableProperty]`-Felder, Validie
ClaudeDo-Task: 06068810-5b5c-4635-80dd-62eeba89fb8c
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.
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.
A task that reports a roadblock but finishes successfully (Done/WaitingForReview/
Failed/Cancelled) had no way to answer it short of a full reset-and-rerun, losing
the run's context. Adds a reply textbox + Send button to the existing ROADBLOCK
card, modeled on the AskUser question card, that resumes the session via
ContinueTaskAsync with the user's own text. Gated on LatestRunSessionId (disabled
with a hint when there's nothing to resume); failures surface through the footer
error strip instead of a modal.
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.
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.
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.
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.