Skip to content

feat(cli): promote 'marketplace doctor' to top-level 'apm doctor' (+ workflow discoverability)#1539

Merged
danielmeppiel merged 2 commits into
mainfrom
danielmeppiel/turbo-waddle
May 29, 2026
Merged

feat(cli): promote 'marketplace doctor' to top-level 'apm doctor' (+ workflow discoverability)#1539
danielmeppiel merged 2 commits into
mainfrom
danielmeppiel/turbo-waddle

Conversation

@danielmeppiel

Copy link
Copy Markdown
Collaborator

TL;DR

Partial fix for #1537. Promotes the diagnostics verb to a top-level command, adds discoverability hooks around the existing lifecycle, and writes a Rosetta Stone guide that maps npm/pnpm/uv/cargo/brew vocabulary to existing APM verbs. Declines the renames that would create alias debt; defers apm status until a concrete shape emerges.

Decision (three-tier disposition of #1537)

Ask Disposition Why
apm sync Decline Exact duplicate of apm install --frozen, which is the same idiom contributors learn from npm ci / pnpm install --frozen-lockfile / uv sync --frozen. Adding sync makes two names for one thing.
apm sync --update Decline Already spelled apm update.
apm check Decline Already spelled apm audit --ci (the CI gate). The name check also collides with the existing apm marketplace check.
apm status Defer Likely covered by apm deps tree + apm outdated + apm targets. Will ship if a concrete status output emerges that isn't covered by composing those.
apm doctor Ship (this PR) Genuine gap. Diagnostics belonged at the top level, not buried under marketplace.

Full reasoning in the issue reply (four-panel synthesis: devx-pro, devx-con, oss-growth, apm-ceo).

What ships

  1. apm doctor — top-level command, thin wrapper around the existing 7-check diagnostic suite (git on PATH, github.com reachability, AuthResolver token, gh CLI, marketplace config, format coverage, duplicate names, version alignment).
  2. Legacy aliasapm marketplace doctor still works but is hidden from marketplace --help and prints [!] 'apm marketplace doctor' is deprecated; use 'apm doctor' instead. before delegating. Zero behavior regression for existing scripts/CI.
  3. apm --help "Common workflows" epilog — surfaces the lifecycle (init -> install -> outdated -> update -> doctor) and the CI idiom (apm install --frozen && apm audit --ci) from the root help.
  4. Contextual error tipsAuthenticationError now points to apm doctor; FrozenInstallError points to apm outdated + apm update. Five touch sites in install.py (x2) and update.py (x2 + reused), one _rich_info line each.
  5. Rosetta Stone guidedocs/src/content/docs/guides/operating-installed-context.md maps "I want to..." questions to existing verbs including the under-discovered apm deps why, apm deps tree, apm install --frozen, and apm audit --ci.

What deliberately does not ship

  • apm status — deferred until a concrete output is articulated. Mentioned as a follow-up in the issue reply.
  • Broadened doctor checks (lockfile drift, cache state, runtime probe) — scope-isolated to keep this PR reviewable. The run_doctor() callable in marketplace/doctor.py is the seam for follow-up PRs to add domains incrementally.

Validation evidence

Lint mirror (canonical contract):

$ uv run --extra dev ruff check src/ tests/                  # All checks passed!
$ uv run --extra dev ruff format --check src/ tests/         # 1147 files already formatted
$ uv run --extra dev python -m pylint --disable=all --enable=R0801 \
    --min-similarity-lines=10 --fail-on=R0801 src/apm_cli/   # 10.00/10
$ bash scripts/lint-auth-signals.sh                          # [+] auth-signal lint clean

Tests: 15790 passed, 1 skipped, 21 xfailed (full tests/unit run, ~2:30). Six new tests in tests/unit/commands/test_doctor.py cover registration, root-help discovery, the workflows epilog, marketplace-help hiding, deprecation hint, and a smoke run of diagnostics.

The commands/install.py arch-invariant budget is raised 2010 -> 2012 with a justification comment matching the lockfile-UX precedent (#1203). Two lines added are pure Click-handler glue (_rich_info per except branch); no new logic.

How to test

# Discoverability
apm --help                       # see 'Common workflows' epilog and 'doctor' in commands
apm doctor --help                # top-level command help

# Functional
apm doctor                       # runs the 8 checks; exits 0 when healthy
apm doctor -v                    # verbose

# Backwards compatibility
apm marketplace --help           # 'doctor' no longer listed
apm marketplace doctor           # still works, prints deprecation hint to stderr

# Error hints
# Trigger a frozen-install drift or an auth failure and confirm the
# 'Tip:' line appears after the error context.

Touched files

  • src/apm_cli/commands/doctor.py (new) — top-level Click wrapper
  • src/apm_cli/commands/marketplace/doctor.py — extracted run_doctor() callable; legacy command is now a hidden deprecated alias
  • src/apm_cli/commands/marketplace/__init__.py — removed "doctor" from MarketplaceGroup._authoring_commands
  • src/apm_cli/cli.py — wired top-level command + added _CLI_EPILOG
  • src/apm_cli/commands/install.py — two error-hint nudges
  • src/apm_cli/commands/update.py — two error-hint nudges
  • tests/unit/commands/test_doctor.py (new) — 6 tests
  • tests/unit/install/test_architecture_invariants.py — budget 2010 -> 2012 with justification
  • docs/src/content/docs/guides/operating-installed-context.md (new) — Rosetta Stone

Refs #1537 (partial; defer apm status follow-up)

)

Closes one of the four asks in #1537 by promoting the diagnostics
verb to a top-level command and adding small discoverability hooks
around the existing lifecycle commands.

Tier 1 (this PR):
* Promote 'apm marketplace doctor' to 'apm doctor'. The legacy
  invocation keeps working as a hidden deprecated alias that prints
  a one-line migration hint before delegating.
* Add a 'Common workflows' epilog to 'apm --help' so init -> install
  -> outdated -> update -> doctor and the 'install --frozen && audit
  --ci' CI idiom are discoverable from the root help.
* Add contextual error tips: AuthenticationError now points to
  'apm doctor'; FrozenInstallError points to 'apm outdated' +
  'apm update'. Five touch sites, one-line nudges each.
* New Rosetta Stone guide 'Operating installed context' mapping
  npm/pnpm/uv/cargo/brew vocabulary to existing apm verbs (deps why,
  deps tree, outdated, install --frozen, audit --ci, doctor).

Tier 2 deferred: 'apm status' summary view -- waits for a concrete
shape that isn't already covered by deps tree + outdated + targets.

Tier 3 declined: 'apm sync', 'apm sync --update', and 'apm check'
are exact duplicates of 'install --frozen', 'update', and 'audit
--ci' respectively (the 'check' name also collides with 'apm
marketplace check'). Documented in the issue reply.

The arch-invariant budget for commands/install.py is raised
2010 -> 2012 with justification matching the lockfile-UX precedent.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 28, 2026 20:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Promotes the existing marketplace diagnostics flow into a discoverable top-level CLI command (apm doctor), while keeping apm marketplace doctor as a hidden, deprecated alias. This supports the installed-context workflow requested in #1537 and adds user-facing discoverability via root help text, targeted error tips, and a new docs guide.

Changes:

  • Added top-level apm doctor command that delegates to shared run_doctor() implementation.
  • Hid apm marketplace doctor from marketplace help, but preserved it as a deprecated alias with a migration hint.
  • Added root apm --help “Common workflows” epilog, contextual tips in install/update error handlers, new unit tests, and a new docs “Rosetta Stone” guide.
Show a summary per file
File Description
tests/unit/install/test_architecture_invariants.py Updates install.py LOC budget and justification text.
tests/unit/commands/test_doctor.py Adds unit tests for command registration, help discoverability, alias behavior, and a diagnostics smoke run.
src/apm_cli/commands/update.py Adds “Tip:” nudges for FrozenInstallError and AuthenticationError.
src/apm_cli/commands/marketplace/doctor.py Extracts run_doctor() and converts legacy marketplace command into a hidden deprecated alias.
src/apm_cli/commands/marketplace/init.py Removes doctor from marketplace authoring command listings.
src/apm_cli/commands/install.py Adds “Tip:” nudges for auth and frozen-install drift paths.
src/apm_cli/commands/doctor.py New top-level Click wrapper around run_doctor().
src/apm_cli/cli.py Registers doctor at top level and adds a “Common workflows” epilog to root help.
docs/src/content/docs/guides/operating-installed-context.md New guide mapping common “installed context” questions to existing APM commands.

Copilot's findings

  • Files reviewed: 9/9 changed files
  • Comments generated: 7

assert n <= 2012, (
f"commands/install.py grew to {n} LOC (budget 2012). "
"Do NOT trim cosmetically -- engage the python-architecture skill "
"(.github/skills/python-architecture/SKILL.md) and propose an "

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed in 8c2e073. The path now points to .apm/skills/python-architecture/SKILL.md (the source of truth; .agents/ is the compiled mirror).

Comment thread tests/unit/commands/test_doctor.py Outdated
Comment on lines +74 to +76
# Deprecation hint is written to stderr; CliRunner merges streams by
# default in Click >= 8.2, so it shows up in result.output.
combined = result.output

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made the assertion stream-agnostic (combines stdout + stderr) in 8c2e073. The test was passing on Click 8.2.1 but only by luck; this version no longer depends on the merge default.

Comment thread src/apm_cli/cli.py Outdated
Comment on lines +47 to +56
_CLI_EPILOG = """\b
Common workflows:
apm init Scaffold a new project
apm install Install dependencies from apm.yml
apm install --frozen Reproduce lockfile exactly (CI-safe)
apm outdated See what's drifted from upstream
apm update Refresh refs and rewrite the lockfile
apm audit --ci Validate lockfile integrity for CI gates
apm doctor Diagnose environment problems
apm run <script> Execute a script from apm.yml

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The '\b' here is a documented Click formatting directive that disables Click's epilog rewrapping (see the same pattern in src/apm_cli/commands/view.py lines 422 and 425). Without it, Click flows the eight workflow lines into one paragraph. I reformatted as concatenated strings (8c2e073) so the directive is more idiomatic, but kept the '\b' since dropping it visibly breaks the table layout. Treating it as a CLI rendering directive rather than user-facing output, matching the repo precedent.

Comment thread src/apm_cli/commands/install.py Outdated
_rich_error(str(e))
if e.diagnostic_context:
_rich_echo(e.diagnostic_context)
_rich_info("Tip: run 'apm doctor' to diagnose auth and connectivity.")

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added symbol='info' in 8c2e073.

Comment thread src/apm_cli/commands/install.py Outdated
Comment on lines +1754 to +1761
_rich_info("Tip: run 'apm doctor' to diagnose auth and connectivity.")
sys.exit(1)
except FrozenInstallError as e:
_maybe_rollback_manifest(ctx.snapshot_manifest_path, ctx.manifest_snapshot, logger)
_rich_error(str(e))
for reason in e.reasons:
_rich_echo(reason)
_rich_info("Tip: run 'apm outdated' to see what changed, then 'apm update'.")

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added symbol='info' in 8c2e073.

Comment on lines 321 to 331
_rich_error(str(e))
for reason in e.reasons:
_rich_echo(reason)
_rich_info("Tip: run 'apm outdated' to see what changed, then 'apm update'.")
sys.exit(1)
except AuthenticationError as e:
_rich_error(str(e))
if e.diagnostic_context:
_rich_echo(e.diagnostic_context)
_rich_info("Tip: run 'apm doctor' to diagnose auth and connectivity.")
sys.exit(1)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added symbol='info' in 8c2e073.

| Diagnose a broken environment | `apm doctor` | Aggregated pass/fail table: git, network, auth, gh CLI, and (if present) marketplace config. |
| Inspect the cache | `apm cache info` | Disk usage and location. `apm cache clean` removes everything; `apm cache prune --days N` is incremental. |
| Inspect resolved runtimes | `apm runtime status` | Active runtime and preference order. |
| Inspect resolved targets | `apm targets --all` | Which harnesses APM will deploy to. |

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in 8c2e073 — row now says 'apm targets' and mentions '--json --all' for the meta-target.

@danielmeppiel danielmeppiel added the panel-review Trigger the apm-review-panel gh-aw workflow label May 29, 2026
Folds 7 inline review comments:

* tests/unit/install/test_architecture_invariants.py: correct skill
  path .github -> .apm/skills/python-architecture/SKILL.md (the
  pre-existing message pointed to a non-existent location).
* tests/unit/commands/test_doctor.py: the deprecation-hint assertion
  now checks both stdout and stderr so it does not depend on Click
  8.2's stream-separation default.
* src/apm_cli/cli.py: reformat _CLI_EPILOG so the per-line layout
  survives Click's epilog rewrapping. Keeps the documented Click
  '\b' formatting marker (already used in src/apm_cli/commands/view.py
  lines 422 and 425) which is a CLI rendering directive, not
  user-facing output.
* src/apm_cli/commands/install.py, src/apm_cli/commands/update.py:
  pass symbol='info' on the new _rich_info() tip lines so they render
  with the standard [i] prefix and match neighbouring usage.
* docs/src/content/docs/guides/operating-installed-context.md:
  document 'apm targets' as the default invocation; mention
  '--json --all' for the agent-skills meta-target.

Arch budget for install.py raised 2012 -> 2014 to absorb ruff's
multi-line reformat of the now-keyword-arg _rich_info() call.
Justification kept inline.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@danielmeppiel danielmeppiel merged commit e62c1ed into main May 29, 2026
64 checks passed
@danielmeppiel danielmeppiel deleted the danielmeppiel/turbo-waddle branch May 29, 2026 09:06
danielmeppiel added a commit that referenced this pull request Jun 1, 2026
* fix(tests): skip POSIX execute-bit assertions on Windows

Two unit tests asserted st_mode & 0o111 == 0o111, which fails on
Windows because NTFS does not honor POSIX execute bits. Guard both
with pytest.mark.skipif(sys.platform == 'win32'), matching the
existing convention used elsewhere in the suite.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(scripts): guard empty PYTEST_EXTRA_ARGS array under bash 3.2

scripts/test-integration.sh runs under `set -euo pipefail`. On macOS
the default /bin/bash is 3.2, where expanding an empty array with a
bare "${arr[@]}" raises an unbound-variable error. Local integration
runs (PYTEST_EXTRA_ARGS unset) aborted before pytest with
'extra_args[@]: unbound variable'. Use the ${arr[@]+"${arr[@]}"}
guard so the empty-array expansion is safe; CI behaviour (with
PYTEST_EXTRA_ARGS set) is unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: release v0.16.1

Roll the [Unreleased] changelog into a dated 0.16.1 block and bump
pyproject.toml + uv.lock. Adds the previously-missing user-facing
entries for #1539 (apm doctor), #1566, #1569, #1567, #1553, #1552,
and #1538 surfaced by enumerating merged PRs since v0.16.0.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* ci: parallelise macOS release integration tests to fix 20min timeout

The v0.16.1 release pipeline failed on the macOS x86_64 (Intel) build:
the consolidated job's "Run integration tests" step hit its 20-minute
timeout at ~61% progress. The tests were passing -- the slower Intel
runner simply could not finish the full suite serially in time, and the
arm64 runner was also near the edge.

Unlike ci-integration.yml, which shards the suite across four runners,
the release workflow runs the whole integration suite on a single
scarce macOS runner. Parallelise it in-process with xdist (-n 2,
matching ci-integration's per-shard width to bound shared-PAT API
concurrency) using --dist loadgroup so the home_env xdist_group markers
keep HOME-mutating tests serialized on one worker. Also raise the step
timeout to 30 minutes for headroom on the slow Intel runner.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: danielmeppiel <danielmeppiel@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

panel-review Trigger the apm-review-panel gh-aw workflow

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants