Skip to content

fix(upgrade): detect failed extraction and stop leaking temp directories - #561

Merged
rhuanbarreto merged 8 commits into
mainfrom
claude/issues-554-555-556-8fccd9
Aug 7, 2026
Merged

fix(upgrade): detect failed extraction and stop leaking temp directories#561
rhuanbarreto merged 8 commits into
mainfrom
claude/issues-554-555-556-8fccd9

Conversation

@rhuanbarreto

Copy link
Copy Markdown
Contributor

Fixes #554, #555 and #556 — all three in binary-upgrade.ts.

#554Expand-Archive failure swallowed on Windows

Confirmed against the exact production command with a deliberately corrupt archive:

command exit code
current 0
+ -ErrorAction Stop 1
+ -ErrorAction Stop, valid archive 0, extracted

-ErrorAction Stop promotes the non-terminating error, and a valid archive still extracts — the guard is not over-rejecting.

Added a post-extraction existence check as well. It covers the tar branch too, so an archive that extracts to nothing is reported where it happens rather than as a missing binary later.

#555 — the backslash branch is not dead

The issue states that GNU tar's escaping yields ..//evil, "which matches none of the three guard conditions". That is incorrect: "..//evil".includes("../") is true — the ../ needle matches at index 0.

Verified end-to-end against real GNU tar 1.35 rather than by reading:

tar lists ..\evil as normalized guard fires
GNU (Linux) ..\evil ..//evil yes
bsdtar (macOS) ..\evil ../evil yes
either, without normalization unchanged no

So the normalization is load-bearing, not decoration. Fire-tested under WSL: deleting .replaceAll("\\", "/") makes the new case fail, restoring it makes it pass.

No production change here. What was actually wrong was the test file, which documented the backslash row as deliberately absent and gave this false reason. The row is now present with the comment corrected; it asserts /\.\.\+evil/u so it holds for both tar implementations.

#556 — temp directory leak

Every failure path now removes the extraction directory.

The issue says "only the success path's caller cleans up" — the caller never cleaned up at all, so a successful upgrade leaked the directory plus the ~100 MB archive inside it. upgradeBinary now removes it once the binary is installed, from an inner finally that runs before the handler calling exitWith() (which ends the process, so a trailing finally would be skipped).

Verification

Each fix was fire-tested by removing it and confirming the corresponding test fails:

removed result
-ErrorAction Stop 1 fail
existence check 2 fail
failure-path rmSync 4 fail
backslash normalization (WSL) 1 fail

The tar-branch tests are skipIf(win32), so they were run under WSL Ubuntu rather than left to CI — 11/11 pass on Linux.

bun run validate green: 2390 pass / 0 fail, archgate check 51/51 with 0 warnings under strict mode.

Expand-Archive reports a corrupt archive as a non-terminating error, so
`powershell -Command` exits 0 and extraction failure went unnoticed.
`-ErrorAction Stop` promotes it to terminating. A post-extraction
existence check covers both branches, so an archive that extracts to
nothing is reported where it happens rather than as a missing binary
later.

The extraction directory is removed on every failure path, and the
caller removes it once the binary is installed.

The tar path-traversal guard's backslash normalization is live, not
dead: GNU tar lists a member stored as `..\evil` with the backslash
doubled, and normalizing that yields `..//evil`, which contains `../`.
bsdtar lists it verbatim, which normalizes to `../evil`. Both trip the
guard, and neither does without the normalization. The traversal table
gains the case it was missing.

Closes #554
Closes #555
Closes #556

Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@rhuanbarreto, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 17 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2ed606fe-c93f-4dec-a91b-02bb173fde8f

📥 Commits

Reviewing files that changed from the base of the PR and between d868dd1 and 41602f9.

📒 Files selected for processing (3)
  • src/helpers/binary-upgrade.ts
  • tests/helpers/binary-upgrade-archive.test.ts
  • tests/helpers/binary-upgrade.test.ts
📝 Walkthrough

Walkthrough

Archive upgrades now validate tar and ZIP extraction, reject unsafe paths, require the expected binary, and clean temporary directories after failures or installation attempts. Tests cover cleanup, backslash traversal, PowerShell exit handling, -ErrorAction Stop, and missing output binaries. Developer guidance now requires byte-accurate fixture probes, WSL validation for Windows-skipped tests, and independent verification of reported failures and fixes.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes cleanup, tar traversal, subprocess handling, and agent-memory changes that are not required by the directly linked issue [#554]. Split unrelated cleanup, traversal, subprocess, and agent-memory changes into separate pull requests or link the corresponding issues.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies failed extraction detection and temporary-directory leak prevention, which are the main production changes.
Description check ✅ Passed The description directly explains the extraction failure fix, cleanup changes, traversal coverage, and verification results.
Linked Issues check ✅ Passed The changes satisfy issue [#554] by making Expand-Archive failures observable and verifying that extraction produces the expected binary.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 6, 2026

Copy link
Copy Markdown

Deploying archgate-cli with  Cloudflare Pages  Cloudflare Pages

Latest commit: 41602f9
Status: ✅  Deploy successful!
Preview URL: https://e1b647fb.archgate-cli.pages.dev
Branch Preview URL: https://claude-issues-554-555-556-8f.archgate-cli.pages.dev

View logs

@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

🤖 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 @.claude/agent-memory/archgate-developer/feedback_verify_agent_claims.md:
- Around line 23-25: Update the guidance around reproducing prior-session
reports to require verification in the current checkout without claiming that
all prior analyses were never executed. Preserve the requirement to reproduce
the reported failure and separately validate the premise behind the proposed
fix, describing prior reports as unverified in the current checkout.

In `@src/helpers/binary-upgrade.ts`:
- Around line 239-287: Update the archive handling around the listing and
extraction subprocesses to consume each configured stdout and stderr stream
concurrently before awaiting process exit, preventing blocked pipes. For the tar
listing process, await its exit code and throw a clear UserError on non-zero
exit before validating listing entries; apply the same stream-draining pattern
to tar extraction and PowerShell extraction while preserving their existing
failure messages.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: 1ba000e2-9e6a-4d04-8a05-0a6021aec531

📥 Commits

Reviewing files that changed from the base of the PR and between dbfa313 and 14ad008.

📒 Files selected for processing (5)
  • .claude/agent-memory/archgate-developer/MEMORY.md
  • .claude/agent-memory/archgate-developer/feedback_verify_agent_claims.md
  • src/commands/upgrade.ts
  • src/helpers/binary-upgrade.ts
  • tests/helpers/binary-upgrade-archive.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Cloudflare Pages
🧰 Additional context used
📓 Path-based instructions (12)
{src,tests}/**/*.ts

📄 CodeRabbit inference engine (.archgate/adrs/LEGAL-001-spdx-license-headers.md)

{src,tests}/**/*.ts: Every TypeScript source file in src/ and tests/ must begin with // SPDX-License-Identifier: Apache-2.0 followed by // Copyright 2026 Archgate.
If a TypeScript file has a shebang line (for example #!/usr/bin/env bun in src/cli.ts), the SPDX license header must appear immediately after the shebang.
Use single-line // comments for the SPDX header; do not use block comments (/* */) or alternate license identifiers.

Files:

  • src/helpers/binary-upgrade.ts
  • src/commands/upgrade.ts
  • tests/helpers/binary-upgrade-archive.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-006-dependency-policy.md)

**/*.{ts,tsx}: Prefer Bun built-ins for file I/O, HTTP, globbing, testing, and subprocess execution; prefer node: built-in modules over npm alternatives when appropriate.
Use Bun.spawn with array-based arguments for all subprocess execution; do not use Bun.$ because it can hang on Windows.
Do not add npm packages for functionality already provided by Bun, such as glob, chalk, or utility libraries used for a single function.
Use Bun APIs such as Bun.file() instead of Node.js-specific APIs such as fs.readFile() when Bun provides an equivalent.
Use relative imports with Bun's native module resolution; do not use TypeScript path aliases.

Use TypeScript strict mode with ESNext and ES modules; derive schema types with z.infer<> rather than defining separate interfaces.

Files:

  • src/helpers/binary-upgrade.ts
  • src/commands/upgrade.ts
  • tests/helpers/binary-upgrade-archive.test.ts
src/**/*.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-018-lazy-load-heavy-dependencies.md)

src/**/*.ts: Heavy runtime dependencies such as inquirer, posthog-node, and @sentry/* must be loaded with dynamic import() at their point of use, never through top-level static value imports.
Type-only imports for heavy dependencies are allowed, but runtime values must be obtained through dynamic import(); for example, use import type { PostHog } from "posthog-node".
SDKs that require early initialization may use eager-start/lazy-await: begin initialization before command registration and await the result at first use, such as in a preAction hook.

src/**/*.ts: In all TypeScript source files under src/, use Bun.env for every environment-variable read and write; never reference process.env, including in comments.
Access Bun.env properties directly; do not create wrapper functions around it or destructure it.
Use nullish coalescing for environment-variable defaults, such as Bun.env.NODE_ENV ?? "production".
For truthy environment-flag checks, use Boolean(Bun.env.FLAG) only inline, as part of a larger expression, or assign it to a variable before using it as a sole condition; otherwise use an explicit defined-and-nonempty comparison.

src/**/*.ts: Every inquirer.prompt(...) call must be wrapped in withPromptFix(() => ...) imported from src/helpers/prompt.ts; keep the wrapper adjacent to the prompt invocation so automated checks can detect it.
Do not call inquirer.prompt(...) directly or reimplement cursor/newline fixes at individual call sites; route all prompt behavior through withPromptFix().

Every call to Bun.Glob#scan() (glob.scan(...)) in source must pass { dot: true } in its options object, including scans whose patterns do not explicitly target dot-directories. Do not use dot: false; intentionally excluded dotfiles must be filtered explicitly after scanning with a comment. Normalize scanned path separators with file.replaceAll("\\", "/") when performing cross-platform path comparisons.

src/**/*.ts: Use `sty...

Files:

  • src/helpers/binary-upgrade.ts
  • src/commands/upgrade.ts
{src,tests,lint,scripts,shims}/**/*.ts

📄 CodeRabbit inference engine (.archgate/adrs/GEN-004-concise-forward-only-code-comments.md)

{src,tests,lint,scripts,shims}/**/*.ts: Project-authored TypeScript comments must be concise, describe current behavior only, and never narrate history, relocations, refactors, or how the code came to be.
A contiguous run of whole-line comments must contain at most five lines of narrative prose; longer rationale belongs in an ADR, agent-memory file, issue, or PR with a pointer. Tests and fixtures follow the same limit.
Use structural TSDoc tags such as @param, @returns, @throws, @example, and @see for structured documentation; tagged sections are exempt from the five-line narrative bound, while @remarks, @description, @summary, @notes, @todo, and @fixme remain counted as prose.

Files:

  • src/helpers/binary-upgrade.ts
  • src/commands/upgrade.ts
  • tests/helpers/binary-upgrade-archive.test.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.{ts,tsx}: For user-scope editors, resolve paths using the editor's actual path helper; do not assume Windows conventions. For opencode, mirror xdg-basedir, which falls back to ~/.config on all platforms.
For opencode-gated behavior, use isOpencodeAvailable() rather than isOpencodeCliAvailable() alone because the Desktop distribution has no CLI binary and shares the config directory.
For Copilot-gated behavior, use isCopilotAvailable() rather than isCopilotCliAvailable() alone because desktop and CLI distributions share ~/.copilot/.

Files:

  • src/helpers/binary-upgrade.ts
  • src/commands/upgrade.ts
**/*.{js,ts,tsx,mjs,cjs}

📄 CodeRabbit inference engine (.archgate/adrs/GEN-003-tool-invocation-via-scripts.md)

Invoke linting, formatting, and validation through package scripts (bun run lint, bun run format, bun run format:check, and bun run validate), rather than directly invoking tool binaries such as bunx prettier, bunx oxfmt, npx eslint, or oxlint.

Files:

  • src/helpers/binary-upgrade.ts
  • src/commands/upgrade.ts
  • tests/helpers/binary-upgrade-archive.test.ts
src/**/!(platform).ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-009-platform-detection-helper.md)

All platform detection in src/ must go through src/helpers/platform.ts (isWindows(), isMacOS(), isLinux(), isWSL(), or getPlatformInfo()); direct process.platform access and duplicated detection logic are forbidden outside platform.ts.

Files:

  • src/helpers/binary-upgrade.ts
  • src/commands/upgrade.ts
**

⚙️ CodeRabbit configuration file

**: This project uses Archgate — an AI governance framework based on
Architecture Decision Records (ADRs). The ADRs in .archgate/adrs/
are the authoritative rules for this codebase. Each ADR has a companion
.rules.ts file with automated checks that run via archgate check.

When reviewing, you must:

  1. Treat ADR violations as blocking issues, not suggestions.
  2. Cite the specific ADR ID when flagging a violation (e.g., "Violates ARCH-006").
  3. Focus on semantic and contextual violations that automated rules cannot catch —
    the .rules.ts files already cover syntactic/structural patterns.
  4. If you are unsure whether something violates an ADR, flag it as a question
    rather than approving it.

Files:

  • src/helpers/binary-upgrade.ts
  • src/commands/upgrade.ts
  • tests/helpers/binary-upgrade-archive.test.ts
src/commands/**/*.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-011-consistent-project-root-resolution.md)

src/commands/**/*.ts: All commands that operate on .archgate/ project resources must use the shared findProjectRoot() from src/helpers/paths.ts; direct process.cwd() project-root resolution is prohibited except in init.
Commands that require a project must use requireProjectRoot() from src/helpers/paths.ts instead of implementing their own missing-project guard. Commands that can operate without a project may use findProjectRoot() and handle null themselves.
When using findProjectRoot() directly, check for a null result and provide a helpful error before exiting.
Pass the resolved projectRoot to projectPaths() when constructing derived project paths.
Do not define local findProjectRoot() variants; use the shared implementation from src/helpers/paths.ts.

Command modules must export a register*Command(program) function, handle I/O only, and contain no business logic.

src/commands/**/*.ts: Each command module must export a register*Command(program) function; each non-index.ts command file must define exactly one command.
Command files must remain thin: parse arguments, call engine/helpers, and format output; business logic must reside in src/engine/, src/helpers/, or src/formats/.
Commands must execute in-process and must not spawn child processes for subcommand execution.
Command files must not call .parse(); argument parsing is handled by the CLI entry point.
Commands should use typed Commander registration APIs, such as @commander-js/extra-typings, within their register*Command functions.

src/commands/**/*.ts: In Commander.js command files, options requiring type narrowing beyond plain strings MUST use new Option() from @commander-js/extra-typings and register it with .addOption() instead of .option().
Use .choices([... ] as const) for options accepting a fixed set of values, and use .default(... as const) when providing a default, preserving literal type inference.
Use .argParser((value) => ...)...

Files:

  • src/commands/upgrade.ts
src/commands/{*.ts,*/index.ts}

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-015-cli-command-documentation-coverage.md)

Top-level command modules must follow the src/commands/<name>.ts or src/commands/<name>/index.ts convention. Nested subcommand files do not count as top-level commands.

Files:

  • src/commands/upgrade.ts
tests/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Tests mirror the src/ structure; shared test fixtures belong under tests/fixtures/.

tests/**/*.ts: Use Bun's built-in bun:test runner for all tests; do not import node:test. Test files belong under tests/, mirror src/, use tests/fixtures/ for shared fixtures, and follow <module-name>.test.ts naming.
Filesystem tests must use isolated mkdtemp directories and clean them up in afterEach or afterAll; do not touch real user-scope paths or leave temporary files behind.
Close external SDK instances, servers, clients, and transports in test hooks, such as await server.close() in afterEach or afterAll.
Restore every captured environment variable with restoreEnv(key, original); never restore with direct assignment such as Bun.env.X = original, because undefined becomes the string "undefined".
Mock os.homedir() via an imported module namespace and spyOn; do not rely on overriding HOME for code using os.homedir(), and keep filesystem writes inside temporary directories.
Shared test helpers, including non-test files under tests/, must restore every captured environment variable with restoreEnv; isolation responsibilities apply across the entire shared Bun test process.

Use _resetAllCaches() from src/helpers/platform.ts to simulate different platforms in tests rather than mocking process.platform directly.

Files:

  • tests/helpers/binary-upgrade-archive.test.ts
tests/**/*.test.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-025-idiomatic-bun-test-parametrization-and-matchers.md)

tests/**/*.test.ts: Use test.each() or describe.each() for the same assertion logic against multiple independent inputs. Do not register tests or call expect() once per case inside a for or .forEach loop.
Use array rows for positional destructuring and object rows for named fields when passing cases to test.each() or describe.each(). Choose descriptive title placeholders such as %s, %p, %d, or $field.
Assert derived facts with the most specific matcher available instead of passing a derived boolean to .toBe(true) or .toBe(false). Compare values directly with .toBe() or .toEqual().
Use specific matchers for common derived checks: .toContain() or .toMatch() for containment, .toBeInstanceOf() for type checks such as Array.isArray, .toHaveLength() for counts, and .find() with .toBeDefined() or .toBeUndefined() for predicate existence checks.
Do not precompute a boolean solely to assert it; assert the underlying values directly with matchers such as .toEqual() or .toBe().
When converting a loop to test.each() or describe.each(), preserve every assertion that ran per iteration; do not drop or merge assertions.

tests/**/*.test.ts: Every runnable test must contain an expect() assertion; use test.skip or test.todo for placeholders rather than assertion-less or silently skipped tests.
Test public interfaces with descriptive names rather than private implementation details.
Do not use mock.module() for first-party modules. Mock them with import * as mod plus spyOn(mod, "fn"), and restore mocks after each test. mock.module() may be used for approved external modules such as inquirer or node:readline.
For HTTP mocking, save globalThis.fetch before direct assignment and restore it in afterEach; do not use mock.module("node:fetch"), which does not intercept Bun's global fetch.
Wrap inline spyOn or mockImplementation lifecycles in try/finally, or manage them in hooks, so mockRestore() runs wh...

Files:

  • tests/helpers/binary-upgrade-archive.test.ts
🧠 Learnings (16)
📚 Learning: 2026-06-11T12:50:28.661Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 406
File: .claude/agent-memory/archgate-developer/feedback_prefer_tests_over_adr_rules.md:8-18
Timestamp: 2026-06-11T12:50:28.661Z
Learning: In `archgate/cli`, for markdown files under `.claude/agent-memory/`, follow the established convention: use YAML frontmatter (with a `name:` field used as the document title) and do not require a top-level `#` (H1) heading. During code review, do not flag missing first-line/first-top-level H1 headings (e.g., MD041) for these agent-memory files since markdownlint is not part of the repo’s `bun run validate` lint pipeline (oxlint/oxfmt only).

Applied to files:

  • .claude/agent-memory/archgate-developer/feedback_verify_agent_claims.md
  • .claude/agent-memory/archgate-developer/MEMORY.md
📚 Learning: 2026-07-25T00:05:20.592Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 496
File: .claude/agent-memory/archgate-developer/project_test_isolation_gotchas.md:10-10
Timestamp: 2026-07-25T00:05:20.592Z
Learning: When reviewing documentation/agent-memory entries under `.claude/agent-memory/**`, do not enforce GEN-004’s “forward-only” comment/narrative requirement. These entries are allowed to keep historical/past-tense incident narratives and dated markers (e.g., `Found YYYY-MM-DD`) because the context is intended to help future agents evaluate edge cases. Outside this scope, GEN-004’s forward-only rule should still apply.

Applied to files:

  • .claude/agent-memory/archgate-developer/feedback_verify_agent_claims.md
  • .claude/agent-memory/archgate-developer/MEMORY.md
📚 Learning: 2026-07-02T16:03:33.031Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 446
File: src/helpers/session-context-opencode.ts:81-100
Timestamp: 2026-07-02T16:03:33.031Z
Learning: For synchronous helper functions that use Bun’s sqlite sync API (i.e., they must remain synchronous), it’s acceptable to use `existsSync` from `node:fs` to check whether the SQLite database file exists. Avoid using `Bun.file(path).exists()` for this purpose because it’s async and would force the helper to become async (no equivalent synchronous Bun alternative). If the DB file is missing, throw/return a clear, actionable "No database found" error (per ARCH-006) rather than letting the sqlite open fail with a generic DB-open error.

Applied to files:

  • src/helpers/binary-upgrade.ts
📚 Learning: 2026-07-25T00:05:58.884Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 496
File: tests/helpers/auth.test.ts:38-46
Timestamp: 2026-07-25T00:05:58.884Z
Learning: When reviewing the Archgate CLI repository’s GEN-004 “concise forward-only narration” comments, don’t rely only on the automated phrase-based narration checks. Those checks can pass even when the comment wording describes historical/transfer semantics rather than current behavior (e.g., saying a prior restore “leaked” a value or a later subprocess “inherited it”). Manually verify that the comment describes the code’s current, forward behavior; flag or adjust comments that imply past/historical state transfer even if GEN-004 enforcement passes.

Applied to files:

  • src/helpers/binary-upgrade.ts
  • src/commands/upgrade.ts
  • tests/helpers/binary-upgrade-archive.test.ts
📚 Learning: 2026-07-25T00:05:59.109Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 496
File: src/cli.ts:0-0
Timestamp: 2026-07-25T00:05:59.109Z
Learning: Code comments may include a concise issue/PR reference (per GEN-004) when it’s used to point readers to fuller rationale instead of inlining that rationale. During review, flag surrounding comment prose that reads like historical context or narrates refactors/relocations; a bare GEN-004-style reference is allowed and should not be flagged by itself.

Applied to files:

  • src/helpers/binary-upgrade.ts
  • src/commands/upgrade.ts
  • tests/helpers/binary-upgrade-archive.test.ts
📚 Learning: 2026-08-04T19:58:05.877Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 543
File: src/helpers/copilot-user-settings.ts:0-0
Timestamp: 2026-08-04T19:58:05.877Z
Learning: In archgate/cli TypeScript code, use `Bun.file(path).exists()` only to check whether a file exists; it must not be used for directory existence checks. For helpers such as `isCopilotAvailable()` that need to detect a configuration directory, use an appropriate directory-aware check such as `existsSync` from `node:fs`.

Applied to files:

  • src/helpers/binary-upgrade.ts
  • src/commands/upgrade.ts
  • tests/helpers/binary-upgrade-archive.test.ts
📚 Learning: 2026-08-05T06:56:33.435Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 546
File: tests/integration/stream-guards.test.ts:3-9
Timestamp: 2026-08-05T06:56:33.435Z
Learning: When reviewing GEN-004 comment-block limits in the Archgate CLI repository, count only narrative prose lines within a block comment. Do not count a closing delimiter such as `*/` as a prose line; for example, in `tests/integration/stream-guards.test.ts`, Lines 4–8 contain five prose lines while Line 9 contains only the delimiter.

Applied to files:

  • src/helpers/binary-upgrade.ts
  • src/commands/upgrade.ts
  • tests/helpers/binary-upgrade-archive.test.ts
📚 Learning: 2026-07-25T22:03:14.216Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 501
File: .archgate/adrs/ARCH-002-error-handling.md:0-0
Timestamp: 2026-07-25T22:03:14.216Z
Learning: In Archgate boundary-wrapped CLI command actions (the handlers that rely on `handleCommandError()` for user-facing error output), expected-failure guards should signal user errors by throwing `new UserError(<message/details>)` rather than directly calling `logError()` followed by `exitWith(1)`. This keeps user-facing logging and the exit path centralized in `handleCommandError()`. For normal/computed command outcomes (e.g., `const exitCode = getExitCode(await runChecks(...))`), use `await exitWith(exitCode)` instead of calling `process.exit(exitCode)` so telemetry/Sentry flushing and outcome tagging still run.

Applied to files:

  • src/helpers/binary-upgrade.ts
  • src/commands/upgrade.ts
  • tests/helpers/binary-upgrade-archive.test.ts
📚 Learning: 2026-07-25T22:03:22.236Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 501
File: .archgate/adrs/ARCH-016-cli-subcommand-documentation-accuracy.md:64-67
Timestamp: 2026-07-25T22:03:22.236Z
Learning: When reviewing CLI subcommand documentation, don’t rely solely on the companion ARCH-016 enforcement rule’s limited path coverage (it only checks `src/commands/<parent>/*.ts` and `src/commands/<parent>/*/index.ts`). Manually verify that subcommands documented by convention in deeper paths (e.g., `src/commands/<parent>/**/add.ts` or `src/commands/adr/domain/add.ts`) have the required documentation, since future nested subcommands can drift without automated detection (tracked by ARCH-015 / GitHub `#503`).

Applied to files:

  • src/commands/upgrade.ts
📚 Learning: 2026-07-15T22:55:51.978Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 476
File: tests/helpers/telemetry-config.test.ts:24-28
Timestamp: 2026-07-15T22:55:51.978Z
Learning: In this Bun/TypeScript codebase, when a unit under test spawns subprocesses via Bun.spawn (e.g., running `git credential ...`), prefer overriding relevant env vars (such as `HOME`, `GIT_CONFIG_GLOBAL`, `GIT_CONFIG_NOSYSTEM`) using `process.env` in the test and restoring them with the test utility (e.g., `restoreEnv` from `tests/test-utils.ts`). Avoid relying on `spyOn(os, 'homedir')` for this purpose, because it only affects in-process calls and does not change the environment inherited by subprocesses; env-var overrides should be used for subprocess-level isolation and must be applied at call time.

Applied to files:

  • tests/helpers/binary-upgrade-archive.test.ts
📚 Learning: 2026-07-15T22:56:35.415Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 476
File: tests/commands/clean.test.ts:61-62
Timestamp: 2026-07-15T22:56:35.415Z
Learning: When reviewing tests that rely on src/helpers/paths.ts `internalPath()`, note that `internalPath()` intentionally reads `Bun.env.HOME ?? Bun.env.USERPROFILE` at call time and only uses `os.homedir()` if neither env var is set. Therefore, don’t suggest changing tests to `spyOn(os, "homedir")` for this behavior; instead, use per-test `Bun.env.HOME` / `Bun.env.USERPROFILE` overrides (as applicable) so the tests control `internalPath()`’s inputs. 

Applied to files:

  • tests/helpers/binary-upgrade-archive.test.ts
📚 Learning: 2026-07-25T15:44:40.668Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 501
File: .archgate/adrs/ARCH-005-testing-standards.md:0-0
Timestamp: 2026-07-25T15:44:40.668Z
Learning: In Archgate CLI test code governed by ARCH-007, only allow `Bun.$` in test suites that are explicitly restricted to a single platform. Any cross-platform test that runs on Linux, macOS, and Windows must avoid `Bun.$` and instead use array-based `Bun.spawn`. For shared git setup used by tests, import and use the `git()` helper from `tests/test-utils.ts` rather than duplicating git setup logic.

Applied to files:

  • tests/helpers/binary-upgrade-archive.test.ts
📚 Learning: 2026-08-05T16:54:13.117Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 550
File: tests/commands/adr/domain/remove.test.ts:21-24
Timestamp: 2026-08-05T16:54:13.117Z
Learning: In the Archgate CLI test suite, continue using `z.object` for JSON output schemas unless a repository-wide testing policy explicitly adopts `z.strictObject`. Do not introduce strict CLI-output schema enforcement as an isolated change in a coverage-focused pull request; require coordinated updates and policy agreement across affected tests.

Applied to files:

  • tests/helpers/binary-upgrade-archive.test.ts
📚 Learning: 2026-07-25T23:21:49.190Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 512
File: tests/engine/git-files.test.ts:98-100
Timestamp: 2026-07-25T23:21:49.190Z
Learning: When reviewing archgate/cli for ARCH-006 (per its ADR frontmatter), only enforce the production-dependency policy scoped to package.json. Do not treat test-only refactors or relocated `node:fs` fixture writes as an ARCH-006 violation (since ARCH-006 does not govern test-file I/O API selection). If there’s a broader/test-wide refactor that would migrate fixture writing to `Bun.write()`, evaluate it separately under the appropriate in-scope rule.

Applied to files:

  • tests/helpers/binary-upgrade-archive.test.ts
📚 Learning: 2026-07-27T16:05:38.683Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 536
File: tests/commands/adr/sync-strict.test.ts:173-173
Timestamp: 2026-07-27T16:05:38.683Z
Learning: In this Bun + TypeScript repo, for rejected-promise assertions use the unawaited form: `expect(promise).rejects.toThrow(...)`. Do NOT add `await` to `expect(promise).rejects.toThrow(...)` (Bun’s types model this as `void`), because it will violate the type-aware oxlint rules `typescript(await-thenable)` and `typescript(no-confusing-void-expression)`. Only request an `await` if the repo adopts a typed, lint-compliant assertion helper or Bun’s typings change.

Applied to files:

  • tests/helpers/binary-upgrade-archive.test.ts
📚 Learning: 2026-08-05T16:54:50.574Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 550
File: tests/helpers/plugin-install-cursor-hooks.test.ts:44-44
Timestamp: 2026-08-05T16:54:50.574Z
Learning: In this repository, every TypeScript module under `src/` must have a matching `<module-name>.test.ts` file under the mirrored `tests/` directory, as required by ARCH-005. Supplemental behavior-suffixed sibling test files are allowed only when the matching parent test file exists. Use such siblings to keep individual test files below the 500-line oxlint limit.

Applied to files:

  • tests/helpers/binary-upgrade-archive.test.ts
🔇 Additional comments (6)
.claude/agent-memory/archgate-developer/MEMORY.md (1)

28-35: LGTM!

src/helpers/binary-upgrade.ts (2)

4-11: LGTM!


228-238: LGTM!

Also applies to: 290-303

src/commands/upgrade.ts (2)

3-3: LGTM!


281-303: LGTM!

tests/helpers/binary-upgrade-archive.test.ts (1)

12-13: LGTM!

Also applies to: 109-143, 158-160, 176-176, 185-203, 224-224, 233-268

Comment thread .claude/agent-memory/archgate-developer/feedback_verify_agent_claims.md Outdated
Comment thread src/helpers/binary-upgrade.ts Outdated
An empty listing from a failed `tar -tzf` read as "no unsafe entries" and
waved the archive past the path-traversal guard; a non-zero listing exit
now aborts before extraction.

Subprocess output goes through one helper that drains stdout and stderr
concurrently with the exit code, the pattern ARCH-007 prescribes.

The tar extraction-failure branch is now covered on every runner rather
than only where `tar -tzf` can read a Windows path.

Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 6, 2026
Deriving the directory to remove from `dirname(binaryPath)` deletes
whichever directory the binary happens to sit in. downloadReleaseBinary
now returns that directory alongside the binary, so the caller removes
the one actually created rather than inferring it.

The success-path cleanup gains an assertion against a real directory,
which is what the mocked string path left uncovered.

Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Code Coverage

Metric Value
Lines 99.9% (9752 / 9762)
Threshold 99.5% minimum — met
Platforms Linux + Windows

Full HTML report available in workflow artifacts.

Per-directory breakdown
Directory Coverage Lines
src/commands/ 100.0% 2365 / 2365
src/engine/ 100.0% 2595 / 2596
src/formats/ 100.0% 151 / 151
src/helpers/ 99.8% 4641 / 4650

@rhuanbarreto

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 7, 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

🤖 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 `@src/helpers/binary-upgrade.ts`:
- Line 327: Update the binary extraction return path around binaryPath to
validate it with lstatSync(binaryPath).isFile() rather than existsSync(),
rejecting symbolic links and directories by throwing UserError before returning.
Add a regression test covering an archive whose archgate member extracts as a
symlink, ensuring it is not accepted or cached.

In `@tests/helpers/binary-upgrade.test.ts`:
- Line 384: Update the test around downloadReleaseBinary to retain tmpDir from
the returned result and wrap the extraction assertions in a finally block that
removes tmpDir, ensuring the archgate-upgrade-* directory is cleaned up after
successful or failed execution.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: cf2115ea-7b2c-4929-b82c-9220a96d4853

📥 Commits

Reviewing files that changed from the base of the PR and between 14ad008 and d868dd1.

📒 Files selected for processing (8)
  • .claude/agent-memory/archgate-developer/MEMORY.md
  • .claude/agent-memory/archgate-developer/feedback_verify_agent_claims.md
  • src/commands/upgrade.ts
  • src/helpers/binary-upgrade.ts
  • tests/commands/upgrade-action.test.ts
  • tests/commands/upgrade-dispatch.test.ts
  • tests/helpers/binary-upgrade-archive.test.ts
  • tests/helpers/binary-upgrade.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Cloudflare Pages
🧰 Additional context used
📓 Path-based instructions (12)
{src,tests}/**/*.ts

📄 CodeRabbit inference engine (.archgate/adrs/LEGAL-001-spdx-license-headers.md)

{src,tests}/**/*.ts: Every TypeScript source file in src/ and tests/ must begin with // SPDX-License-Identifier: Apache-2.0 followed by // Copyright 2026 Archgate.
If a TypeScript file has a shebang line (for example #!/usr/bin/env bun in src/cli.ts), the SPDX license header must appear immediately after the shebang.
Use single-line // comments for the SPDX header; do not use block comments (/* */) or alternate license identifiers.

Files:

  • tests/helpers/binary-upgrade.test.ts
  • src/commands/upgrade.ts
  • tests/commands/upgrade-dispatch.test.ts
  • src/helpers/binary-upgrade.ts
  • tests/helpers/binary-upgrade-archive.test.ts
  • tests/commands/upgrade-action.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-006-dependency-policy.md)

**/*.{ts,tsx}: Prefer Bun built-ins for file I/O, HTTP, globbing, testing, and subprocess execution; prefer node: built-in modules over npm alternatives when appropriate.
Use Bun.spawn with array-based arguments for all subprocess execution; do not use Bun.$ because it can hang on Windows.
Do not add npm packages for functionality already provided by Bun, such as glob, chalk, or utility libraries used for a single function.
Use Bun APIs such as Bun.file() instead of Node.js-specific APIs such as fs.readFile() when Bun provides an equivalent.
Use relative imports with Bun's native module resolution; do not use TypeScript path aliases.

Use TypeScript strict mode with ESNext and ES modules; derive schema types with z.infer<> rather than defining separate interfaces.

Files:

  • tests/helpers/binary-upgrade.test.ts
  • src/commands/upgrade.ts
  • tests/commands/upgrade-dispatch.test.ts
  • src/helpers/binary-upgrade.ts
  • tests/helpers/binary-upgrade-archive.test.ts
  • tests/commands/upgrade-action.test.ts
{src,tests,lint,scripts,shims}/**/*.ts

📄 CodeRabbit inference engine (.archgate/adrs/GEN-004-concise-forward-only-code-comments.md)

{src,tests,lint,scripts,shims}/**/*.ts: Project-authored TypeScript comments must be concise, describe current behavior only, and never narrate history, relocations, refactors, or how the code came to be.
A contiguous run of whole-line comments must contain at most five lines of narrative prose; longer rationale belongs in an ADR, agent-memory file, issue, or PR with a pointer. Tests and fixtures follow the same limit.
Use structural TSDoc tags such as @param, @returns, @throws, @example, and @see for structured documentation; tagged sections are exempt from the five-line narrative bound, while @remarks, @description, @summary, @notes, @todo, and @fixme remain counted as prose.

Files:

  • tests/helpers/binary-upgrade.test.ts
  • src/commands/upgrade.ts
  • tests/commands/upgrade-dispatch.test.ts
  • src/helpers/binary-upgrade.ts
  • tests/helpers/binary-upgrade-archive.test.ts
  • tests/commands/upgrade-action.test.ts
tests/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Tests mirror the src/ structure; shared test fixtures belong under tests/fixtures/.

tests/**/*.ts: Use Bun's built-in bun:test runner for all tests; do not import node:test. Test files belong under tests/, mirror src/, use tests/fixtures/ for shared fixtures, and follow <module-name>.test.ts naming.
Filesystem tests must use isolated mkdtemp directories and clean them up in afterEach or afterAll; do not touch real user-scope paths or leave temporary files behind.
Close external SDK instances, servers, clients, and transports in test hooks, such as await server.close() in afterEach or afterAll.
Restore every captured environment variable with restoreEnv(key, original); never restore with direct assignment such as Bun.env.X = original, because undefined becomes the string "undefined".
Mock os.homedir() via an imported module namespace and spyOn; do not rely on overriding HOME for code using os.homedir(), and keep filesystem writes inside temporary directories.
Shared test helpers, including non-test files under tests/, must restore every captured environment variable with restoreEnv; isolation responsibilities apply across the entire shared Bun test process.

Use _resetAllCaches() from src/helpers/platform.ts to simulate different platforms in tests rather than mocking process.platform directly.

Files:

  • tests/helpers/binary-upgrade.test.ts
  • tests/commands/upgrade-dispatch.test.ts
  • tests/helpers/binary-upgrade-archive.test.ts
  • tests/commands/upgrade-action.test.ts
**/*.{js,ts,tsx,mjs,cjs}

📄 CodeRabbit inference engine (.archgate/adrs/GEN-003-tool-invocation-via-scripts.md)

Invoke linting, formatting, and validation through package scripts (bun run lint, bun run format, bun run format:check, and bun run validate), rather than directly invoking tool binaries such as bunx prettier, bunx oxfmt, npx eslint, or oxlint.

Files:

  • tests/helpers/binary-upgrade.test.ts
  • src/commands/upgrade.ts
  • tests/commands/upgrade-dispatch.test.ts
  • src/helpers/binary-upgrade.ts
  • tests/helpers/binary-upgrade-archive.test.ts
  • tests/commands/upgrade-action.test.ts
tests/**/*.test.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-025-idiomatic-bun-test-parametrization-and-matchers.md)

tests/**/*.test.ts: Use test.each() or describe.each() for the same assertion logic against multiple independent inputs. Do not register tests or call expect() once per case inside a for or .forEach loop.
Use array rows for positional destructuring and object rows for named fields when passing cases to test.each() or describe.each(). Choose descriptive title placeholders such as %s, %p, %d, or $field.
Assert derived facts with the most specific matcher available instead of passing a derived boolean to .toBe(true) or .toBe(false). Compare values directly with .toBe() or .toEqual().
Use specific matchers for common derived checks: .toContain() or .toMatch() for containment, .toBeInstanceOf() for type checks such as Array.isArray, .toHaveLength() for counts, and .find() with .toBeDefined() or .toBeUndefined() for predicate existence checks.
Do not precompute a boolean solely to assert it; assert the underlying values directly with matchers such as .toEqual() or .toBe().
When converting a loop to test.each() or describe.each(), preserve every assertion that ran per iteration; do not drop or merge assertions.

tests/**/*.test.ts: Every runnable test must contain an expect() assertion; use test.skip or test.todo for placeholders rather than assertion-less or silently skipped tests.
Test public interfaces with descriptive names rather than private implementation details.
Do not use mock.module() for first-party modules. Mock them with import * as mod plus spyOn(mod, "fn"), and restore mocks after each test. mock.module() may be used for approved external modules such as inquirer or node:readline.
For HTTP mocking, save globalThis.fetch before direct assignment and restore it in afterEach; do not use mock.module("node:fetch"), which does not intercept Bun's global fetch.
Wrap inline spyOn or mockImplementation lifecycles in try/finally, or manage them in hooks, so mockRestore() runs wh...

Files:

  • tests/helpers/binary-upgrade.test.ts
  • tests/commands/upgrade-dispatch.test.ts
  • tests/helpers/binary-upgrade-archive.test.ts
  • tests/commands/upgrade-action.test.ts
**

⚙️ CodeRabbit configuration file

**: This project uses Archgate — an AI governance framework based on
Architecture Decision Records (ADRs). The ADRs in .archgate/adrs/
are the authoritative rules for this codebase. Each ADR has a companion
.rules.ts file with automated checks that run via archgate check.

When reviewing, you must:

  1. Treat ADR violations as blocking issues, not suggestions.
  2. Cite the specific ADR ID when flagging a violation (e.g., "Violates ARCH-006").
  3. Focus on semantic and contextual violations that automated rules cannot catch —
    the .rules.ts files already cover syntactic/structural patterns.
  4. If you are unsure whether something violates an ADR, flag it as a question
    rather than approving it.

Files:

  • tests/helpers/binary-upgrade.test.ts
  • src/commands/upgrade.ts
  • tests/commands/upgrade-dispatch.test.ts
  • src/helpers/binary-upgrade.ts
  • tests/helpers/binary-upgrade-archive.test.ts
  • tests/commands/upgrade-action.test.ts
src/commands/**/*.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-011-consistent-project-root-resolution.md)

src/commands/**/*.ts: All commands that operate on .archgate/ project resources must use the shared findProjectRoot() from src/helpers/paths.ts; direct process.cwd() project-root resolution is prohibited except in init.
Commands that require a project must use requireProjectRoot() from src/helpers/paths.ts instead of implementing their own missing-project guard. Commands that can operate without a project may use findProjectRoot() and handle null themselves.
When using findProjectRoot() directly, check for a null result and provide a helpful error before exiting.
Pass the resolved projectRoot to projectPaths() when constructing derived project paths.
Do not define local findProjectRoot() variants; use the shared implementation from src/helpers/paths.ts.

Command modules must export a register*Command(program) function, handle I/O only, and contain no business logic.

src/commands/**/*.ts: Each command module must export a register*Command(program) function; each non-index.ts command file must define exactly one command.
Command files must remain thin: parse arguments, call engine/helpers, and format output; business logic must reside in src/engine/, src/helpers/, or src/formats/.
Commands must execute in-process and must not spawn child processes for subcommand execution.
Command files must not call .parse(); argument parsing is handled by the CLI entry point.
Commands should use typed Commander registration APIs, such as @commander-js/extra-typings, within their register*Command functions.

src/commands/**/*.ts: In Commander.js command files, options requiring type narrowing beyond plain strings MUST use new Option() from @commander-js/extra-typings and register it with .addOption() instead of .option().
Use .choices([... ] as const) for options accepting a fixed set of values, and use .default(... as const) when providing a default, preserving literal type inference.
Use .argParser((value) => ...)...

Files:

  • src/commands/upgrade.ts
src/**/*.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-018-lazy-load-heavy-dependencies.md)

src/**/*.ts: Heavy runtime dependencies such as inquirer, posthog-node, and @sentry/* must be loaded with dynamic import() at their point of use, never through top-level static value imports.
Type-only imports for heavy dependencies are allowed, but runtime values must be obtained through dynamic import(); for example, use import type { PostHog } from "posthog-node".
SDKs that require early initialization may use eager-start/lazy-await: begin initialization before command registration and await the result at first use, such as in a preAction hook.

src/**/*.ts: In all TypeScript source files under src/, use Bun.env for every environment-variable read and write; never reference process.env, including in comments.
Access Bun.env properties directly; do not create wrapper functions around it or destructure it.
Use nullish coalescing for environment-variable defaults, such as Bun.env.NODE_ENV ?? "production".
For truthy environment-flag checks, use Boolean(Bun.env.FLAG) only inline, as part of a larger expression, or assign it to a variable before using it as a sole condition; otherwise use an explicit defined-and-nonempty comparison.

src/**/*.ts: Every inquirer.prompt(...) call must be wrapped in withPromptFix(() => ...) imported from src/helpers/prompt.ts; keep the wrapper adjacent to the prompt invocation so automated checks can detect it.
Do not call inquirer.prompt(...) directly or reimplement cursor/newline fixes at individual call sites; route all prompt behavior through withPromptFix().

Every call to Bun.Glob#scan() (glob.scan(...)) in source must pass { dot: true } in its options object, including scans whose patterns do not explicitly target dot-directories. Do not use dot: false; intentionally excluded dotfiles must be filtered explicitly after scanning with a comment. Normalize scanned path separators with file.replaceAll("\\", "/") when performing cross-platform path comparisons.

src/**/*.ts: Use `sty...

Files:

  • src/commands/upgrade.ts
  • src/helpers/binary-upgrade.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.{ts,tsx}: For user-scope editors, resolve paths using the editor's actual path helper; do not assume Windows conventions. For opencode, mirror xdg-basedir, which falls back to ~/.config on all platforms.
For opencode-gated behavior, use isOpencodeAvailable() rather than isOpencodeCliAvailable() alone because the Desktop distribution has no CLI binary and shares the config directory.
For Copilot-gated behavior, use isCopilotAvailable() rather than isCopilotCliAvailable() alone because desktop and CLI distributions share ~/.copilot/.

Files:

  • src/commands/upgrade.ts
  • src/helpers/binary-upgrade.ts
src/commands/{*.ts,*/index.ts}

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-015-cli-command-documentation-coverage.md)

Top-level command modules must follow the src/commands/<name>.ts or src/commands/<name>/index.ts convention. Nested subcommand files do not count as top-level commands.

Files:

  • src/commands/upgrade.ts
src/**/!(platform).ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-009-platform-detection-helper.md)

All platform detection in src/ must go through src/helpers/platform.ts (isWindows(), isMacOS(), isLinux(), isWSL(), or getPlatformInfo()); direct process.platform access and duplicated detection logic are forbidden outside platform.ts.

Files:

  • src/commands/upgrade.ts
  • src/helpers/binary-upgrade.ts
🧠 Learnings (17)
📚 Learning: 2026-07-15T22:55:51.978Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 476
File: tests/helpers/telemetry-config.test.ts:24-28
Timestamp: 2026-07-15T22:55:51.978Z
Learning: In this Bun/TypeScript codebase, when a unit under test spawns subprocesses via Bun.spawn (e.g., running `git credential ...`), prefer overriding relevant env vars (such as `HOME`, `GIT_CONFIG_GLOBAL`, `GIT_CONFIG_NOSYSTEM`) using `process.env` in the test and restoring them with the test utility (e.g., `restoreEnv` from `tests/test-utils.ts`). Avoid relying on `spyOn(os, 'homedir')` for this purpose, because it only affects in-process calls and does not change the environment inherited by subprocesses; env-var overrides should be used for subprocess-level isolation and must be applied at call time.

Applied to files:

  • tests/helpers/binary-upgrade.test.ts
  • tests/helpers/binary-upgrade-archive.test.ts
📚 Learning: 2026-07-15T22:56:35.415Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 476
File: tests/commands/clean.test.ts:61-62
Timestamp: 2026-07-15T22:56:35.415Z
Learning: When reviewing tests that rely on src/helpers/paths.ts `internalPath()`, note that `internalPath()` intentionally reads `Bun.env.HOME ?? Bun.env.USERPROFILE` at call time and only uses `os.homedir()` if neither env var is set. Therefore, don’t suggest changing tests to `spyOn(os, "homedir")` for this behavior; instead, use per-test `Bun.env.HOME` / `Bun.env.USERPROFILE` overrides (as applicable) so the tests control `internalPath()`’s inputs. 

Applied to files:

  • tests/helpers/binary-upgrade.test.ts
  • tests/commands/upgrade-dispatch.test.ts
  • tests/helpers/binary-upgrade-archive.test.ts
  • tests/commands/upgrade-action.test.ts
📚 Learning: 2026-07-25T00:05:58.884Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 496
File: tests/helpers/auth.test.ts:38-46
Timestamp: 2026-07-25T00:05:58.884Z
Learning: When reviewing the Archgate CLI repository’s GEN-004 “concise forward-only narration” comments, don’t rely only on the automated phrase-based narration checks. Those checks can pass even when the comment wording describes historical/transfer semantics rather than current behavior (e.g., saying a prior restore “leaked” a value or a later subprocess “inherited it”). Manually verify that the comment describes the code’s current, forward behavior; flag or adjust comments that imply past/historical state transfer even if GEN-004 enforcement passes.

Applied to files:

  • tests/helpers/binary-upgrade.test.ts
  • src/commands/upgrade.ts
  • tests/commands/upgrade-dispatch.test.ts
  • src/helpers/binary-upgrade.ts
  • tests/helpers/binary-upgrade-archive.test.ts
  • tests/commands/upgrade-action.test.ts
📚 Learning: 2026-07-25T00:05:59.109Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 496
File: src/cli.ts:0-0
Timestamp: 2026-07-25T00:05:59.109Z
Learning: Code comments may include a concise issue/PR reference (per GEN-004) when it’s used to point readers to fuller rationale instead of inlining that rationale. During review, flag surrounding comment prose that reads like historical context or narrates refactors/relocations; a bare GEN-004-style reference is allowed and should not be flagged by itself.

Applied to files:

  • tests/helpers/binary-upgrade.test.ts
  • src/commands/upgrade.ts
  • tests/commands/upgrade-dispatch.test.ts
  • src/helpers/binary-upgrade.ts
  • tests/helpers/binary-upgrade-archive.test.ts
  • tests/commands/upgrade-action.test.ts
📚 Learning: 2026-08-04T19:58:05.877Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 543
File: src/helpers/copilot-user-settings.ts:0-0
Timestamp: 2026-08-04T19:58:05.877Z
Learning: In archgate/cli TypeScript code, use `Bun.file(path).exists()` only to check whether a file exists; it must not be used for directory existence checks. For helpers such as `isCopilotAvailable()` that need to detect a configuration directory, use an appropriate directory-aware check such as `existsSync` from `node:fs`.

Applied to files:

  • tests/helpers/binary-upgrade.test.ts
  • src/commands/upgrade.ts
  • tests/commands/upgrade-dispatch.test.ts
  • src/helpers/binary-upgrade.ts
  • tests/helpers/binary-upgrade-archive.test.ts
  • tests/commands/upgrade-action.test.ts
📚 Learning: 2026-08-05T06:56:33.435Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 546
File: tests/integration/stream-guards.test.ts:3-9
Timestamp: 2026-08-05T06:56:33.435Z
Learning: When reviewing GEN-004 comment-block limits in the Archgate CLI repository, count only narrative prose lines within a block comment. Do not count a closing delimiter such as `*/` as a prose line; for example, in `tests/integration/stream-guards.test.ts`, Lines 4–8 contain five prose lines while Line 9 contains only the delimiter.

Applied to files:

  • tests/helpers/binary-upgrade.test.ts
  • src/commands/upgrade.ts
  • tests/commands/upgrade-dispatch.test.ts
  • src/helpers/binary-upgrade.ts
  • tests/helpers/binary-upgrade-archive.test.ts
  • tests/commands/upgrade-action.test.ts
📚 Learning: 2026-08-06T21:09:28.014Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 561
File: src/helpers/binary-upgrade.ts:239-287
Timestamp: 2026-08-06T21:09:28.014Z
Learning: In archgate/cli TypeScript code, follow ARCH-007 for Bun subprocess stream capture; ARCH-017 does not govern subprocess pipe handling. When Bun.spawn() uses piped stdout or stderr, use a shared capture helper where practical, consume configured streams concurrently to avoid deadlocks, and verify the subprocess exit code before trusting captured output.

Applied to files:

  • tests/helpers/binary-upgrade.test.ts
  • src/commands/upgrade.ts
  • tests/commands/upgrade-dispatch.test.ts
  • src/helpers/binary-upgrade.ts
  • tests/helpers/binary-upgrade-archive.test.ts
  • tests/commands/upgrade-action.test.ts
📚 Learning: 2026-07-25T15:44:40.668Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 501
File: .archgate/adrs/ARCH-005-testing-standards.md:0-0
Timestamp: 2026-07-25T15:44:40.668Z
Learning: In Archgate CLI test code governed by ARCH-007, only allow `Bun.$` in test suites that are explicitly restricted to a single platform. Any cross-platform test that runs on Linux, macOS, and Windows must avoid `Bun.$` and instead use array-based `Bun.spawn`. For shared git setup used by tests, import and use the `git()` helper from `tests/test-utils.ts` rather than duplicating git setup logic.

Applied to files:

  • tests/helpers/binary-upgrade.test.ts
  • tests/commands/upgrade-dispatch.test.ts
  • tests/helpers/binary-upgrade-archive.test.ts
  • tests/commands/upgrade-action.test.ts
📚 Learning: 2026-08-05T16:54:13.117Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 550
File: tests/commands/adr/domain/remove.test.ts:21-24
Timestamp: 2026-08-05T16:54:13.117Z
Learning: In the Archgate CLI test suite, continue using `z.object` for JSON output schemas unless a repository-wide testing policy explicitly adopts `z.strictObject`. Do not introduce strict CLI-output schema enforcement as an isolated change in a coverage-focused pull request; require coordinated updates and policy agreement across affected tests.

Applied to files:

  • tests/helpers/binary-upgrade.test.ts
  • tests/commands/upgrade-dispatch.test.ts
  • tests/helpers/binary-upgrade-archive.test.ts
  • tests/commands/upgrade-action.test.ts
📚 Learning: 2026-07-25T22:03:14.216Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 501
File: .archgate/adrs/ARCH-002-error-handling.md:0-0
Timestamp: 2026-07-25T22:03:14.216Z
Learning: In Archgate boundary-wrapped CLI command actions (the handlers that rely on `handleCommandError()` for user-facing error output), expected-failure guards should signal user errors by throwing `new UserError(<message/details>)` rather than directly calling `logError()` followed by `exitWith(1)`. This keeps user-facing logging and the exit path centralized in `handleCommandError()`. For normal/computed command outcomes (e.g., `const exitCode = getExitCode(await runChecks(...))`), use `await exitWith(exitCode)` instead of calling `process.exit(exitCode)` so telemetry/Sentry flushing and outcome tagging still run.

Applied to files:

  • tests/helpers/binary-upgrade.test.ts
  • src/commands/upgrade.ts
  • tests/commands/upgrade-dispatch.test.ts
  • src/helpers/binary-upgrade.ts
  • tests/helpers/binary-upgrade-archive.test.ts
  • tests/commands/upgrade-action.test.ts
📚 Learning: 2026-07-25T23:21:49.190Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 512
File: tests/engine/git-files.test.ts:98-100
Timestamp: 2026-07-25T23:21:49.190Z
Learning: When reviewing archgate/cli for ARCH-006 (per its ADR frontmatter), only enforce the production-dependency policy scoped to package.json. Do not treat test-only refactors or relocated `node:fs` fixture writes as an ARCH-006 violation (since ARCH-006 does not govern test-file I/O API selection). If there’s a broader/test-wide refactor that would migrate fixture writing to `Bun.write()`, evaluate it separately under the appropriate in-scope rule.

Applied to files:

  • tests/helpers/binary-upgrade.test.ts
  • tests/commands/upgrade-dispatch.test.ts
  • tests/helpers/binary-upgrade-archive.test.ts
  • tests/commands/upgrade-action.test.ts
📚 Learning: 2026-07-27T16:05:38.683Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 536
File: tests/commands/adr/sync-strict.test.ts:173-173
Timestamp: 2026-07-27T16:05:38.683Z
Learning: In this Bun + TypeScript repo, for rejected-promise assertions use the unawaited form: `expect(promise).rejects.toThrow(...)`. Do NOT add `await` to `expect(promise).rejects.toThrow(...)` (Bun’s types model this as `void`), because it will violate the type-aware oxlint rules `typescript(await-thenable)` and `typescript(no-confusing-void-expression)`. Only request an `await` if the repo adopts a typed, lint-compliant assertion helper or Bun’s typings change.

Applied to files:

  • tests/helpers/binary-upgrade.test.ts
  • tests/commands/upgrade-dispatch.test.ts
  • tests/helpers/binary-upgrade-archive.test.ts
  • tests/commands/upgrade-action.test.ts
📚 Learning: 2026-08-05T16:54:50.574Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 550
File: tests/helpers/plugin-install-cursor-hooks.test.ts:44-44
Timestamp: 2026-08-05T16:54:50.574Z
Learning: In this repository, every TypeScript module under `src/` must have a matching `<module-name>.test.ts` file under the mirrored `tests/` directory, as required by ARCH-005. Supplemental behavior-suffixed sibling test files are allowed only when the matching parent test file exists. Use such siblings to keep individual test files below the 500-line oxlint limit.

Applied to files:

  • tests/helpers/binary-upgrade.test.ts
  • tests/commands/upgrade-dispatch.test.ts
  • tests/helpers/binary-upgrade-archive.test.ts
  • tests/commands/upgrade-action.test.ts
📚 Learning: 2026-07-25T22:03:22.236Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 501
File: .archgate/adrs/ARCH-016-cli-subcommand-documentation-accuracy.md:64-67
Timestamp: 2026-07-25T22:03:22.236Z
Learning: When reviewing CLI subcommand documentation, don’t rely solely on the companion ARCH-016 enforcement rule’s limited path coverage (it only checks `src/commands/<parent>/*.ts` and `src/commands/<parent>/*/index.ts`). Manually verify that subcommands documented by convention in deeper paths (e.g., `src/commands/<parent>/**/add.ts` or `src/commands/adr/domain/add.ts`) have the required documentation, since future nested subcommands can drift without automated detection (tracked by ARCH-015 / GitHub `#503`).

Applied to files:

  • src/commands/upgrade.ts
📚 Learning: 2026-06-11T12:50:28.661Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 406
File: .claude/agent-memory/archgate-developer/feedback_prefer_tests_over_adr_rules.md:8-18
Timestamp: 2026-06-11T12:50:28.661Z
Learning: In `archgate/cli`, for markdown files under `.claude/agent-memory/`, follow the established convention: use YAML frontmatter (with a `name:` field used as the document title) and do not require a top-level `#` (H1) heading. During code review, do not flag missing first-line/first-top-level H1 headings (e.g., MD041) for these agent-memory files since markdownlint is not part of the repo’s `bun run validate` lint pipeline (oxlint/oxfmt only).

Applied to files:

  • .claude/agent-memory/archgate-developer/MEMORY.md
  • .claude/agent-memory/archgate-developer/feedback_verify_agent_claims.md
📚 Learning: 2026-07-25T00:05:20.592Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 496
File: .claude/agent-memory/archgate-developer/project_test_isolation_gotchas.md:10-10
Timestamp: 2026-07-25T00:05:20.592Z
Learning: When reviewing documentation/agent-memory entries under `.claude/agent-memory/**`, do not enforce GEN-004’s “forward-only” comment/narrative requirement. These entries are allowed to keep historical/past-tense incident narratives and dated markers (e.g., `Found YYYY-MM-DD`) because the context is intended to help future agents evaluate edge cases. Outside this scope, GEN-004’s forward-only rule should still apply.

Applied to files:

  • .claude/agent-memory/archgate-developer/MEMORY.md
  • .claude/agent-memory/archgate-developer/feedback_verify_agent_claims.md
📚 Learning: 2026-07-02T16:03:33.031Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 446
File: src/helpers/session-context-opencode.ts:81-100
Timestamp: 2026-07-02T16:03:33.031Z
Learning: For synchronous helper functions that use Bun’s sqlite sync API (i.e., they must remain synchronous), it’s acceptable to use `existsSync` from `node:fs` to check whether the SQLite database file exists. Avoid using `Bun.file(path).exists()` for this purpose because it’s async and would force the helper to become async (no equivalent synchronous Bun alternative). If the DB file is missing, throw/return a clear, actionable "No database found" error (per ARCH-006) rather than letting the sqlite open fail with a generic DB-open error.

Applied to files:

  • src/helpers/binary-upgrade.ts
🔇 Additional comments (10)
.claude/agent-memory/archgate-developer/MEMORY.md (1)

28-28: LGTM!

Also applies to: 35-36

.claude/agent-memory/archgate-developer/feedback_verify_agent_claims.md (1)

25-27: LGTM!

src/helpers/binary-upgrade.ts (4)

124-129: Use an accurate ARCH-007 rationale.

Line 127 states that current Bun leaves the child blocked and prevents proc.exited from resolving. The recorded verification found that current Bun buffers these pipes. State that ARCH-007 requires concurrent capture without asserting this unsupported runtime behavior.

Based on learnings, current Bun buffers piped output while ARCH-007 still requires concurrent capture.

Source: Learnings


130-166: LGTM!


269-312: LGTM!


328-333: LGTM!

src/commands/upgrade.ts (1)

281-301: LGTM!

tests/helpers/binary-upgrade-archive.test.ts (1)

128-157: LGTM!

Also applies to: 223-262, 320-326

tests/commands/upgrade-action.test.ts (1)

19-20: LGTM!

Also applies to: 48-52, 72-75, 104-104, 138-140

tests/commands/upgrade-dispatch.test.ts (1)

467-470: LGTM!

Comment thread src/helpers/binary-upgrade.ts
Comment thread tests/helpers/binary-upgrade.test.ts Outdated
existsSync follows symlinks and accepts directories, so an archive member
that is either passed the post-extraction check. replaceBinary renames
without following, which would leave ~/.archgate/bin/ holding a symlink
aimed wherever the archive chose. lstat rejects both.

The zip extraction test removes the directory the download created, which
is not the fixture's own tmpDir the surrounding finally already handles.

Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
@rhuanbarreto
rhuanbarreto merged commit 8e1c5db into main Aug 7, 2026
34 checks passed
@rhuanbarreto
rhuanbarreto deleted the claude/issues-554-555-556-8fccd9 branch August 7, 2026 06:45
@archgatebot archgatebot Bot mentioned this pull request Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant