Skip to content

refactor(cli): let help resolve command aliases itself and retire R12 - #2293

Merged
thymikee merged 8 commits into
mainfrom
claude/audit-r12-help-alias
Sep 5, 2026
Merged

refactor(cli): let help resolve command aliases itself and retire R12#2293
thymikee merged 8 commits into
mainfrom
claude/audit-r12-help-alias

Conversation

@thymikee

@thymikee thymikee commented Sep 5, 2026

Copy link
Copy Markdown
Member

Summary

bin.ts's --help fast path composed buildCommandUsageText(normalizeCliCommandAlias(helpTarget)) inline, which is what let it drift once already (#1641). The composition now lives in cli-help.ts as resolveHelpTargetUsageText, used by both bin.ts and the MCP help tool, and the R12 static checker that only existed to prove the composition was not bypassed is retired.

The process entry itself is now importable: src/cli/entry.ts exports runEntry(argv, modules, io) and bin.ts shrinks to the five lazy import() calls it injects. That is what makes the fast path testable without spawning a process or reading V8 coverage reports.

Before: bin.ts and mcp/server-guide.ts each composed alias normalization by hand, and nothing could import the dispatch. After: both call resolveHelpTargetUsageText, and entry.test.ts drives the dispatch directly.

Validation

Tested commit: 0803173pnpm check:affected --run green locally (scripts/ changed, so the full check set ran).

  • src/cli/entry.test.ts: every registry alias prints its canonical help with only the help module loaded; an unknown topic falls through to the CLI loader; --version, bare usage, mcp, and startup failure each have a case. Seen red: hand-routing long-press/relaunch to the CLI loader failed the alias case.
  • cli-help-alias-fast-path.test.ts: registry-derived alias parity against buildCommandUsageText (independent oracle); rotate resolves to null.
  • test/integration/smoke-cli.test.ts: one registry-derived alias --help byte-identical check against the real bin.ts; --help, --version, -V, no-command unchanged.
  • pnpm check:layering green after retiring R12; eager-closure budgets unchanged.

bin.ts's --help fast path composed
buildCommandUsageText(normalizeCliCommandAlias(helpTarget)) inline, which
let a future edit call buildCommandUsageText raw without anyone noticing
until an alias's help silently dropped back to a full CLI bootstrap (the
regression #1641 fixed). Move the composition into cli-schema/cli-help.ts
as resolveHelpTargetUsageText, so bin.ts just calls one function that owns
its own alias normalization; bin.ts no longer imports the alias registry
at all.

Retargets cli-help-alias-fast-path.test.ts at the new function (same three
cases) and adds a process-level smoke test asserting `tap --help`/`launch
--help` stdout is byte-identical to `press --help`/`open --help`. Seen red
by temporarily removing the `tap` alias from CLI_COMMAND_ALIASES (both
fast and slow paths lose the alias, producing an "Unknown command: tap"
mismatch); green again after restoring it.

Verified manually: `node --experimental-strip-types src/bin.ts tap --help`
stays byte-identical to `press --help`, and `launch --help` to `open
--help`; `rotate --help` still falls through to the retired-command error.
bin.ts can no longer compose buildCommandUsageText and
normalizeCliCommandAlias incorrectly because it doesn't hold either import
any more — resolveHelpTargetUsageText in cli-schema/cli-help.ts is the only
call site, and cli-help-alias-fast-path.test.ts plus the new smoke-cli
process test pin it. The static R12 checker existed only to prove that
composition from source text; delete it along with its rule wiring in
check.ts (rule function, import, LAYERING_RULE_IDS/LAYERING_RULES entries,
header comment, summary string).

Drops scripts/layering/bin-alias-fast-path.ts (352 lines) and its test
(311 lines). Updates the two stale references left behind:
record-runtime-mechanics-policy.ts's comparison to R12's "delegate to your
single owner" shape, and check-wiring.test.ts's header, which named
bin-alias-fast-path.test.ts as the seam it protects.

rule-ids.ts discovers rule ids by scanning source text rather than a
hand-maintained list, so no entry there needed updating.

Verified: pnpm check:layering green (175/175), including
check-wiring.test.ts and rule-ids.test.ts; pnpm check:quick (lint +
typecheck) clean; scripts/__tests__/eager-closure-budgets.test.ts
(418/418) unaffected, since neither bin.ts nor cli-help.ts sits in any
HUB_ENTRY_FILES or facade closure — both files reach cli-help.ts only
through a dynamic import.
The byte-identical stdout test cannot fail when the fast path is bypassed:
src/cli.ts's slow path resolves the same alias and writes the identical
string, so a reintroduced hand-written table in bin.ts (the exact shape of
#1641) would still pass it. Add a second process-level test that runs
`tap`/`launch --help` and `rotate --help` with NODE_V8_COVERAGE set and reads
the subprocess's own coverage report for src/cli/process-entry.ts, the one
module runCli's slow path loads and the fast path never does.

Seen red: forcing the fast path to always fall through to runCli (simulating
the reintroduced-table bug) failed this test (bootstrappedFullCli true where
false was expected) while the byte-identical test stayed green; reverted and
confirmed both green.
The canonical side of "alias help output matches its canonical command" also
called resolveHelpTargetUsageText, so the assertion became self-consistency:
a degenerate normalizer that maps every input to one canonical command would
make aliasHelp and canonicalHelp equal for every case. Compare
resolveHelpTargetUsageText(alias) against buildCommandUsageText(canonical)
(no alias normalization on the canonical side) instead, restoring the
original two-source oracle.

Seen red: pointing resolveHelpTargetUsageText at a degenerate
`return buildCommandUsageText('press')` failed this test
("launch --help" no longer byte-identical to "open --help"); reverted and
confirmed green.
server-guide.ts's help tool composed
buildCommandUsageText(normalizeCliCommandAlias(topic)) inline, the same
composition bin.ts held before this PR moved it into cli-help.ts. That left
a second hand-written call site the R12 gate's own kill criterion said had
to be gone before retirement was moot. Call resolveHelpTargetUsageText(topic)
instead; behavior is unchanged (manually confirmed tap/press and rotate
topics still match) since it's the same composition, and no closure/layering
change since server-guide.ts already imports cli-help.ts statically.
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
JS raw 2.61 MB 2.61 MB +285 B
JS gzip 878.8 kB 878.9 kB +177 B
npm bundled raw 2.61 MB 2.61 MB +285 B
npm bundled gzip 878.8 kB 878.9 kB +177 B
npm tarball 1.04 MB 1.04 MB +195 B
npm unpacked 3.52 MB 3.52 MB +285 B
npm clean-installed 3.52 MB 3.52 MB +285 B

npm unpacked components

Component Base Current Diff
JS / dist source 2.77 MB 2.77 MB +285 B
Apple runner source/project 544.4 kB 544.4 kB 0 B
Apple snapshot presentation source 33.8 kB 33.8 kB 0 B
Apple Simulator snapshot bridge source 30.5 kB 30.5 kB 0 B
macOS helper source 54.8 kB 54.8 kB 0 B
Android helper artifacts 43.3 kB 43.3 kB 0 B
Other package files 46.7 kB 46.7 kB 0 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 29.6 ms 27.9 ms -1.7 ms
CLI --help 80.9 ms 80.5 ms -0.4 ms

Top changed chunks:

Chunk Raw diff Gzip diff
dist/src/cli-help.js +107 B +36 B
dist/src/session2.js 0 B -1 B
dist/src/daemon-client-lifecycle.js 0 B -1 B
dist/src/runner-client.js 0 B -1 B

Top changed packed files

Packed file Base Current Diff
dist/src/cli-command-aliases.js 0 B 553 B +553 B
dist/src/internal/bin.js 1.9 kB 1.5 kB -473 B
dist/src/cli-help.js 92.6 kB 92.7 kB +107 B
dist/src/version.js 720 B 815 B +95 B
dist/src/cli.js 25.5 kB 25.5 kB +7 B
dist/src/server.js 7.9 kB 7.9 kB +6 B
dist/src/config.js 1.3 kB 1.3 kB -5 B
dist/src/connection.js 23.2 kB 23.2 kB -5 B

@thymikee

thymikee commented Sep 5, 2026

Copy link
Copy Markdown
Member Author

One P2 remains at exact head 0b83a38c3b83537117a00f4ce7ad8ed5e24d72e1: retiring R12 leaves the real bin.ts fast path unproven for two existing aliases. The process-level regression exercises only tap and launch, while the registry also contains long-press and relaunch. A future partial hand-written mapping or omitted alias could therefore recreate the silent full-bootstrap fallback while the resolver-only unit tests still pass.

Derive the process cases from the alias registry/catalog (or at minimum cover every current alias), assert each alias bypasses src/cli/process-entry.ts, and keep the canonical-output oracle independent. The resolver ownership move, MCP route, and all 17 applicable CI checks otherwise look clean. Holding ready-for-human until this regression class is fully preserved.

@thymikee

thymikee commented Sep 5, 2026

Copy link
Copy Markdown
Member Author

Addressed at 313574982031a289d84dc9993d912521760fff12: the process-level smoke test now derives its alias list from the registry (cliAliasesForCommand over listCliCommandNames), covers all four current aliases (tap, launch, relaunch, long-press), asserts each bypasses src/cli/process-entry.ts via the NODE_V8_COVERAGE oracle, and keeps the canonical --help oracle as a direct canonical invocation. A non-empty pin lists the four known aliases so an emptied registry fails loudly. Seen red: a hand-written tap/launch-only map failed both loops on long-press. Full pnpm check:affected --run green locally at this head.

@thymikee

thymikee commented Sep 5, 2026

Copy link
Copy Markdown
Member Author

Re-reviewed exact head 313574982031a289d84dc9993d912521760fff12: clean code review; prior P2 resolved. Both process-level checks now cover all registry/catalog-derived aliases, compare to direct canonical invocations, and prove the full bootstrap is bypassed. The alias-set assertion prevents vacuous empty coverage. No new findings in the test-only delta. CI is still running, so full merge readiness remains conditional on its completion.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Sep 5, 2026
bin.ts ran its dispatch at import time, so the only way to prove that an
alias --help never loads the full CLI was to spawn the process under
NODE_V8_COVERAGE and grep the report for process-entry.ts. That oracle
needed a paragraph to justify; the code was wrong, not the comment.

The dispatch now lives in src/cli/entry.ts as runEntry(argv, modules, io),
with the five lazy imports injected by bin.ts. entry.test.ts drives it with
recording loaders and the real help module: every registry alias prints its
canonical help with only the help module loaded, an unknown topic falls
through to the CLI loader, --version, bare usage, mcp, and startup failures
each have one case. The subprocess coverage machinery, the alias table pin,
and the multi-line comments are gone; the smoke test keeps one
registry-derived byte-identical alias --help check against the real bin.ts.

Seen red: hand-routing long-press and relaunch to the CLI loader inside
entry.ts failed "every registered alias prints its canonical help without
loading the CLI"; restored.
@thymikee

thymikee commented Sep 5, 2026

Copy link
Copy Markdown
Member Author

Reworked at 08031734734b6558b2b0125416fc259de11f7ad7 per the comment feedback: the paragraph comments were justifying a workaround (an untestable bin.ts proven via V8 coverage reports), so the code changed instead. The dispatch is now src/cli/entry.ts runEntry(argv, modules, io) with the five lazy imports injected by a 25-line bin.ts; entry.test.ts drives it with recording loaders and the real help module (every registry alias → canonical help with only the help module loaded; unknown topic → CLI loader; version/usage/mcp/failure cases). The coverage oracle, temp dirs, and alias-table pin are deleted; the smoke test keeps one registry-derived byte-identical alias --help check against the real bin.ts. Every added comment is one line. Seen red: hand-routing long-press/relaunch inside entry.ts fails the alias case. Full local gate green at this head.

@thymikee
thymikee merged commit 35362fe into main Sep 5, 2026
18 checks passed
@thymikee
thymikee deleted the claude/audit-r12-help-alias branch September 5, 2026 18:01
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-09-05 18:01 UTC

thymikee added a commit that referenced this pull request Sep 5, 2026
* origin/main:
  perf: bundle runtime dependencies and report full install size (#2310)
  ci: avoid unrelated Apple runner cache invalidation (#2303)
  fix(web): preserve the backend ref so snapshot refs match actionable refs (#2283)
  test(daemon): session-open-url-prewarm through the request seam (#2304)
  test(daemon): session-devices-batch-runtime through the request seam (#2305)
  chore(gates): layering baselines ratchet against merge-base (#2299)
  test(daemon): one typed conformance helper for the daemon runtime suites (#2298)
  chore(layering): derive the contracts export inventory from package.json (#2297)
  perf: bundle tar-stream to reduce install footprint (#2286)
  docs: simplify agent context and resolve conflicting guidance (#2287)
  refactor(cli): let help resolve command aliases itself and retire R12 (#2293)
thymikee added a commit that referenced this pull request Sep 5, 2026
* origin/main:
  perf: bundle runtime dependencies and report full install size (#2310)
  ci: avoid unrelated Apple runner cache invalidation (#2303)
  fix(web): preserve the backend ref so snapshot refs match actionable refs (#2283)
  test(daemon): session-open-url-prewarm through the request seam (#2304)
  test(daemon): session-devices-batch-runtime through the request seam (#2305)
  chore(gates): layering baselines ratchet against merge-base (#2299)
  test(daemon): one typed conformance helper for the daemon runtime suites (#2298)
  chore(layering): derive the contracts export inventory from package.json (#2297)
  perf: bundle tar-stream to reduce install footprint (#2286)
  docs: simplify agent context and resolve conflicting guidance (#2287)
  refactor(cli): let help resolve command aliases itself and retire R12 (#2293)
  refactor(commands): retire the navigation-only type projection (#2294)
  feat(runtime): route managed leases through contained transports (#2285)
  refactor(contracts): build unavailable runtime facts once (#2291)
  refactor(cli): derive the common flag readers from the common-field table (#2292)
  feat(daemon): add managed allocation operation journal (#2284)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-human Valid work that needs human implementation, judgment, or maintainer merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant