diff --git a/docs/guides/getting-started.md b/docs/guides/getting-started.md index 1c84b0c01a..619d582d10 100644 --- a/docs/guides/getting-started.md +++ b/docs/guides/getting-started.md @@ -1212,6 +1212,7 @@ new-project-prompt generate a Codex prompt for project connection demo create a disposable local demo goal doctor diagnose installation and import health update [check|plan|apply] inspect or apply through the active install owner +upgrade upgrade the CLI and the installed macOS App in one command registry inspect registered goals registry-boundary classify registry local/public boundary and push policy status show first-screen operator status diff --git a/loopx/help_surface.py b/loopx/help_surface.py index 55abccdf52..420603d52f 100644 --- a/loopx/help_surface.py +++ b/loopx/help_surface.py @@ -237,8 +237,11 @@ def register_command_reference(subparsers: argparse._SubParsersAction) -> None: }, {"command": "loopx upgrade-plan", "purpose": "Plan default heartbeat upgrade propagation."}, { - "command": "loopx update [check|plan|apply]", - "purpose": "Inspect installation ownership, then explicitly apply archive updates.", + "command": "loopx update [check|plan|apply] / loopx upgrade", + "purpose": ( + "Inspect installation ownership, then explicitly apply archive updates, " + "or run one command that upgrades the CLI and the installed macOS App." + ), }, { "command": "loopx project-skill --help", diff --git a/man/loopx.1 b/man/loopx.1 index 36ca34ba52..773b618e3c 100644 --- a/man/loopx.1 +++ b/man/loopx.1 @@ -185,8 +185,8 @@ Preview, append, or read supervisor proposals and host execution receipts. \fBloopx upgrade\-plan\fR Plan default heartbeat upgrade propagation. .TP -\fBloopx update [check|plan|apply]\fR -Inspect installation ownership, then explicitly apply archive updates. +\fBloopx update [check|plan|apply] / loopx upgrade\fR +Inspect installation ownership, then explicitly apply archive updates, or run one command that upgrades the CLI and the installed macOS App. .TP \fBloopx project\-skill \-\-help\fR Install, inspect, or remove release\-owned skills in selected project agent hosts. diff --git a/tests/test_cli_manual_visibility.py b/tests/test_cli_manual_visibility.py new file mode 100644 index 0000000000..ca17dbe547 --- /dev/null +++ b/tests/test_cli_manual_visibility.py @@ -0,0 +1,54 @@ +"""Keep the manual-visibility decision for every top-level CLI command on PRs. + +The release preflight smoke (examples/cli-help-manpage-smoke.py) enforces the +same rule across the installed manual, but it only runs in the daily +full-public fleet. A command or alias added without a catalog decision +therefore stayed invisible until that fleet turned red, so the pure rule and +the checked-in rendered manual are guarded here as well. +""" +from __future__ import annotations + +import argparse +from pathlib import Path + +from loopx.cli import build_parser +from loopx.help_surface import ( + MANPAGE_COMMAND_HELP_ONLY, + manpage_top_level_commands, + render_manpage, +) + + +REPO_ROOT = Path(__file__).resolve().parents[1] +MANPAGE = REPO_ROOT / "man" / "loopx.1" + + +def _top_level_commands() -> frozenset[str]: + subparsers = next( + action + for action in build_parser()._actions + if isinstance(action, argparse._SubParsersAction) + ) + return frozenset(subparsers.choices) + + +def test_every_top_level_command_declares_manual_visibility() -> None: + commands = _top_level_commands() + documented = manpage_top_level_commands() + help_only = MANPAGE_COMMAND_HELP_ONLY + assert documented.isdisjoint(help_only) + assert commands == documented | help_only, { + # Classify each reported command in loopx/help_surface.py: add it to + # COMMAND_GROUPS when the manual should teach it, or to + # MANPAGE_COMMAND_HELP_ONLY when its command help owns the guidance. + "unclassified": sorted(commands - documented - help_only), + "stale_manual": sorted(documented - commands), + "stale_help_only": sorted(help_only - commands), + } + + +def test_checked_in_manual_matches_the_canonical_catalog() -> None: + assert MANPAGE.read_text(encoding="utf-8") == render_manpage(), ( + "regenerate the checked-in manual after a catalog change: " + "uv run --extra test python scripts/render-manpage.py --output man/loopx.1" + )