Skip to content

perf(cli): route command aliases through the help fast path - #1641

Open
thymikee wants to merge 2 commits into
mainfrom
perf/cli-alias-help-fast-path
Open

perf(cli): route command aliases through the help fast path#1641
thymikee wants to merge 2 commits into
mainfrom
perf/cli-alias-help-fast-path

Conversation

@thymikee

@thymikee thymikee commented Aug 6, 2026

Copy link
Copy Markdown
Member

Summary

agent-device <command> --help has a fast path in src/bin.ts that prints static help without booting the full CLI. It resolved aliases through a hand-written two-entry table while the real registry (src/commands/cli-command-aliases.ts) has five, so tap, launch, and relaunch silently missed it and fell through to a full runCli() bootstrap just to print static text.

It now delegates to normalizeCliCommandAlias, so every alias the registry knows about gets the fast path automatically.

Measured on the built CLI, 3 runs each, warmed:

--help before after
press (control) 49 ms 49 ms
long-press (control, was in the old table) 43 ms 49 ms
tap 156 ms 49 ms
launch 157 ms 50 ms
relaunch 156 ms 53 ms

long-press vs tap is the controlled comparison: both are aliases printing identical help, differing only by whether the stale table knew about them. Output is byte-identical to each alias's canonical command (diff clean on all four pairs).

rotate deliberately still misses the fast path, so its rename migration error keeps rendering through the slow path. No carve-out was added to bin.ts for it — it simply isn't in the alias registry.

Validation

pnpm check:affected --run green. The size report on the batch PR this was split from showed CLI --version 29.4 → 28.4 ms, confirming the one static import added to bin.ts costs nothing measurable — tsdown inlines the alias table into bin.js rather than emitting a chunk.

On the regression test, one thing a reviewer should know. bin.ts runs unguarded top-level dispatch on import and is excluded from coverage by design, so it cannot be imported in a unit test. The committed tests pin the registry composition bin.ts calls, which makes them a durable guard against a future sixth alias lacking help text — but they are not a revert-pin on this specific bug, and reverting bin.ts does not fail them. The test file header says so explicitly rather than overclaiming.

The genuine red/green proof was done out-of-suite via the module graph, since output is identical on both paths and only the loaded modules differ:

# stale table, rebuilt dist:
NODE_V8_COVERAGE=<dir> node bin/agent-device.mjs tap --help
grep 'dist/src/cli\.js' <dir>/*.json   → MATCH      (slow path taken)
NODE_V8_COVERAGE=<dir> node bin/agent-device.mjs press --help
grep 'dist/src/cli\.js' <dir>/*.json   → no match   (control stayed fast)

# after the fix:
NODE_V8_COVERAGE=<dir> node bin/agent-device.mjs tap --help
grep 'dist/src/cli\.js' <dir>/*.json   → no match   (fast path confirmed)

This was not added as a committed test: spawning real node subprocesses in unit-core would violate the suite's "unit tests must not wait real time" budget and has no precedent in the repo.

Scope

2 files, +69/−7. Not device-facing, so no simulator/emulator evidence applies.

Split out of #1639 per review. Found by a read-only codebase audit.

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
JS raw 1.99 MB 1.99 MB -18 B
JS gzip 635.8 kB 635.8 kB +20 B
npm tarball 769.2 kB 769.2 kB -9 B
npm unpacked 2.69 MB 2.69 MB +27 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 27.6 ms 27.9 ms +0.2 ms
CLI --help 66.9 ms 66.7 ms -0.2 ms

Top changed chunks:

Chunk Raw diff Gzip diff
dist/src/cli.js -293 B -85 B

@thymikee

thymikee commented Aug 6, 2026

Copy link
Copy Markdown
Member Author

Reviewed exact head 9418f11. The implementation correctly routes help aliases through the shared registry, but one P2 regression-test gap remains: src/cli/parser/__tests__/cli-help-alias-fast-path.test.ts never exercises src/bin.ts, so reverting src/bin.ts:59 to the stale two-entry table leaves all three new tests green. Per the repository’s red-before-green rule, add a deterministic bin-entrypoint or structural guard that pins this production call and demonstrate it failing on the prior code. The out-of-suite module-graph proof establishes the current behavior, but it does not prevent recurrence. Deterministic CI is otherwise green. iOS Smoke is red on the same unrelated testBareTypeUsesTappedInputWhenSoftwareKeyboardIsHidden failure reproduced on #1640/main; that is separate owner-action CI, but still blocks readiness until resolved or rerun green.

@thymikee
thymikee force-pushed the perf/cli-alias-help-fast-path branch from 9418f11 to 842469b Compare August 6, 2026 13:48
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://callstack.github.io/agent-device/pr-preview/pr-1641/

Built to branch gh-pages at 2026-08-06 14:16 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

bin.ts's `--help` fast path resolved aliases through a hand-written
two-entry table that had drifted out of sync with the real
CLI_COMMAND_ALIASES registry (five entries). `tap`, `launch`, and
`relaunch` missed the table and silently fell through to a full
runCli() bootstrap just to print static help text (~150-165ms vs
~45-50ms for aliases already in the table).

Delegate to the shared normalizeCliCommandAlias registry instead of
the stale local table, so every alias the registry knows about gets
the fast path automatically.
The unit test added for the alias fast-path fix (cli-help-alias-fast-path.test.ts)
calls normalizeCliCommandAlias directly, so it stays green even if bin.ts
itself reverts to a hand-rolled table — it pins the registry composition,
not bin.ts's own wiring, and bin.ts cannot be safely unit-imported (it runs
unguarded top-level dispatch on import and is deliberately excluded from
coverage).

Add an AST-based structural guard instead, in the style already established
by scripts/layering/session-state.ts, facade-exports.ts, and zero-dep-jobs.ts
(oxc-parser's module/program records, not a line scan, so a fixture's string
literal can't produce a false hit). R12 asserts two facts about src/bin.ts:
it holds a value import of normalizeCliCommandAlias from
commands/cli-command-aliases.ts, and it contains none of the registry's own
alias tokens as string literals. The token list is read out of the
registry's own source (CLI_COMMAND_ALIASES's `alias:` property values), not
hard-coded, so a future sixth alias is covered automatically. Both facts
were false on the pre-fix bin.ts, verified by reverting locally and
capturing the failure before restoring the fix.

Wired into the existing check:layering chain (already part of
check:tooling), next to R7's session-state ownership rule, which pins the
same "delegate to your single owner" shape.
@thymikee
thymikee force-pushed the perf/cli-alias-help-fast-path branch from 842469b to 7dc6f0f Compare August 6, 2026 14:10
@thymikee

thymikee commented Aug 6, 2026

Copy link
Copy Markdown
Member Author

Re-reviewed exact head 7dc6f0f1. The production fix is correct, but the new R12 guard still does not pin its wiring:

  • P2 — R12 never proves the resolver is called on the help target. It checks only that normalizeCliCommandAlias is value-imported and that local alias literals are absent. src/bin.ts can regress to buildCommandUsageText(helpTarget) while the import is used harmlessly elsewhere (or as void normalizeCliCommandAlias), and the real-tree gate stays green. Assert via AST that the imported local binding is invoked in the argument feeding buildCommandUsageText, and add a negative fixture with a present-but-unused/unrelated import.

The current PR body is also stale relative to the six-file structural-guard diff. Current head is mergeable and completed checks are green, with required lanes still pending; no ready-for-human label.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant