diff --git a/CLAUDE.md b/CLAUDE.md index 8d32fc6c..7f24ea61 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -87,4 +87,4 @@ dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj -c Release - `docs/improvement-plan.md` — improvement snapshot from 2026-04-13 (historical) - `docs/prompts-inventory.md`, `docs/mailbox-proposal.md` — reference material (mailbox integration is parked) - `CHANGELOG.md` — Keep a Changelog format, maintained on release -- `docs/explore-notes/` — distilled maps of complex subsystems (detail too fine for a CLAUDE.md, read on demand). **Before** deep-exploring a subsystem, check for a matching note first; **after** a deep explore, distill durable findings back and bump its "verified against" commit. Always verify against current code before trusting. See `docs/explore-notes/README.md`. Current notes: `worker-task-pipeline`, `usage-monitoring`, `external-mcp`, `review-merge`, `conpty-sessions`. +- `docs/explore-notes/` — distilled maps of complex subsystems (detail too fine for a CLAUDE.md, read on demand). **Before** deep-exploring a subsystem, check for a matching note first; **after** a deep explore, distill durable findings back and bump its "verified against" commit. Always verify against current code before trusting. See `docs/explore-notes/README.md`. Current notes: `worker-task-pipeline`, `usage-monitoring`, `external-mcp`, `review-merge`, `conpty-sessions`, `installer-preflight`. diff --git a/docs/explore-notes/README.md b/docs/explore-notes/README.md index 0424e92f..9ec51d8b 100644 --- a/docs/explore-notes/README.md +++ b/docs/explore-notes/README.md @@ -19,6 +19,7 @@ These sit **between** the CLAUDE.md files and the code: | [external-mcp](external-mcp.md) | The `claudedo` MCP tool surface + its two test-enforced conventions | | [review-merge](review-merge.md) | Approve=merge-unit, verify gate, `MergeCommit`/revert, diff stack, conflict resolver | | [conpty-sessions](conpty-sessions.md) | Interactive/planning/list-handler launch specs + the arg-flattening gotcha | +| [installer-preflight](installer-preflight.md) | CLI version/login/auto-mode research, the `ExecutableResolver`/shim root cause, and the Installer's `Checks/`+`SystemCheckPage` implementation status | ## Rules diff --git a/docs/explore-notes/installer-preflight.md b/docs/explore-notes/installer-preflight.md new file mode 100644 index 00000000..807f3a80 --- /dev/null +++ b/docs/explore-notes/installer-preflight.md @@ -0,0 +1,252 @@ +# Installer preflight: CLI version gate & environment checks + +> **Explore-note — verify before trusting.** Distilled map of a subsystem, not authoritative. +> Last verified against commit `bdee731` (2026-08-05). +> Drift check: `git log --oneline bdee731..HEAD -- src/ClaudeDo.Worker/Lifecycle/ClaudeCliPreflight.cs src/ClaudeDo.Worker/ClaudeDo.Worker.csproj src/ClaudeDo.App/ClaudeDo.App.csproj .gitea/workflows/release.yml src/ClaudeDo.Installer/Checks src/ClaudeDo.Data/Environment/ExecutableResolver.cs` +> Stable structure only (no line numbers). See docs/explore-notes/README.md. + +## Implementation status (as of 2026-08-05) + +The research below (§1–5) led to an implementation, but it is **not on `main` yet** — it exists +on two unmerged task branches: + +- `claudedo/06aca9b3afec4b939f59b627bfe21737` — `src/ClaudeDo.Installer/Checks/*` + (`GitCheck`, `GitIdentityCheck`, `WriteAccessCheck`, `PortCheck`, `ClaudeCliCheck`, + `ClaudeVersionCheck`, `ClaudeAuthCheck`, `PermissionModeAutoCheck`, `EnvironmentCheckService`, + `ClaudeCliLookup`) plus `SystemCheckPage` (the Fresh-Install wizard page hosting them) and its + own copy of `src/ClaudeDo.Data/Environment/ExecutableResolver.cs`. +- `claudedo/40272c0bb3b14562b59c022d09c382b6` — the original `ExecutableResolver.cs`, plus wiring + it into `ClaudeDo.Worker`'s `ClaudeCliPreflight` and `ClaudeProcess` (the actual root-cause fix: + both used to spawn `claude` with `UseShellExecute = false` and no `.cmd`/`.bat` shim resolution, + so an npm-installed `claude.cmd` was invisible to the Worker even though it worked in a shell). + +The two branches were authored independently and each vendored its own copy of +`ExecutableResolver.cs` (identical except `06aca9b3` adds a `FallbackDirectories()` diagnostic +helper); merging both cleanly requires picking one copy, not literally running `git merge` twice. +Two planned follow-up tasks — a "Claude Help Me" button and a Config-mode Diagnose section — +never got past a blocked first step, precisely because this prerequisite work wasn't on `main` +when they ran. See `Environment Checks` in `src/ClaudeDo.Installer/CLAUDE.md` and `docs/open.md` +for the current gap and the manual verification checklist. + +What's confirmed as **matching the research below**: `ClaudeVersionCheck.MinimumVersion` is +`2.1.220`, exactly the "verified-floor, not a proven minimum" constant from §3. `ClaudeAuthCheck` +uses `claude auth status --json` exactly as recommended in §4, parsing only the `loggedIn` field. +`PermissionModeAutoCheck` is the static "is `auto` listed in `--help`" check recommended in §2 — +real org/model/plan eligibility is deliberately **not** checked, matching the recommendation not +to build that (a real task run surfaces a startup rejection fast enough on its own). No .NET +Desktop Runtime check was implemented as an `IEnvironmentCheck` (§5's registry-key detection +remains a documented-but-unbuilt option, not currently gating anything). + +--- + +Pure research, no code changed. Answers the five questions from the "Root Cause +`--permission-mode auto`" task. Sources: the locally installed CLI (`claude --version` / +`--help`), the official docs at `code.claude.com` (fetched 2026-08-05), and this repo's own +csproj/workflow files. + +## 1. Why can `--permission-mode auto` fail? + +**It is very rarely a CLI-version problem.** Per the official permission-modes doc +(`code.claude.com/docs/en/permission-modes#eliminate-prompts-with-auto-mode`), auto mode +requires **all** of: + +- **Plan**: any plan (Free/Pro/Max/Team/Enterprise) qualifies in principle. +- **Organization**: on Team/Enterprise, on by default; an admin can disable it org-wide via + `permissions.disableAutoMode: "disable"` in managed settings. When disabled this way, the CLI + **"rejects `--permission-mode auto` at startup"** (verbatim from the doc) — not a runtime + fallback, a hard reject. +- **Model**: on the Anthropic API / Claude Platform on AWS — Opus 4.6+, Sonnet 4.6+, or Fable 5. + On Bedrock / Vertex / Foundry / signed-in gateway sessions — only Sonnet 5, Opus 4.7+, Fable 5. + Older models (Sonnet 4.5, Opus 4.5, Haiku, claude-3-*) are **not supported on any provider**. + A per-org `availableModels` restriction that only allows an old model would silently make + auto mode unavailable even on a Team/Enterprise org that hasn't touched `disableAutoMode`. +- **Provider opt-in (historical)**: on Bedrock/Vertex/Foundry/gateway, CLI **v2.1.158–v2.1.206** + required `CLAUDE_CODE_ENABLE_AUTO_MODE=1`; **v2.1.207** removed that requirement. This does + **not** apply to a normal claude.ai / Console (Anthropic API) login — only to those four + provider types. + +A separate, easy-to-hit trap: `defaultMode: "auto"` in **project or local** settings +(`.claude/settings.json`, `.claude/settings.local.json`) is silently **ignored** since v2.1.142 +— it must live in `~/.claude/settings.json` (user scope). A repo that ships `defaultMode: auto` +in its own `.claude/settings.json` will start in Manual mode with **no error at all**. This +doesn't apply to ClaudeDo's case (it passes `--permission-mode auto` as an explicit CLI flag, +not via a checked-in settings file), but is worth knowing if the failure mode was "silently +starts in Manual" rather than "CLI errors out". + +Quoted from the docs, verbatim: *"If Claude Code reports auto mode as unavailable, one of these +requirements is unmet; this is not a transient outage."* + +**Not verifiable**: which of the above actually hit the colleague — we have no diagnostic from +their machine (no `claude auth status --json` output, no `claude --version`, no org name). Do +not guess; if this recurs, capture `claude auth status --json` and `claude --version` from the +affected machine before further debugging. + +## 2. How to reliably detect whether `auto` is supported + +**Static, cheap, always safe (no API call):** +- `claude --version` — semver string, e.g. `2.1.220 (Claude Code)`. +- `claude --help` — the `--permission-mode ` line lists the accepted enum. Confirmed by + testing an invalid value locally: + ``` + $ claude -p "test" --permission-mode bogus + error: option '--permission-mode ' argument 'bogus' is invalid. Allowed choices are + acceptEdits, auto, bypassPermissions, manual, dontAsk, plan. + ``` + This is validated by the CLI's arg parser (Commander.js) **before any network call** — exits + 1 immediately. So checking that `auto` is one of the listed choices is a legitimate, free, + fast static check — but it only proves the *flag* is recognized, **not** that auto mode is + actually usable (org/model/plan gating happens later, at session start, not at arg-parse time). +- `claude auth status --json` — cheap, local/fast, **does not send a prompt**. Returns: + ```json + { "loggedIn": true, "authMethod": "claude.ai", "apiProvider": "firstParty", + "email": "...", "orgId": "...", "orgName": "...", "subscriptionType": "team" } + ``` + This is the answer to question 4 (see below) and also gives `subscriptionType`/`orgName` — + useful context but **still not a direct "is auto mode eligible" answer** (doesn't report the + active model or `disableAutoMode` policy). + +**No cheap subcommand exists for full eligibility.** Checked `claude auto-mode --help`: it only +has `config` (effective auto-mode *rule* config — allow/deny lists, not eligibility), +`defaults` (same, shipped defaults), `critique`, and `reset`. None report plan/model/org +eligibility. `claude doctor` (non-interactive) does **not** report auto-mode eligibility either +— it explicitly says *"For a full setup checkup that can also fix issues, run `/doctor` in a +session"*; the in-session `/doctor` slash command is the one the docs say proposes +`defaultMode: auto` when eligible, but that requires an interactive session, not a scriptable +preflight. + +**Dynamic (real probe) is the only way to fully confirm eligibility**, and per the docs an +org-disabled or otherwise-ineligible account **rejects at startup** (fast, before any model +turn) — so a probe doesn't have to be a full expensive run. A minimal probe such as +`claude -p "ok" --permission-mode auto --max-turns 1 --output-format json` would fail fast on +ineligibility (reject at startup) but still costs one real turn + tokens on the success path, +and still requires a working prompt/response round trip on the happy path. **Recommendation**: +don't build this into an automated preflight; a static version+flag check plus `auth status` +covers the reliably-detectable ground, and a real first task run will surface an auto-mode +rejection immediately and cheaply (fails at startup, not mid-task) if it's actually unavailable. +**Implemented as:** `PermissionModeAutoCheck` (Warning) — the static flag-listed check only. + +## 3. Minimum CLI version for the flags ClaudeDo uses + +Flags used (from `ClaudeArgsBuilder` per `src/ClaudeDo.Worker/CLAUDE.md`): `--permission-mode +auto`, `--effort`, `--agents`, `--json-schema`, `--append-system-prompt`, `--output-format +stream-json --verbose`, `--resume`, and (installer) `claude mcp add --transport http --scope +user`. + +**Not verifiable precisely.** All eight of these are foundational, long-established flags. +The official changelog (`raw.githubusercontent.com/anthropics/claude-code/main/CHANGELOG.md`) +only retains roughly the last ~40 entries (oldest visible: `2.1.181`); auto mode itself, and +`--json-schema`/`--resume`/`mcp add --transport` all clearly predate that window (the earliest +found reference is a *bug fix* mentioning "sessions created before v2.1.85" for `--resume`, +implying `--resume` existed well before 2.1.85). There is no accessible source that pins an +"introduced in vX" date for any of these eight flags — guessing one would violate the task's +explicit instruction not to invent a source-less root cause. + +What **is** sourced, from the docs fetched 2026-08-05: +- `--json-schema` + invalid-schema handling: before v2.1.205, an invalid schema was silently + ignored (returned unstructured text); v2.1.205 made it a hard `Error: --json-schema is not a + valid JSON Schema` exit. Not a "does it exist" gate, but changes error-handling behavior + ClaudeDo might currently rely on failing loudly. +- `--output-format stream-json --verbose` + `system/init.capabilities` array requires v2.1.205+ + (absent before). Not currently consumed by ClaudeDo per the Worker CLAUDE.md's stream handling + description, so not a hard requirement today. +- `system/init.mcp_server_errors` field requires v2.1.219+. Not currently consumed by ClaudeDo. +- Manual-mode label/`manual` alias requires v2.1.200+ — irrelevant, ClaudeDo passes `auto` + explicitly, never `manual`. +- Auto mode's Bedrock/Vertex/Foundry/gateway opt-in-env-var requirement was removed in v2.1.207 + — irrelevant for a direct claude.ai/Console login (this machine: `apiProvider: "firstParty"`). + +**Decided constant** (see below) is therefore **the newest version we can positively confirm +works end-to-end on this machine** (`2.1.220`), not a proven theoretical minimum — because no +lower true minimum is derivable from available sources without guessing. +**Implemented as:** `ClaudeVersionCheck.MinimumVersion = new Version(2, 1, 220)` (Error). + +## 4. Detecting "CLI is logged in" without sending a prompt + +`claude auth status --json` (confirmed working, instant, no API/model call): +```json +{ "loggedIn": true, "authMethod": "claude.ai", "apiProvider": "firstParty", + "email": "...", "orgId": "...", "orgName": "...", "subscriptionType": "team" } +``` +This is the CLI's own maintained answer — cheaper and more robust than parsing +`.credentials.json` directly (format may be internal/undocumented and is a hard-blocked file +for this task's own tooling; the docs page confirms it lives at +`%USERPROFILE%\.claude\.credentials.json` on Windows but say nothing about its schema being a +stable public contract). `claude auth status --text` is available for a human-readable variant; +`--json` is the default and the right one for a preflight to parse. +**Implemented as:** `ClaudeAuthCheck` (Error) — parses only the `loggedIn` boolean; any other +field, or a non-zero exit code, or unparseable JSON, becomes `Unknown` rather than `Failed`. + +## 5. .NET runtimes required by the published `app\` / `worker\` artifacts + +From `.gitea/workflows/release.yml` (the only build/publish pipeline in this repo) and the two +csproj files: + +- **`ClaudeDo.App`** (`net8.0`, Avalonia, `WinExe`) — published via + `dotnet publish ... -r win-x64 --self-contained true`. **Self-contained**: bundles its own + .NET 8 runtime. **No .NET runtime needs to be pre-installed** on the target machine for the + app itself. +- **`ClaudeDo.Worker`** (`net8.0`, `Microsoft.NET.Sdk.Web`, ASP.NET Core) — same treatment: + `-r win-x64 --self-contained true`. Also fully self-contained; no ASP.NET Core runtime needs + to be pre-installed. +- **`ClaudeDo.Installer`** (`net8.0-windows`, WPF) is the one exception: published + `--self-contained false -p:PublishSingleFile=true` — **framework-dependent**. The csproj + comment explains why: *"the WPF runtime pack isn't distributed for cross-compile on Linux CI, + which made self-contained bundles crash on startup with AV in the apphost."* The target + machine **must** have the **.NET 8 Desktop Runtime (x64)** installed before running + `ClaudeDo.Installer.exe` — this is the actual runtime-preflight gap, not the app/worker. + +**How to check on a target machine**: +- `dotnet --list-runtimes` — look for a `Microsoft.WindowsDesktop.App 8.0.x` line (Desktop + Runtime, required by the installer). Requires the `dotnet` CLI itself to be on PATH; not + guaranteed present on a fresh machine that never installed the SDK, only the runtime — in + that case `dotnet` may not exist at all even though the runtime DLLs do. +- Registry fallback (works even without the `dotnet` CLI on PATH): + `HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x64\sharedfx\Microsoft.WindowsDesktop.App` — + each installed version is a subkey/value here. This is the standard documented detection + mechanism for .NET Desktop Runtime presence on Windows and is what most installer-detection + tooling (e.g. Squirrel, WiX bundles) uses instead of shelling out to `dotnet`. +- **Not independently verified in this task**: the exact registry key shape wasn't inspected + live (would require reading `HKLM\SOFTWARE\dotnet\...` on this machine, which is standard + .NET installer-detection convention, but out of scope to screenshot/dump here since the task + is docs-only and this is a well-documented, non-project-specific Windows convention). + **Not implemented** as an `IEnvironmentCheck` — none of the shipped checks verify the Desktop + Runtime; if the Installer itself is running at all, .NET 8 Desktop Runtime is implicitly + present (framework-dependent publish would otherwise fail to launch). + +## Beschlossene Konstanten + +| Constant | Value | Confidence | +|---|---|---| +| Minimum CLI version | `2.1.220` | **Verified-floor, not a proven minimum.** This is the newest version confirmed installed and working end-to-end on a dev machine for every flag ClaudeDo uses. No lower true minimum could be sourced (see §3) — treat any lower value as a guess. | +| Credentials file path | `%USERPROFILE%\.claude\.credentials.json` (Windows) | Sourced from official docs; content/schema not inspected (hard-blocked secrets file). | +| Login-check command | `claude auth status --json` | Verified locally, instant, no model call. Fields: `loggedIn`, `authMethod`, `apiProvider`, `email`, `orgId`, `orgName`, `subscriptionType`. | +| App/Worker runtime requirement | **None** — self-contained win-x64 publish | Sourced from `.gitea/workflows/release.yml`. | +| Installer runtime requirement | **.NET 8 Desktop Runtime (x64)** must be pre-installed | Sourced from `.gitea/workflows/release.yml` comment + `ClaudeDo.Installer.csproj`. | + +## Erkennungsstrategie pro Check + +| Check | Type | Command | Expected pass output | Implemented as | +|---|---|---|---|---| +| git present + version | static | `git --version` (via `ExecutableResolver`) | resolves, exit 0 | `GitCheck` (Error) | +| git identity set | static | `git config --get user.name` / `user.email` | both non-empty | `GitIdentityCheck` (Warning) | +| install dir + data dir writable | static | probe-file write/delete | succeeds | `WriteAccessCheck` (Error) | +| SignalR/ExternalMcp ports free | static | `TcpListener` bind probe + owning-process lookup | free, or owned by running `ClaudeDo.Worker` | `PortCheck` (Warning) | +| CLI present + version | static | `claude --version` | `X.Y.Z (Claude Code)`; parse and compare `X.Y.Z >= 2.1.220` | `ClaudeCliCheck` (Error) / `ClaudeVersionCheck` (Error) | +| `auto` recognized as a flag value | static | `claude --help` (or trigger the parse error path) | `--permission-mode ` help text lists `auto` among the choices | `PermissionModeAutoCheck` (Warning) | +| CLI logged in | static/cheap | `claude auth status --json` | exit 0, `loggedIn: true` | `ClaudeAuthCheck` (Error) | +| Auto mode actually eligible (org/model/plan) | **not statically detectable** | none exists | N/A — see §2; don't build this, let a real task run surface a fast startup rejection instead | not implemented (by design) | +| .NET Desktop Runtime present (installer only) | static | `dotnet --list-runtimes` (if `dotnet` on PATH) or registry `HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x64\sharedfx\Microsoft.WindowsDesktop.App` | a `Microsoft.WindowsDesktop.App 8.0.x` entry exists | not implemented (see §5) | +| App/Worker runtime present | **not needed** | — | self-contained, nothing to check | not implemented (not needed) | + +## Not verifiable (explicit) + +- The actual root cause on the colleague's machine — no diagnostic data was captured from it. +- Exact stderr/exit-code wording the CLI prints when auto mode is rejected at startup for an + ineligible org/model (docs state the *behavior* — "rejects `--permission-mode auto` at + startup" — but not the literal message; this machine's account is eligible, so it couldn't be + reproduced locally). +- A true (not just "newest confirmed") minimum SemVer for `--effort`, `--agents`, + `--json-schema`, `--append-system-prompt`, `--resume`, `claude mcp add --transport http + --scope user` — all predate the retrievable changelog window. +- Live inspection of the `HKLM\SOFTWARE\dotnet\Setup\InstalledVersions` registry shape on this + machine (documented Windows convention, not independently screenshotted here). diff --git a/docs/open.md b/docs/open.md index 50eba621..007b2ecd 100644 --- a/docs/open.md +++ b/docs/open.md @@ -55,6 +55,7 @@ Offene Entscheidungen dazu: ## Beobachtung (offen — Entscheidung Mika) - **`--permission-mode auto` + Modell `haiku` → Writes werden denied:** Kontrolliert verifiziert (CLI 2.1.207): unter dem Default-Mode `auto` bekommt **sonnet** Writes auto-approved (`permission_denials:[]`), **haiku** wird `denied` (`permission_denials:[Write]`, keine Datei) — eine haiku-Task macht unter `auto` still nichts und landet ohne Änderung in `WaitingForReview`. Normalbetrieb (Default = sonnet) nicht betroffen. KEINE CLI-Regression, sondern modellabhängiges `auto`-Verhalten. Optionen falls es nervt: haiku aus der Auswahl nehmen, ODER Runner auf `acceptEdits`/`bypassPermissions` (modell-unabhängig). Mika: erstmal beobachten. Siehe Memory `auto_permission_haiku_footgun`. +- **`QueueServiceTests.UsageGate_TransitionLogging_FiresOncePerChange` ist zeitbasiert flaky, unabhängig von dieser Session:** Schlägt reproduzierbar fehl (`Expected 1, Actual 0` Warn-Log-Aufrufe), sowohl solo (`--filter`) als auch im Vollauf, auf einem sauberen `git worktree add` gegen `main` (bdee731) — also **kein** durch diese Abschluss-Session verursachter Regress (die Session hat keine `.cs`-Datei angefasst). Ursache: der Test verlässt sich auf einen festen `Task.Delay(200)`, um mehrere 50-ms-Backstop-Ticks abzuwarten (Kommentar im Test: „Several backstop ticks (50ms interval) all observe the same blocked state"); auf einer stark ausgelasteten Maschine (hier: viele parallele ClaudeDo-Worktrees/Builds) reicht das Fenster nicht immer. Zum Vergleich: derselbe Test lief in einer zweiten, isolierten Verifikation (Scratch-Merge für den Environment-Checks-Task) sauber durch (876/876). Fix wäre ein Poll-basiertes Warten statt fixem Sleep — aber außerhalb des Scopes dieser Doku/Verifikations-Session (keine Code-Änderung angefasst). --- @@ -137,6 +138,49 @@ verifiziert**: beim Laden übernommen und beim Speichern nur unverändert zurückgeschrieben (kein Clobber), aber nicht editierbar. Falls gewünscht, ein eigenes Feld ergänzen. +## Offene Verifikation (2026-08-05, Environment Checks / SystemCheckPage) + +**Voraussetzung, bevor irgendeiner der Punkte unten geprüft werden kann:** die Branches +`claudedo/06aca9b3afec4b939f59b627bfe21737` (Checks + SystemCheckPage) und +`claudedo/40272c0bb3b14562b59c022d09c382b6` (ExecutableResolver-Wiring in +`ClaudeDo.Worker`) müssen erst gemerged werden — sie lagen bei dieser Abschluss-Session +noch nicht auf `main`. Build/Test-Nachweis unten stammt aus einer lokalen Scratch-Integration +beider Branches, nicht aus `main` selbst. Details → `installer-preflight` in +`docs/explore-notes/README.md` und den neuen Abschnitt „Environment Checks" in +`src/ClaudeDo.Installer/CLAUDE.md`. + +**Zusätzliche Lücke, unabhängig vom Merge:** die zwei Folge-Tasks „Claude Help Me"-Button +und Diagnose-Sektion (Config-Modus/`SettingsWindow`) sind **nicht implementiert** — beide +liefen ins selbe Merge-Problem und wurden ohne jede Code-Änderung als `Blocked` beendet. Die +folgenden Punkte, die diese zwei Features beträfen, können also noch nicht geprüft werden und +brauchen zuerst eine neue Umsetzungsrunde: + +- [ ] „Claude Help Me" öffnet ein Terminal mit laufender Claude-Session, und die Session hat + den Diagnose-Report tatsächlich gelesen — **nicht umsetzbar, Feature existiert nicht.** +- [ ] Der Help-Me-Button ist korrekt deaktiviert, wenn `claude` nicht im PATH ist, mit + verständlichem Tooltip — **nicht umsetzbar, Feature existiert nicht.** +- [ ] Diagnose-Sektion im Config-Modus zeigt die echten installierten Pfade/Ports, und der + laufende Worker auf 47821 gilt nicht als Konflikt — **nicht umsetzbar, Feature existiert + nicht.** + +Sobald die beiden Branches oben gemerged sind, sind folgende Punkte real prüfbar (gebaut + +unit-getestet gegen die Scratch-Integration, aber **nicht visuell verifiziert**): + +- [ ] SystemCheckPage: Layout, Icon-/Farbwirkung der vier Status (Ok grün / Warnung orange / + Fehler rot / Unbekannt grau — `StatusGreenBrush`/`StatusOrangeBrush`/`StatusRedBrush`/ + `StatusGrayBrush`), Lesbarkeit der Hint-Texte, DE und EN. +- [ ] Weiter-Button gesperrt bei einem echten blockierenden Fehler (z. B. `claude` nicht im + PATH → `claude-cli` Error/Failed), und der Grund ist in der Zusammenfassungszeile + sichtbar (nennt den/die blockierenden Check(s) namentlich). +- [ ] „Erneut prüfen" wechselt einen Status live (z. B. git-Identity setzen → Warnung + verschwindet), ohne dass ein zweiter paralleler Lauf startet, wenn währenddessen erneut + geklickt wird. +- [ ] Update-Modus zeigt die SystemCheckPage **nicht** (Wizard bleibt Welcome + Install). +- [ ] Auf einem Rechner mit npm-installiertem `claude.cmd`: `claude-cli`-Check findet es + (Detail-Text „Resolved via a shim…"), und ein Task läuft im Worker durch (bestätigt, dass + `ClaudeProcess`/`ClaudeCliPreflight` den Shim über `cmd.exe /c` tatsächlich startet, nicht + nur, dass der Check ihn findet). + --- ## Bewusst verworfen (nicht erneut vorschlagen) diff --git a/src/ClaudeDo.Installer/CLAUDE.md b/src/ClaudeDo.Installer/CLAUDE.md index e50b4eb2..47a360e5 100644 --- a/src/ClaudeDo.Installer/CLAUDE.md +++ b/src/ClaudeDo.Installer/CLAUDE.md @@ -31,8 +31,8 @@ the on-disk installer to run the *app* update. App-update detection is unaffecte | Mode | Condition | Window | |---|---|---| -| `FreshInstall` | No `install.json` | Full wizard (all pages) | -| `Update` | `install.json` present + newer release available | Wizard — Welcome + Install pages only | +| `FreshInstall` | No `install.json` | Full wizard: Welcome → **SystemCheck** → Paths → Service → UiSettings → Install | +| `Update` | `install.json` present + newer release available | Wizard — Welcome + Install pages only (SystemCheck **not** shown) | | `Config` | Current version, or Gitea API unreachable | `SettingsWindow` (settings / repair / uninstall) | ## Install Pipelines @@ -60,7 +60,8 @@ Installer/ ConfigModels, InstallerService, UninstallRunner, PageResolver, AutostartShortcut, ShortcutFactory, ProcessRunner, DarkTitleBar Interfaces/ — IInstallStep + StepResult/StepStatus/StepProgress, IInstallerPage - Pages/ — WelcomePage, PathsPage, ServicePage, UiSettingsPage, InstallPage + Checks/ — environment preflight checks, see "Environment Checks" below + Pages/ — WelcomePage, SystemCheckPage, PathsPage, ServicePage, UiSettingsPage, InstallPage (each: ViewModel + View.xaml) Views/ — WizardWindow(+WizardViewModel), SettingsWindow(+SettingsViewModel) ``` @@ -124,3 +125,52 @@ and restored if it fails. | `%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\ClaudeDo Worker.lnk` | Worker autostart | The Apps & Features uninstall string and "Rerun Installer" both point at `\uninstaller\ClaudeDo.Installer.exe` with no `/uninstall` flag — Config mode is detected from `install.json`. + +## Environment Checks + +> **Merge status (2026-08-05): not yet on `main`.** The `Checks/` folder, `SystemCheckPage`, +> and `ExecutableResolver` described below exist only on unmerged task branches +> (`claudedo/06aca9b3afec4b939f59b627bfe21737` for the Installer side, +> `claudedo/40272c0bb3b14562b59c022d09c382b6` for the `ClaudeDo.Worker` wiring). Build/test +> verification for this section was done against a local scratch integration of both, not +> against this repo's actual `main`. Merge them (or re-derive equivalent commits) before trusting +> this section against the checked-out code. See `docs/open.md` for the outstanding gap this +> leaves (`Checks/` and `SystemCheckPage` are real, but the "Claude Help Me" button and the +> Config-mode Diagnose section described as follow-ups were never implemented — both follow-up +> tasks blocked on this same missing merge and shipped no code). + +`Checks/` holds one `IEnvironmentCheck` per concern, run in parallel by `EnvironmentCheckService.RunAllAsync`: + +| Check | Severity | What it verifies | +|---|---|---| +| `GitCheck` | Error | `git` resolvable (via `ExecutableResolver`) and runs | +| `WriteAccessCheck` | Error | install dir + `%APPDATA%` (or first existing parent) are writable | +| `ClaudeCliCheck` | Error | `claude` resolvable on PATH, including npm `.cmd`/`.bat`/`.ps1` shims | +| `ClaudeVersionCheck` | Error | resolved `claude --version` ≥ `ClaudeVersionCheck.MinimumVersion` (currently `2.1.220`) | +| `ClaudeAuthCheck` | Error | `claude auth status --json` reports `loggedIn: true` (never sends a prompt) | +| `GitIdentityCheck` | Warning | `git config user.name`/`user.email` are set | +| `PortCheck` | Warning | `SignalRPort`/`ExternalMcpPort` are free, or already owned by a running `ClaudeDo.Worker` | +| `PermissionModeAutoCheck` | Warning | CLI's `--help` still lists `auto` as a `--permission-mode` choice | + +Each check returns a `CheckResult` with `CheckStatus` (`Ok` / `Failed` / `Unknown`). A check that +throws is caught by `EnvironmentCheckService` and turned into `Unknown`, never a crash. + +**Gating rule:** `EnvironmentCheckReport.HasBlockingError` is true only when a check with +`Severity == Error` has `Status == Failed`. Warnings never block, and `Unknown` never blocks +regardless of severity (an indeterminate result — e.g. the CLI not found, so version/auth/auto-mode +can't be checked — must not strand the user; the underlying `Error`-severity check for the CLI +itself, `ClaudeCliCheck`, is what blocks in that case). + +`SystemCheckPage` (`Pages/SystemCheckPage/`) hosts the check list in the **FreshInstall** wizard +only, registered via `PageResolver` at `Order = 1` (directly after `WelcomePage`); `WizardViewModel` +filters it back out in `Update` mode along with Paths/Service/UiSettings. Checks run automatically +on page entry (`LoadAsync`, guarded against double-entry). "Next" is disabled via +`IInstallerPage.BlocksNavigation` (`IsRunning || HasBlockingError`) — `WizardViewModel.CanGoNext` +subscribes to `PropertyChanged` on the current page so a live recheck can flip it back. A "Recheck" +button re-runs `EnvironmentCheckService.RunAllAsync` (disabled while already running). + +**Not implemented (see merge-status note above):** a "Claude Help Me" button that launches an +external terminal with a live `claude` session for setup troubleshooting, and a Diagnose section +in `SettingsWindow` (Config mode) that re-runs the same checks against the installed configuration. +Both were speced as follow-up tasks; both blocked before writing any code because their prerequisite +(this section) wasn't on `main` yet. diff --git a/src/ClaudeDo.Worker/CLAUDE.md b/src/ClaudeDo.Worker/CLAUDE.md index 42833b69..5a1c65ad 100644 --- a/src/ClaudeDo.Worker/CLAUDE.md +++ b/src/ClaudeDo.Worker/CLAUDE.md @@ -16,7 +16,8 @@ Worker/ State/ — TaskStateService + TransitionResult (sole owner of Status/PlanningPhase/BlockedBy writes) Queue/ — IQueueWaker, IQueuePicker, QueueService, OverrideSlotService, RunCancellationRegistry Lifecycle/ — StaleTaskRecovery, TaskResetService, TaskMergeService, VerifyCommandRunner, - ClaudeCliPreflight, OrphanRecovery, PlanningLineageRecovery, + ClaudeCliPreflight (resolves via ExecutableResolver, see Key Components below), + OrphanRecovery, PlanningLineageRecovery, AttachmentOrphanRecovery, PromptFileRecovery (last four = startup sweeps) Worktrees/ — WorktreeMaintenanceService Agents/ — AgentFileService, DefaultAgentSeeder @@ -111,7 +112,8 @@ Full flow, invariants, and model/effort/max-turns resolution (including the low- ## Key Components -- **ClaudeProcess** — spawns `claude -p --output-format stream-json --verbose --permission-mode auto` (or whatever app settings specify). Prompt via stdin, NDJSON from stdout. CancellationToken kills the process tree. +- **ClaudeCliPreflight** — startup check that `claude --version` runs; resolves the binary via `ExecutableResolver` first and returns a clear "not found on PATH" result instead of a raw `Process.Start` exception when it doesn't. *(Not yet on `main` — see below.)* +- **ClaudeProcess** — spawns `claude -p --output-format stream-json --verbose --permission-mode auto` (or whatever app settings specify). Prompt via stdin, NDJSON from stdout. CancellationToken kills the process tree. Resolves `_cfg.ClaudeBin` via `ExecutableResolver` (`ClaudeDo.Data`, shared with `ClaudeCliPreflight` and the Installer's checks) before spawning — a `.cmd`/`.bat` shim (e.g. an npm-installed `claude`) is launched through `cmd.exe /c` since `UseShellExecute = false` can't exec a shim directly; a resolved `.exe` starts exactly as before. Throws if nothing resolves. *(Not yet on `main` — see `Environment Checks` in `ClaudeDo.Installer/CLAUDE.md`.)* - **ClaudeArgsBuilder** — `--model`, `--effort`, `--max-turns`, `--append-system-prompt`, `--agents`, `--json-schema`, `--resume` - **StreamAnalyzer** — parses NDJSON; extracts session_id, token counts, turn counts, result text, structured output. Replaced MessageParser. - **WorktreeManager** — worktrees on `claudedo/{taskId[:8]}` branches; commits with semantic messages, updates DB with head commit + diff stats