Four IEnvironmentCheck implementations in src/ClaudeDo.Installer/Checks/:
- ClaudeCliCheck (Error) — resolves ctx.ClaudeBin via ExecutableResolver, runs
--version; failure message lists searched PATH entries + fallback dirs, flags
shim resolution (.cmd/.ps1) in Detail.
- ClaudeVersionCheck (Error) — tolerant version parsing (ignores surrounding
text), numeric System.Version comparison against a named floor constant
(2.1.220, see docs/explore-notes/installer-preflight.md §3). Unparseable or
missing CLI -> Unknown, never Failed.
- ClaudeAuthCheck (Error) — `claude auth status --json`, never sends a prompt.
- PermissionModeAutoCheck (Warning) — per the note's §2 conclusion, real
auto-mode eligibility (org/model/plan) has no cheap static signal, so this
only confirms `claude --help` still lists "auto" as a --permission-mode
choice. Kept as its own check rather than folded into ClaudeVersionCheck,
since the note explicitly separates "flag recognized" from "mode eligible"
and only the former is checkable at all.
All four share a new ClaudeCliLookup that resolves the CLI and runs
--version exactly once per install run (memoized, semaphore-guarded), so
none of the three version-dependent checks repeats that call.
Foundation prerequisites this task depends on (docs/explore-notes/
installer-preflight.md, ExecutableResolver, the IEnvironmentCheck/CheckResult/
EnvironmentCheckService abstraction, ProcessCommand, IProcessRunner) existed
only on an unmerged sibling branch, not on main. Rather than merging that
whole unreviewed branch, the specific prerequisite files were copied in
as-is (git history shows their origin). GitCheck/GitIdentityCheck/PortCheck/
WriteAccessCheck from that branch were intentionally NOT brought in — out of
scope for this task.
Deviations/decisions worth flagging:
- Added ExecutableResolver.FallbackDirectories() (public) so ClaudeCliCheck
can name the checked fallback locations in its failure message; the prior
branch kept that list private.
- Paths.cs now qualifies System.Environment explicitly. Adding the
ClaudeDo.Data.Environment namespace makes bare `Environment.X` inside any
ClaudeDo.Data.* namespace resolve to the sibling namespace instead of
System.Environment (C# prefers nested/enclosing namespace members over
usings) — this broke the build until qualified.
Not done (explicitly out of scope): no DI wiring into the wizard UI, no
XAML — matches how the prerequisite Git/Port/WriteAccess checks were also
left unwired.
Implements IEnvironmentCheck for the four checks derivable without a
Claude CLI probe:
- GitCheck (Error) - resolves git via ExecutableResolver (handles .cmd
shims), parses `git --version`.
- GitIdentityCheck (Warning) - user.name/user.email presence; Unknown
(not Failed) if git itself is missing, so it doesn't duplicate GitCheck's
failure.
- PortCheck (Warning) - loopback bind probe for SignalRPort/ExternalMcpPort;
resolves the owning process via a new NetstatPortOwnerResolver and treats
a port held by the running ClaudeDo.Worker (update/repair case) as Ok.
Both ports are configurable, hence a warning.
- WriteAccessCheck (Error) - create+delete a probe file in InstallDirectory
and ~/.todo-app (walking up to the first existing parent), not an ACL
read (ACLs lie on virtualized paths).
Process calls go through a new IProcessRunner wrapping the existing static
ProcessRunner, so checks are fakeable in tests instead of spawning real
processes.
DotnetRuntimeCheck was intentionally not added: per
docs/explore-notes/installer-preflight.md, App/Worker publish
self-contained (no preinstalled runtime needed), and the Installer's own
.NET 8 Desktop Runtime requirement is self-proving - a framework-dependent
apphost can't reach managed code at all if that runtime is missing, so a
check running from inside the process can never observe a failure.
Brings in two prerequisite commits this task builds on that hadn't reached
this branch yet: the IEnvironmentCheck/EnvironmentCheckService scaffolding
and the installer-preflight.md research note.