Skip to content

test(cli): pin help, error, and exit-code contracts ahead of the gunshi migration - #449

Merged
oekazuma merged 1 commit into
mainfrom
test/gunshi-phase0-characterization
Aug 9, 2026
Merged

oekazuma merged 1 commit into
mainfrom
test/gunshi-phase0-characterization

Conversation

@oekazuma

@oekazuma oekazuma commented Aug 9, 2026 •

Copy link
Copy Markdown
Owner

Phase 0 of the gunshi migration plan (#448, docs/superpowers/specs/2026-08-10-gunshi-cli-migration-design.md): record the CLI's user-visible contracts before any gunshi code exists, so migration diffs are judged against pinned behavior. Also pays down audit backlog 2608-TEST-01/03 (bin.ts in-process seam + built-dist gate-flag E2E) — the suite stands on its own merits regardless of the migration.

The seam

bin.ts (139 lines) is now a 14-line entry; the full dispatch lives in a new src/cli.ts as runCli(argv, io?) → { code, exit: 'natural' | 'immediate' }. Two deliberate shapes:

  • A separate module, not an export from bin.ts — bin.ts ends in void main(), so importing a seam from it would execute the CLI against vitest's argv; and an import.meta.url entry guard was rejected because npm/pnpm bin shims are symlinks (a realpath check would silently break the published binary).
  • exit discriminator instead of a bare number — today's code uses three exit mechanisms deliberately (natural drain for docs/explain/help/version to avoid truncating pipe writes; immediate exit for install/ci/argv errors, which may hold prompt/timer handles; flush-then-immediate for the analyzer, whose report is the largest write). Unifying them is not provably safe, so the thin entry reproduces each path's exact mechanism.

Behavior preservation verified two ways: floor-smoke 8/8 unchanged on the branch, and a coordinator byte-comparison of main's rebuilt dist vs this branch's dist across 12 command surfaces (help ×5, version, docs list/show/redirect, explain list/error, flag-guard errors, rules×category conflict) — exit codes, stdout, and stderr all byte-identical.

The pins

  • Help goldens (help-golden.test.ts, 6 snapshots): root/docs/explain/install/ci --help + --version (digits normalized). When gunshi's generated format lands in Phase 2, the snapshot diff IS the review surface.
  • Contract matrix (cli-contract.test.ts, 16 cells / 10 classes): the fix(cli): reject flag-shaped and empty values on string flags #397/cli: --out-file - in its space-separated form writes a file instead of stdout #383 flag-guard class, docs-vs-./docs dispatch, docs show redirect, sub-command error surfaces — bin-level representatives, with resolve-args.test.ts still owning the exhaustive matrix.
  • Gate-flag E2E (scripts/cli-e2e.mjs, Node builtins only, 7 checks): the built bin.js on generated fixtures — clean→0, --fail-on warning→1, --min-health→1, non-project→2, --reporter json stdout parses. Wired into CI's test job after the floor-smoke step (dist already built); the floor-smoke job is untouched.

Two Phase-2 inputs discovered and pinned as-is (reality, not the doc)

  1. ci <unknown-subcommand> prints its help to stdout and exits 2 — contradicting the design doc's "stdout empty on every exit-2 path" invariant. Pinned with a comment; either the code or the doc moves in Phase 2.
  2. Unknown flags are silently ignored (util.parseArgs strict:false passthrough). gunshi will likely reject them — an explicit Phase 2 decision point, now impossible to change by accident.

No changeset — tests, an internal behavior-preserving refactor, and CI wiring only. Full suite 2,549 green.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Improved CLI command dispatch, argument handling, help, version, documentation, explanation, installation, and CI commands.
    • Added consistent exit-code behavior and support for structured JSON output.
    • Improved diagnostics and output handling across CLI operations.
  • Bug Fixes

    • Improved validation for conflicting options, invalid projects, and missing paths.
  • Tests

    • Added comprehensive end-to-end coverage for CLI flags, subcommands, errors, reporting, and help output.

…hi migration

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 9, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The CLI entry point now delegates dispatch to runCli. The change adds injectable install I/O, CLI contract and help snapshots, a built-binary E2E runner, and CI integration.

Changes

CLI dispatch and coverage

Layer / File(s) Summary
Centralized CLI dispatcher
packages/cli/src/cli.ts, packages/cli/src/bin.ts
runCli handles command dispatch, argument resolution, analysis execution, I/O, and exit results. bin.ts applies the returned exit mode.
CLI I/O and contract validation
packages/cli/src/install/cli.ts, packages/cli/test/cli-contract.test.ts, packages/cli/test/help-golden.test.ts
Install diagnostics use injectable I/O. Tests cover parsing, validation, dispatch, diagnostics, exit codes, help, and version output.
Built CLI E2E execution
scripts/cli-e2e.mjs, package.json, .github/workflows/ci.yml
The E2E runner checks the built CLI with temporary fixtures. The package script and CI test job invoke it.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant bin_ts
  participant runCli
  participant subcommand
  participant analyzer
  bin_ts->>runCli: Pass argv and CLI I/O
  alt Subcommand
    runCli->>subcommand: Dispatch docs, explain, install, or ci
    subcommand-->>runCli: Return command result
  else Analysis
    runCli->>analyzer: Run resolved analysis options
    analyzer-->>runCli: Return analysis result
  end
  runCli-->>bin_ts: Return exit code and termination mode
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding CLI contract tests for help, errors, and exit codes before the Gunshi migration.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@oekazuma

oekazuma commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 9, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@oekazuma

oekazuma commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@oekazuma oekazuma closed this Aug 9, 2026
@oekazuma oekazuma reopened this Aug 9, 2026
@coderabbitai

coderabbitai Bot commented Aug 9, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
scripts/cli-e2e.mjs (2)

25-42: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Add a timeout to the child process.

If the built CLI hangs, execFileSync blocks until the 15-minute job timeout. A per-check timeout fails fast and reports the failing check by name. execFileSync sets err.signal to SIGTERM on timeout, so the existing signal assertions produce a clear message.

♻️ Proposed timeout
 function runCli(args, opts = {}) {
   try {
     const stdout = execFileSync(process.execPath, [cliBin, ...args], {
       stdio: ['ignore', 'pipe', 'pipe'],
       encoding: 'utf8',
+      timeout: 60_000,
       ...opts
     });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/cli-e2e.mjs` around lines 25 - 42, Update runCli to pass a
per-process timeout option to execFileSync, while preserving any caller-provided
options through the existing opts spread. Use the timeout value expected by the
checks so hung CLI invocations terminate promptly and continue returning the
resulting status and signal through the existing error handling.

89-148: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Optional: extract the repeated exit-code assertion.

The signal/code assertion pair repeats in six checks. A single helper keeps the messages consistent and shortens each check.

♻️ Sketch of the helper
function expectExit(args, expected, opts) {
  const { code, signal, stdout, stderr } = runCli(args, opts);
  assert.equal(signal, null, `killed by signal ${signal} (stderr: ${stderr})`);
  assert.equal(
    code,
    expected,
    `\`svelte-vitals ${args.join(' ')}\` expected exit ${expected}, got ${code}: ${stderr}`
  );
  return { stdout, stderr };
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/cli-e2e.mjs` around lines 89 - 148, Optionally add an expectExit
helper near the existing CLI test utilities that wraps runCli, validates signal
is null and code matches the expected exit status, and returns stdout/stderr for
callers that need them. Replace the repeated signal/code assertions in the
affected check blocks, including the tests around clean, warning-only,
minimum-health, and non-project behavior, while preserving their existing
cleanup and stderr-specific assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/cli/src/bin.ts`:
- Around line 5-12: Invoke the defined main function from the executable entry
point in bin.ts so runCli executes for every CLI command. Preserve the existing
exit-code handling inside main, including immediate process exits and assigned
process.exitCode values.
- Around line 7-8: Update the immediate-exit branch in bin.ts to await
completion of both standard output and standard error drains before calling
process.exit(code). Ensure all callers returning exit: 'immediate', including
install, ci, and argument-validation paths, use this shared draining behavior
rather than analyzer-specific or stdout-only handling.

---

Nitpick comments:
In `@scripts/cli-e2e.mjs`:
- Around line 25-42: Update runCli to pass a per-process timeout option to
execFileSync, while preserving any caller-provided options through the existing
opts spread. Use the timeout value expected by the checks so hung CLI
invocations terminate promptly and continue returning the resulting status and
signal through the existing error handling.
- Around line 89-148: Optionally add an expectExit helper near the existing CLI
test utilities that wraps runCli, validates signal is null and code matches the
expected exit status, and returns stdout/stderr for callers that need them.
Replace the repeated signal/code assertions in the affected check blocks,
including the tests around clean, warning-only, minimum-health, and non-project
behavior, while preserving their existing cleanup and stderr-specific
assertions.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ef94b35b-8efc-42f0-80f6-049688bc761b

📥 Commits

Reviewing files that changed from the base of the PR and between 1f3732c and f3360d7.

⛔ Files ignored due to path filters (1)
  • packages/cli/test/__snapshots__/help-golden.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (8)
  • .github/workflows/ci.yml
  • package.json
  • packages/cli/src/bin.ts
  • packages/cli/src/cli.ts
  • packages/cli/src/install/cli.ts
  • packages/cli/test/cli-contract.test.ts
  • packages/cli/test/help-golden.test.ts
  • scripts/cli-e2e.mjs

Comment thread packages/cli/src/bin.ts
Comment thread packages/cli/src/bin.ts
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