AppSurface Search
Guide

Design: LocalSecrets Linux Trusted secret-tool Resolution

Source of truth

View source Edit this page

Last updated

Generated by /office-hours and queued for /autoplan on 2026-06-21 Branch: codex/localsecrets-secret-tool-autoplan Repo: forge-trust/AppSurface Status: DRAFT Mode: Builder

Problem Statement

Project item 202769416 tracks [0.1.0 security][low] LocalSecrets Linux adapter executes secret-tool discovered from PATH. PlatformAppSurfaceLocalSecretStore.CreateInnerStore currently resolves Linux Secret Service by calling FindOnPath("secret-tool"), then executes the first matching path for LocalSecrets get, set, delete, list, and doctor operations. That creates an untrusted executable search path risk for a public package that otherwise frames LocalSecrets as a fail-closed pre-vault secret posture.

Chosen Approach

Use a trusted resolver plus an explicit override:

  • Stop searching arbitrary PATH for secret-tool.
  • Resolve default Linux secret-tool only from trusted system candidates, in order: /usr/bin/secret-tool, then /bin/secret-tool.
  • Define "trusted system candidate" as an AppSurface-maintained absolute path allowlist for conventional root-managed OS locations. The resolver may check PATH only to explain that a candidate was ignored; it must never execute from PATH.
  • Add an explicit full-path override for nonstandard Linux installs.
  • Reject relative, empty, missing, directory, non-executable, and unsupported-platform override paths with paste-safe diagnostics before process launch.
  • Keep LocalSecrets scoped to local, pre-vault, user-session secret storage. Do not introduce a generic vault abstraction or a native libsecret binding in this issue.

Key Changes

  • Add AppSurfaceLocalSecretsOptions.LinuxSecretToolPath.
    • Default is null, meaning trusted system candidates only.
    • XML docs must call this an advanced Linux-only escape hatch for trusted nonstandard installs.
  • Update PlatformAppSurfaceLocalSecretStore.
    • Keep the parameterless constructor for source compatibility.
    • Add an options-aware constructor suitable for DI so AppSurfaceLocalSecretsModule can pass configured options.
    • Replace FindOnPath with an internal Linux executable resolver that is directly testable without reflection.
    • Parameterize the resolver over candidate paths and filesystem/executable probes so tests do not depend on the host's real /usr/bin or /bin state.
    • Map command startup failures from Process.Start() into LocalSecretResultStatus.Unavailable diagnostics so an invalid executable cannot escape the LocalSecrets result contract.
    • Preserve existing result statuses; use terminal display-safe diagnostics rather than adding a new public enum value.
  • Update appsurface secrets commands.
    • Add --secret-tool-path to secrets init|set|get|list|delete|doctor.
    • Reject --secret-tool-path together with --store-file; do not silently ignore one, because that produces false confidence in doctor.
    • Keep the override as a one-shot CLI flag. Do not add an environment variable or persistent CLI config in this issue; package consumers use AppSurfaceLocalSecretsOptions.LinuxSecretToolPath for app runtime persistence.
    • Keep --store-file as the deterministic examples/tests path.
  • Update package docs.
    • README platform matrix must say Linux uses trusted secret-tool locations or an explicit override, not PATH search.
    • Linux/local-secrets pitfalls must explain why PATH discovery is intentionally avoided.
    • Add a "Linux nonstandard secret-tool" recipe with test -x, a shell variable, doctor, set, and the matching ConfigureAppSurfaceLocalSecrets snippet.
    • Add an "escape hatches, safest first" section: trusted defaults, LinuxSecretToolPath, --store-file for deterministic examples/tests, custom store registration, and FailClosedOnStoreFailure = false as the last-resort posture change with explicit risk.
    • CI/headless/container guidance remains env/key-per-file, not LocalSecrets.

Existing Code To Reuse

  • Config/ForgeTrust.AppSurface.Config.LocalSecrets/PlatformAppSurfaceLocalSecretStore.cs
    • UnsupportedPlatformLocalSecretStore already produces display-safe platform diagnostics.
    • LinuxSecretServiceLocalSecretStore already accepts a concrete executable path and command runner.
    • DefaultPlatformSecretCommandRunner already uses structured ProcessStartInfo.ArgumentList with UseShellExecute = false.
  • Config/ForgeTrust.AppSurface.Config.LocalSecrets/AppSurfaceLocalSecretsOptions.cs
    • Existing options are the right public API surface for package consumers.
  • Cli/ForgeTrust.AppSurface.Cli/SecretsCommand.cs
    • Existing command base already centralizes shared options and platform/file store selection.
  • Config/ForgeTrust.AppSurface.Config.LocalSecrets.Tests/PlatformAppSurfaceLocalSecretStoreTests.cs
    • Existing internal test access and fake command runner support deterministic branch coverage.

Not In Scope

  • Native libsecret bindings.
  • A generic external-command resolver abstraction for unrelated packages.
  • New LocalSecretResultStatus enum values.
  • Team vaults, remote vault providers, CI secret injection, production rotation, or centralized audit.
  • Live macOS/Windows/Linux backend automation in normal CI. Manual or quarantined smoke evidence may be added, but the main regression suite should remain deterministic.

Test Plan

  • Add resolver unit tests:
    • ignores fake secret-tool on PATH;
    • selects /usr/bin/secret-tool before /bin/secret-tool;
    • returns a safe diagnostic when no trusted candidate exists;
    • accepts an absolute existing override path;
    • rejects relative, empty, missing, directory, and non-executable override paths;
    • rejects overrides supplied on non-Linux platforms.
  • Add command-runner tests proving startup exceptions are mapped to Unavailable diagnostics rather than thrown.
  • Add CLI tests proving --secret-tool-path flows into platform store selection and that --secret-tool-path plus --store-file throws a CommandException for doctor and one key command.
  • Add DI/options tests proving ConfigureAppSurfaceLocalSecrets(options => options.LinuxSecretToolPath = ...) affects the default platform store.
  • Update README/package docs contract tests where package docs are verified.
  • Verification commands:
    • dotnet test Config/ForgeTrust.AppSurface.Config.LocalSecrets.Tests/ForgeTrust.AppSurface.Config.LocalSecrets.Tests.csproj --no-restore
    • dotnet test Cli/ForgeTrust.AppSurface.Cli.Tests/ForgeTrust.AppSurface.Cli.Tests.csproj --no-restore
    • package docs/index verification used by this repo
    • dotnet format --verify-no-changes
    • git diff --check

Risks And Review Focus

  • Public API: LinuxSecretToolPath must be documented as Linux-only and advanced, not as a normal happy-path setting.
  • Compatibility: nonstandard installs such as Nix, Linuxbrew, or custom prefixes need the explicit override path.
  • Security: no code path should fall back to arbitrary PATH search.
  • Diagnostics: failures must say problem, cause, and fix without printing secret values.
  • Prior project learning: LocalSecrets platform-store patches can look logically correct while still having residual live-backend risk, because most coverage uses fakes. Keep that risk visible in review and release notes.

Success Criteria

  • No arbitrary PATH search remains for Linux secret-tool.
  • Package consumers can configure a trusted nonstandard path deliberately.
  • CLI users can pass a trusted nonstandard path deliberately.
  • Existing happy-path Linux command execution still uses LinuxSecretServiceLocalSecretStore once a trusted path is resolved.
  • Tests cover success, failure, override, and bypass branches through public/internal test seams without reflection.

/autoplan Review

Phase 0: Intake And Scope

  • Base branch: main.
  • Branch: codex/localsecrets-secret-tool-autoplan.
  • Current commit: bc9f58e8.
  • UI scope: no. Design review skipped.
  • DX scope: yes. This is a package and CLI change with user-facing flags, docs, diagnostics, and migration behavior.
  • Affected code evidence:
    • PlatformAppSurfaceLocalSecretStore.CreateInnerStore currently calls FindOnPath("secret-tool").
    • LinuxSecretServiceLocalSecretStore already accepts a concrete command path.
    • DefaultPlatformSecretCommandRunner.Run uses Process.Start() directly, so command startup failures must be mapped.
    • SecretsCommandBase.BuildContext centralizes platform/file store selection.

Phase 1: CEO Review

Mode: SELECTIVE_EXPANSION.

Premise challenge:

Premise Verdict Review Decision
LocalSecrets should be fail-closed even before a remote vault exists. Valid. Keep the security hardening in scope.
Removing PATH search is enough. Incomplete. Reframe as an external-command trust policy.
/usr/bin and /bin are automatically safe. Too implicit. Define them as conventional root-managed candidates, not a universal proof of trust.
Nonstandard installs should still work. Valid. Require an explicit absolute override with diagnostics and docs.
Native libsecret should wait. Valid for this issue. Document as tactical mitigation, not the long-term Linux ideal.

What already exists:

Sub-problem Existing Code To Reuse
Concrete Linux command execution LinuxSecretServiceLocalSecretStore(secretToolPath)
Safe command argument passing DefaultPlatformSecretCommandRunner uses ArgumentList and UseShellExecute = false
Display-safe diagnostics AppSurfaceLocalSecretDiagnostic and UnsupportedPlatformLocalSecretStore
Shared CLI store selection SecretsCommandBase.BuildContext
Package option shape AppSurfaceLocalSecretsOptions and ConfigureAppSurfaceLocalSecrets

Dream state delta:

CURRENT
  Linux LocalSecrets executes first secret-tool found on PATH.
THIS PLAN
  Linux LocalSecrets executes only a trusted candidate or explicit absolute override,
  with diagnostics that explain ignored PATH candidates and exact remediation.
12-MONTH IDEAL
  Linux backend avoids external process trust where practical, or documents a stable,
  tested external-command policy with live smoke evidence.

Alternatives considered:

Approach Effort Risk Decision
Minimal allowlist only Low Breaks Nix/Linuxbrew/custom installs without a rescue path. Rejected.
Trusted resolver plus explicit override Medium Adds API/CLI surface, but keeps compatibility intentional. Chosen.
Native libsecret binding High Larger portability, packaging, and live-backend risk. Deferred.

CEO dual voices:

Dimension Subagent Codex Consensus
Premises valid? Mostly, but external-command policy is unstated. Same concern. Confirmed.
Right problem to solve? Yes, but frame it as trust policy. Yes, do not let low severity shrink it. Confirmed.
Scope calibration correct? Needs diagnostics/docs elevated. CLI surface may be inflated. Taste decision.
Alternatives sufficiently explored? Native backend needs rationale. Same. Confirmed.
Competitive/market risks covered? Developer trust and compatibility need owning. Same. Confirmed.
6-month trajectory sound? Needs maintainer policy for future paths. Same. Confirmed.

CEO auto-decisions:

  • Keep --secret-tool-path on all local secret subcommands because store selection is centralized and developers need the same trusted path for init, set, get, list, delete, and doctor.
  • Do not add an environment variable for the override in this issue. A second ambient source would reintroduce surprise around executable selection. Apps get persistence through options; CLI gets an explicit one-shot flag.
  • Document native libsecret as deferred because the blast radius is larger than this security item and deterministic coverage would not prove the native desktop behavior anyway.

Error and rescue registry:

Error Cause Required Rescue
No trusted candidate found /usr/bin/secret-tool and /bin/secret-tool missing Show checked paths, ignored PATH candidate if present, exact --secret-tool-path and options fix
Invalid override Empty, relative, missing, directory, or non-executable path Fail before launch with Problem, Cause, Fix, Docs, Retryable=false
Override on unsupported OS Linux-only path supplied on macOS/Windows Explain the native platform store is used instead; fail rather than silently ignore
--store-file plus --secret-tool-path Test store and platform override both supplied Throw CommandException with one clear choice
Process startup failure Existing file cannot be executed or OS rejects launch Map to Unavailable diagnostic, not an uncaught exception

Phase 2: Design Review

Skipped. This plan has no UI, layout, navigation, or visual interaction scope.

Phase 3: Engineering Review

Architecture diagram:

CLI command / App module
  |
  | app runtime: IOptions<AppSurfaceLocalSecretsOptions>
  | CLI runtime: --secret-tool-path
  v
PlatformAppSurfaceLocalSecretStore
  |
  +-- macOS -> MacOsKeychainLocalSecretStore
  +-- Windows -> WindowsCredentialManagerLocalSecretStore
  +-- Linux -> LinuxSecretToolResolver
        |
        +-- trusted candidates: /usr/bin/secret-tool, /bin/secret-tool
        +-- explicit absolute override: LinuxSecretToolPath / --secret-tool-path
        +-- rejected inputs -> Unsupported/Unavailable diagnostic
        v
     LinuxSecretServiceLocalSecretStore(concretePath)
        v
     DefaultPlatformSecretCommandRunner

Code quality findings:

Finding Severity Decision
Resolver tests cannot depend on real /usr/bin or /bin. P1 Add candidate/probe seams to the internal resolver.
Existing Process.Start() can throw below the diagnostic layer. P1 Catch startup exceptions and map to LocalSecrets diagnostics.
--store-file plus --secret-tool-path can produce false-positive doctor output. P1 Reject the combination.
Linux list is O(n) platform command calls across indexed keys. P2 Document as existing behavior and add bounded/fail-fast tests; batch/search list is deferred.

Test diagram:

CODE PATHS                                                   TEST REQUIREMENT
[+] Linux resolver
  |-- no override, /usr/bin exists                           unit: chooses /usr/bin
  |-- no override, only /bin exists                           unit: chooses /bin
  |-- fake PATH candidate only                                unit: ignored, diagnostic mentions PATH ignored
  |-- no trusted candidate                                    unit: unsupported/unavailable diagnostic
  |-- override empty/relative/missing/directory               unit: invalid override diagnostic
  |-- override existing but non-executable                    unit: invalid override diagnostic
  |-- override on non-Linux                                   unit: unsupported override diagnostic
[+] Platform store construction
  |-- parameterless constructor                               existing source compatibility smoke
  |-- options-aware constructor                               unit/DI: uses LinuxSecretToolPath
[+] Command runner
  |-- process returns exit code                               existing tests
  |-- process times out                                       existing tests
  |-- process fails to start                                  unit: maps to Unavailable diagnostic
[+] CLI command base
  |-- no store-file, override supplied                        CLI test: platform store path receives override
  |-- store-file supplied                                     existing file-store tests
  |-- store-file + override supplied                          CLI test: CommandException
[+] Docs/package index
  |-- README platform matrix                                  docs contract
  |-- CLI README override recipe                              docs contract
  |-- release note security hardening                         package/release verification

Failure modes registry:

Failure Mode Impact Plan Coverage
PATH hijack remains possible through a fallback branch Security regression P1 test: fake PATH candidate ignored
Override path points at a non-executable file Uncaught runtime exception P1 resolver and command-runner tests
Doctor verifies file store while user meant platform store False confidence P1 CLI conflict rejection
Nonstandard Linux install cannot recover Adoption regression P1 docs recipe and exact diagnostic fix
Real DBus/Secret Service behavior differs from fakes Release risk Manual/quarantined smoke checklist stays visible

Performance review:

  • No database, network, cache, or memory hot path is introduced.
  • Existing Linux list behavior can run one command per indexed key. This issue should not redesign listing, but tests should prove terminal failure returns early and docs should avoid promising list performance for large namespaces.

Engineering consensus:

Dimension Subagent Codex Consensus
Architecture sound? Sound with resolver seam. Advisory voice also requested trust definition. Confirmed.
Test coverage sufficient? Needs non-executable, conflict, startup-failure tests. Advisory voice requested migration diagnostics. Confirmed.
Performance risks addressed? O(n) list behavior should be documented. No conflicting concern. Single-voice flag.
Security threats covered? Needs no PATH fallback plus executable validation. Same. Confirmed.
Error paths handled? Needs Process.Start exception mapping. Same. Confirmed.
Deployment risk manageable? Manageable with docs/release note. Compatibility break underplayed. Confirmed.

Phase 3.5: DX Review

Target developer persona:

Field Value
Who Solo or small-team .NET developer adding AppSurface LocalSecrets before a remote vault exists
Context Local Linux development, often with Nix/Linuxbrew/custom installs or a distro where secret-tool is not in /usr/bin
Tolerance 5 minutes to diagnose and recover; low tolerance for security docs that do not give a copy-ready command
Expects Safe defaults, explicit override, no secret printing, and error text that says exactly what to run next

Developer perspective:

I add LocalSecrets, run appsurface secrets doctor, and it says Linux Secret Service is unavailable. I know secret-tool exists because my shell can run it, but AppSurface says it ignored PATH. That is acceptable only if the diagnostic tells me which paths it checked, why PATH is not trusted, how to verify my real binary with test -x, and the exact --secret-tool-path "$SECRET_TOOL" command to rerun. Without that, this looks like a regression, not a security feature.

Competitive DX benchmark:

Tool TTHW Relevant DX Choice Source
Doppler CLI 2-5 min after install/login One doppler run -- your-command shape for runtime injection https://docs.doppler.com/docs/cli
1Password CLI 5-10 min after account/setup Secret references plus op run, op read, and op inject flows https://www.1password.dev/cli/secret-references
AppSurface LocalSecrets target Under 5 min from failed Linux doctor to fixed doctor Diagnostic prints exact one-shot CLI flag and options snippet This plan

TTHW assessment:

  • Current Linux override TTHW: unpredictable, roughly 10-20 minutes if docs remain generic.
  • Target Linux override TTHW: under 5 minutes from first failure to successful doctor.
  • Magical moment: appsurface secrets doctor --secret-tool-path "$SECRET_TOOL" reports the Linux Secret Service store is available without printing any secret value.

Developer journey map:

Stage Developer Does Friction Required Plan Change
Discover Reads LocalSecrets README and package index Linux trust policy hidden in platform matrix Add dedicated Linux resolver policy section
Install Installs package and CLI CLI install path outside this issue Reuse existing docs
Hello World Runs init, set, doctor Override can be forgotten per command Use shell variable recipe and doctor rerun text
Real Usage Configures app runtime CLI flag does not persist into app options Include ConfigureAppSurfaceLocalSecrets snippet
Debug Sees diagnostic Diagnostics need checked paths, ignored PATH, exact fix Add diagnostic matrix and tests
Upgrade Existing PATH users break Compatibility hardening needs release note Add release note with migration path
Scale Lists many keys Existing O(n) calls can be slow Document behavior; defer redesign
Migrate Moves to remote/team vault LocalSecrets remains pre-vault Keep env/key-per-file/vault guidance
Secure Chooses escape hatch Wrong escape hatch can weaken posture Add safest-first escape hatch docs

DX scorecard:

Dimension Initial Revised Plan
Getting Started 5/10 8/10 after Linux recipe and diagnostic copy-paste commands
API/CLI Ergonomics 6/10 8/10 with explicit conflict rejection and no ambient env var
Error Quality 6/10 9/10 with diagnostic matrix and docs hints
Documentation 5/10 8/10 with package README, CLI README, release note, package index
Escape Hatches 6/10 8/10 with safest-first ordering
Upgrade Path 5/10 8/10 with compatibility note
Measurement 4/10 7/10 with docs contract and targeted tests
Overall DX 5.4/10 8.0/10

DX implementation checklist:

  • Add Linux nonstandard secret-tool copy-paste recipe with SECRET_TOOL=/absolute/path/to/secret-tool.
  • Make diagnostics include checked paths, ignored PATH candidate when present, exact CLI rerun, options snippet, docs hint, and retryability.
  • Reject --store-file plus --secret-tool-path.
  • Add CLI README coverage, not just package README coverage.
  • Add release note explaining this is a security hardening and how nonstandard Linux installs recover.

DX consensus:

Dimension Subagent Codex Consensus
Getting started < 5 min? Not unless recipe is added. Advisory voice also wanted migration diagnostics. Confirmed.
API/CLI naming guessable? Explicit flag ok, but easy to forget. CLI surface possibly inflated. Taste decision.
Error messages actionable? Diagnostic matrix needed. Same. Confirmed.
Docs findable and complete? CLI README missing. Same migration concern. Confirmed.
Upgrade path safe? Needs release note. Same. Confirmed.
Dev environment friction-free? Needs no ambient env var and exact rerun. Compatible with trust policy. Confirmed.

Cross-Phase Themes

Theme Phases Decision
This is an external-command trust policy, not just a PATH bug. CEO, Eng, DX Make trust policy explicit in code, docs, and tests.
Diagnostics are the migration path. CEO, Eng, DX Treat diagnostic matrix and docs links as P1.
Compatibility for nonstandard Linux installs must be deliberate. CEO, DX Support explicit absolute override; no PATH fallback or env var.
Live backend risk remains even with deterministic tests. CEO, Eng Add manual/quarantined smoke guidance; do not block deterministic CI on desktop stores.

Decision Audit Trail

# Phase Decision Classification Principle Rationale Rejected
1 CEO Reframe as Linux external-command trust policy Mechanical P1/P5 Fixes the real trust boundary, not just one helper method. Treat as narrow PATH cleanup only
2 CEO Keep explicit CLI flag on all secret subcommands Taste P1/P6 Every platform command can need the same trusted path; central command base limits implementation spread. Doctor-only flag
3 CEO Do not add env var or persistent CLI config Taste P5 Avoids a second ambient executable source after removing PATH search. Add APPSURFACE_SECRET_TOOL_PATH
4 Eng Add filesystem/executable probe seam for resolver tests Mechanical P5 Keeps tests deterministic and avoids reflection or host /usr/bin dependency. Test against real system paths
5 Eng Reject --store-file plus --secret-tool-path Mechanical P1 Prevents false-positive doctor output. Silently prefer --store-file
6 Eng Map command startup failures to LocalSecrets diagnostics Mechanical P1 Preserves the result contract for invalid override paths. Let Process.Start() throw
7 DX Add Linux nonstandard install recipe and diagnostic matrix Mechanical P1 Makes the stricter security posture recoverable in under 5 minutes. Generic platform matrix note
8 DX Document escape hatches safest-first Mechanical P1/P5 Prevents users from weakening fail-closed posture to work around Linux path friction. Document only the new option

Implementation Tasks

  • AP-LS-01 (P1, human: 0.5 day / CC: 45 min) - LocalSecrets resolver - Replace PATH search with a trusted Linux resolver, explicit override validation, ignored-PATH diagnostics, and deterministic probe seams.
    • Files: Config/ForgeTrust.AppSurface.Config.LocalSecrets/PlatformAppSurfaceLocalSecretStore.cs, Config/ForgeTrust.AppSurface.Config.LocalSecrets.Tests/PlatformAppSurfaceLocalSecretStoreTests.cs
  • AP-LS-02 (P1, human: 0.25 day / CC: 25 min) - Command runner diagnostics - Map process startup failures to Unavailable diagnostics and test non-executable/bad executable paths.
    • Files: Config/ForgeTrust.AppSurface.Config.LocalSecrets/PlatformAppSurfaceLocalSecretStore.cs, Config/ForgeTrust.AppSurface.Config.LocalSecrets.Tests/PlatformAppSurfaceLocalSecretStoreTests.cs
  • AP-LS-03 (P1, human: 0.5 day / CC: 40 min) - CLI override contract - Add --secret-tool-path, reject it with --store-file, and test doctor plus one key command.
    • Files: Cli/ForgeTrust.AppSurface.Cli/SecretsCommand.cs, Cli/ForgeTrust.AppSurface.Cli.Tests/ProgramEntryPointTests.cs
  • AP-LS-04 (P1, human: 0.5 day / CC: 45 min) - Options and DI - Add AppSurfaceLocalSecretsOptions.LinuxSecretToolPath, XML docs, options-aware platform store construction, and registration tests.
    • Files: Config/ForgeTrust.AppSurface.Config.LocalSecrets/AppSurfaceLocalSecretsOptions.cs, Config/ForgeTrust.AppSurface.Config.LocalSecrets/AppSurfaceLocalSecretsModule.cs, Config/ForgeTrust.AppSurface.Config.LocalSecrets.Tests/LocalSecretsRegistrationTests.cs
  • AP-LS-05 (P1, human: 0.5 day / CC: 45 min) - Docs and release note - Add trust policy, Linux recipe, safest-first escape hatches, CLI README coverage, package index references, and release note.
    • Files: Config/ForgeTrust.AppSurface.Config.LocalSecrets/README.md, Cli/ForgeTrust.AppSurface.Cli/README.md, packages/README.md, packages/package-index.yml, releases/unreleased.md
  • AP-LS-06 (P2, human: 0.25 day / CC: 20 min) - Live smoke guidance - Add a manual/quarantined Linux smoke checklist for DBus/Secret Service behavior without blocking deterministic CI.
    • Files: Config/ForgeTrust.AppSurface.Config.LocalSecrets/README.md, eng/plans/local-secrets-linux-secret-tool-resolution.md

GSTACK REVIEW REPORT

Review Skill Focus Runs Status Findings
CEO Review /plan-ceo-review via /autoplan Strategy, scope, trust policy 1 clean 3 plan refinements implemented
Design Review /plan-design-review via /autoplan UI/visual design 0 skipped No UI scope
Eng Review /plan-eng-review via /autoplan Architecture, tests, failure modes 1 clean 5 engineering findings implemented
DX Review /plan-devex-review via /autoplan CLI/package docs and TTHW 1 clean 5 DX findings implemented

Implementation status: completed. Remaining taste choices are intentional: keep the explicit CLI flag on every subcommand, and do not add an environment variable in this issue.