Skip to content

perf(engine): match globs in memory against git-tracked files#474

Merged
rhuanbarreto merged 5 commits into
mainfrom
perf/engine-in-memory-glob-matching
Jul 13, 2026
Merged

perf(engine): match globs in memory against git-tracked files#474
rhuanbarreto merged 5 commits into
mainfrom
perf/engine-in-memory-glob-matching

Conversation

@rhuanbarreto

@rhuanbarreto rhuanbarreto commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Problem

archgate check pegged CPU on real-world projects. Root cause: every ADR scope resolution and every rule-facing file listing (ctx.glob, ctx.grepFiles) walked the filesystem with Bun.Glob#scan() and filtered against the git-tracked set only afterwards — the traversal cost was paid in full before the filter ran. On a project with 333 tracked files and 43,645 filesystem entries (.venv/, data/), one check performed ~70 full walks (one per ADR scope + one per rule glob), all in parallel.

Fix

  1. In-memory matching (src/engine/glob-utils.ts, new): match patterns against the git ls-files --cached --others --exclude-standard set (minus --deleted) via Bun.Glob#match(). match() handles dot-prefixed segments without options and brace-with-/ groups natively (Bun.Glob.scan() silently returns empty results for brace patterns containing path separators oven-sh/bun#32596 affects scanning only). Scanning is now fallback-only (non-git project / respectGitignore: false), confined to glob-utils.ts/git-files.ts. The rule-sandbox contract (no ../absolute patterns, including brace-expanded alternatives) is validated identically on both paths and pinned by tests.
  2. Per-run caches (runner.ts): glob results (keyed by pattern + tracked mode) and file text (keyed by abs path) shared across all rule contexts as promises; glob arrays copied on return so one rule's mutation can't corrupt another's view; readJSON deliberately uncached (mutable-object leakage).
  3. Subprocess race fix (git-files.ts): Promise.allSettled instead of Promise.all for the two git ls-files spawns — a rejection (non-git dir) previously abandoned the in-flight sibling, whose live cwd handle locks the directory on Windows (EBUSY on temp-dir cleanup).

Governance

  • New ADR ARCH-023 (Engine File Listing via In-Memory Git-Tracked Matching) with a companion rule confining engine scan() call sites to the two fallback modules — dogfooded clean and fire-tested against a planted violation.
  • ARCH-007 extended with the Promise.allSettled Do/Don't.

Results

Metric Before After
Affected project engine time 1147ms 160ms (7.2x)
Affected project wall clock 2.55s ~0.9s
This repo's check 564ms 186ms

Follow-up (separate PR)

The remaining ~700ms of wall clock is Bun startup + re-transpiling/parsing every .rules.ts per invocation — planned content-hash cache under ~/.archgate/.

Validation

bun run validate passes (lint, typecheck, format, 1454 tests, ADR check 44/44, knip, build).

@cursor

cursor Bot commented Jul 13, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_923ba3cb-cf9c-471e-92d2-58ebbf982883)

@coderabbitai

coderabbitai Bot commented Jul 13, 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: 48 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

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: 3085efc6-b39f-4c4f-913b-230bd18cc28c

📥 Commits

Reviewing files that changed from the base of the PR and between b742068 and c9bcf7d.

📒 Files selected for processing (2)
  • .archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.md
  • .claude/settings.json
📝 Walkthrough

Walkthrough

The change adds ARCH-023 and an enforcement rule for restricting filesystem glob scans. Engine glob utilities now validate and expand patterns, match against Git-tracked files, and provide a scan fallback. Git file resolution subtracts deleted worktree files and coordinates subprocesses with Promise.allSettled. Rule execution now shares per-run glob and file-text caches while isolating returned glob arrays. Tests cover matching, fallback behavior, sandboxing, deleted files, and cache isolation.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: matching engine globs in memory against git-tracked files for performance.
Description check ✅ Passed The description matches the changeset and explains the performance fix, caches, subprocess tweak, and ADR updates.
Docstring Coverage ✅ Passed Docstring coverage is 91.67% which is sufficient. The required threshold is 80.00%.
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.

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 Jul 13, 2026

Copy link
Copy Markdown

Deploying archgate-cli with  Cloudflare Pages  Cloudflare Pages

Latest commit: c9bcf7d
Status: ✅  Deploy successful!
Preview URL: https://1f5a55f7.archgate-cli.pages.dev
Branch Preview URL: https://perf-engine-in-memory-glob-m.archgate-cli.pages.dev

View logs

@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Code Coverage

Metric Value
Lines 90.8% (7821 / 8612)
Threshold 90% minimum — met
Platforms Linux + Windows

Full HTML report available in workflow artifacts.

Per-directory breakdown
Directory Coverage Lines
src/commands/ 89.0% 1947 / 2188
src/engine/ 92.1% 1733 / 1881
src/formats/ 98.7% 148 / 150
src/helpers/ 90.9% 3993 / 4393

Every ADR scope resolution and rule-facing file listing (ctx.glob,
ctx.grepFiles) walked the filesystem with Bun.Glob#scan() and filtered
against the git-tracked set only afterwards. On a project with 333
tracked files and 43,645 filesystem entries (.venv/, data/), a single
check performed ~70 full walks, pegging CPU: engine time 1147ms.

- Add src/engine/glob-utils.ts: match patterns in memory against the
  git ls-files set via Bun.Glob#match() (handles dot-paths without
  options and brace-with-/ groups natively, unlike scan). Scanning is
  now fallback-only (non-git / respectGitignore: false). The sandbox
  contract (no .. / absolute patterns, incl. brace alternatives) is
  validated identically on both paths.
- Subtract git ls-files --deleted from the tracked set so in-memory
  matching only sees files that exist on disk; use Promise.allSettled
  so a rejected spawn cannot abandon a still-running sibling whose cwd
  handle locks the directory on Windows (EBUSY).
- Add per-run caches in runChecks (glob results + file text, shared
  promises; glob arrays copied on return; readJSON deliberately
  uncached to avoid mutable-object leakage between rules).
- New ADR ARCH-023 with a companion rule confining engine scan call
  sites to glob-utils.ts/git-files.ts; extend ARCH-007 with the
  allSettled lesson.

Measured on the affected project: engine 1147ms -> 160ms (7.2x), wall
2.55s -> ~0.9s. This repo's own check: 564ms -> 186ms.

Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
@rhuanbarreto rhuanbarreto force-pushed the perf/engine-in-memory-glob-matching branch from 3497c23 to d0d4aad Compare July 13, 2026 15:09
@cursor

cursor Bot commented Jul 13, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_f12cfd4f-9075-46e7-8c94-68bf474c1f2b)

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

cursor Bot commented Jul 13, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_00cfe663-0c8d-497d-b5cf-c1fa79d1451c)

@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: 1

🤖 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 `@tests/engine/glob-utils.test.ts`:
- Around line 101-112: Remove the 15-second timeout override from the “tracked
set excludes files deleted from the worktree” test, or increase it above the
project’s 60-second global timeout. Keep the test behavior and assertions
unchanged, and only retain an override if this genuinely slow subprocess-based
test requires more than 60 seconds.
🪄 Autofix (Beta)

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: 3a6bc20a-4742-4081-b679-faa8b819b53f

📥 Commits

Reviewing files that changed from the base of the PR and between 83292a5 and 3497c23.

📒 Files selected for processing (11)
  • .archgate/adrs/ARCH-007-cross-platform-subprocess-execution.md
  • .archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.md
  • .archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.rules.ts
  • .claude/agent-memory/archgate-developer/MEMORY.md
  • .claude/agent-memory/archgate-developer/project_rules_engine_internals.md
  • src/engine/git-files.ts
  • src/engine/glob-utils.ts
  • src/engine/runner.ts
  • tests/engine/expand-brace-pattern.test.ts
  • tests/engine/glob-utils.test.ts
  • tests/engine/runner.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Smoke Test (Windows) / Windows
  • GitHub Check: Cloudflare Pages
🧰 Additional context used
📓 Path-based instructions (14)
tests/**/*.test.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-005-testing-standards.md)

tests/**/*.test.ts: Use Bun's built-in test runner (bun:test) for all test files, and place tests under tests/ mirroring the src/ directory structure with <module-name>.test.ts naming.
Use temp directories (mkdtemp) for filesystem tests, and clean them up in afterEach or afterAll.
Close external SDK instances (servers, clients, transports, connections) in afterEach or afterAll, managing their lifecycle in hooks rather than inside individual test bodies.
When a test creates a temporary git repository and runs git commit, configure local user.email and user.name immediately after git init.
Test public module interfaces, not private implementation details.
Use descriptive test names that explain the expected behavior.
Every runnable test()/it() must contain at least one expect() assertion; smoke tests must make the contract explicit with expect(() => fn()).not.toThrow() or await expect(promise).resolves.toBeUndefined().
Use test.skip, test.skipIf, or test.todo for intentionally empty or disabled tests; do not use bare return or empty callbacks to skip work.
If the first expect() is being added to a previously assertion-less test file, add expect to the bun:test import.
When mocking fetch in tests, assign directly to globalThis.fetch and restore the original or use mock.restore() afterward.
Wrap spyOn() and inline mockImplementation() usage in try/finally, or create and restore spies in hooks, so mockRestore() always runs.
Only raise a per-test timeout above the global bun test --timeout 60000; never set a shorter per-test timeout.
Mock first-party modules with import * as mod plus spyOn(mod, "fn"), not mock.module().
When a test needs to redirect user-scope paths, mock os.homedir() instead of relying on HOME/Bun.env.HOME; restore the spy in test hooks.
Do not depend on network access in unit tests.
Do not leave temp files after test runs.
Do not leave external SDK instances open after tests...

Files:

  • tests/engine/expand-brace-pattern.test.ts
  • tests/engine/runner.test.ts
  • tests/engine/glob-utils.test.ts
**/*.{ts,tsx,js,jsx,mjs,cjs}

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

**/*.{ts,tsx,js,jsx,mjs,cjs}: Use Bun built-ins for file I/O (Bun.file, Bun.write), HTTP, subprocess execution (Bun.spawn), globbing (Bun.Glob), and testing (bun:test).
Do not use Node.js-specific APIs when Bun alternatives exist; for example, use Bun.file() instead of fs.readFile() for simple reads.
Prefer node: built-in modules such as node:util, node:path, and node:fs over npm alternatives.
Do not use utility libraries for single functions, such as importing lodash for one helper like pick.
Do not use path aliases (tsconfig paths); use relative imports with Bun's native module resolution.
Do not install packages globally during development; use bunx for one-off tools.

Files:

  • tests/engine/expand-brace-pattern.test.ts
  • tests/engine/runner.test.ts
  • src/engine/glob-utils.ts
  • tests/engine/glob-utils.test.ts
  • src/engine/git-files.ts
  • src/engine/runner.ts
tests/**/*.ts

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

In test files, use _resetPlatformCache() to simulate different platforms instead of mocking or mutating process.platform directly.

Files:

  • tests/engine/expand-brace-pattern.test.ts
  • tests/engine/runner.test.ts
  • tests/engine/glob-utils.test.ts
{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/engine/expand-brace-pattern.test.ts
  • tests/engine/runner.test.ts
  • src/engine/glob-utils.ts
  • tests/engine/glob-utils.test.ts
  • src/engine/git-files.ts
  • src/engine/runner.ts
**

⚙️ CodeRabbit configuration file

**: # CLAUDE.md

Archgate is a CLI tool for AI governance via Architecture Decision Records (ADRs) — combining human-readable docs with machine-checkable rules. The CLI dogfoods itself via ADRs in .archgate/adrs/. AI features are delivered as a Claude Code plugin (../plugins/claude-code), not via direct API calls.

Technology Stack

  • Runtime: Bun (>=1.2.21) — not Node.js compatible
  • Language: TypeScript (strict mode, ESNext, ES modules)
  • CLI framework: Commander.js (@commander-js/extra-typings)
  • Linter: Oxlint | Formatter: Oxfmt | Dead exports: Knip | Commits: Conventional Commits

Commands

bun run src/cli.ts <command>  # run CLI locally
bun run lint                  # oxlint
bun run typecheck             # tsc --build
bun run format                # oxfmt --write
bun run format:check          # oxfmt --check
bun run test                  # all tests (not bare `bun test` — picks up --timeout; see GEN-003)
bun run knip                  # dead export detection
bun run validate              # MANDATORY: lint + typecheck + format:check + test + ADR check + knip + build check
bun run build:check            # verify build compiles (CI builds binaries via release workflow)
bun run commit                # conventional commit wizard

Validation Gate

bun run validate must pass before any task is considered complete. Fail-fast pipeline: lint → typecheck → format:check → test → ADR check → knip → build check. Mirrors CI in .github/workflows/code-pull-request.yml.

Git Hooks (Git 2.54+)

Config-based hooks in .githooks run validation locally before commits and pushes:

  • pre-commit: lint + typecheck + format:check (~15s)
  • pre-push: full bun run validate (~60s, mirrors CI)

Activate once per clone:

git config --local include.path ../.githooks

Opt out of a specific hook: git config --local hook.<name>.enabled false. Skip all hooks for a single commit: git commit --no-verify.

#...

Files:

  • tests/engine/expand-brace-pattern.test.ts
  • tests/engine/runner.test.ts
  • src/engine/glob-utils.ts
  • tests/engine/glob-utils.test.ts
  • src/engine/git-files.ts
  • src/engine/runner.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/engine/expand-brace-pattern.test.ts
  • tests/engine/runner.test.ts
  • src/engine/glob-utils.ts
  • tests/engine/glob-utils.test.ts
  • src/engine/git-files.ts
  • src/engine/runner.ts
.archgate/adrs/**

⚙️ CodeRabbit configuration file

.archgate/adrs/**: ---
id: ARCH-001
title: Command Structure
domain: architecture
rules: true
files: ["src/commands/**/*.ts"]

Context

The CLI needs a consistent pattern for defining and registering commands. As the command surface grows (init, check, adr, review-context, session-context, upgrade, clean), the registration mechanism must scale without introducing hidden coupling or making the dependency graph opaque.

Alternatives considered:

  • Auto-discovery via executableDir() — Commander.js supports automatic command discovery by scanning a directory for executable files. This eliminates manual imports but hides the dependency graph: adding or removing a command has no type-checked reference, making dead command detection impossible. It also requires each command to be a standalone executable, which prevents in-process testing and forces separate process spawning for every subcommand invocation.
  • Plugin-based registration — A plugin system where commands register themselves via a manifest or hook (similar to Oclif or Clipanion). This adds flexibility for third-party extensions but introduces significant complexity for an internal CLI with a known, finite set of commands. The indirection makes it harder to trace which code handles which command.
  • Single-file command map — Define all commands in a single file as a map of name-to-handler. Simple but creates a monolithic file that grows with every command, making merge conflicts frequent and readability poor.

The explicit register pattern strikes the right balance: each command owns its registration logic, the entry point makes all commands visible at a glance, and in-process execution enables straightforward testing without process spawning.

Decision

Commands live in src/commands/ and export a register*Command(program) function. The main entry point (src/cli.ts) explicitly imports and calls each register function. Subcommands (e.g., adr create, adr list) use nested directories wi...

Files:

  • .archgate/adrs/ARCH-007-cross-platform-subprocess-execution.md
  • .archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.rules.ts
  • .archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.md
src/**/*.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-003-output-formatting.md)

src/**/*.ts: Use styleText(format, text) from node:util for all terminal colors and formatting in CLI source files; do not use raw ANSI escape codes or third-party color libraries.
Commands that produce structured results and support --json must emit machine-readable JSON to stdout with no colors or decorative formatting.
When a command supports --json, use formatJSON() from src/helpers/output.ts for JSON serialization, and pass forcePretty: true when the user explicitly provided --json.
Use isAgentContext() from src/helpers/output.ts to enable auto-JSON behavior for commands that support both human-readable and JSON output modes.
CLI output must not include emoji; use text symbols and colors instead.
Send normal command output to stdout with console.log(), and send errors, warnings, and debug messages to stderr via logError(), logWarn(), and logDebug().
Keep CLI output concise and scannable by using whitespace and alignment instead of long text blocks.
Respect NO_COLOR automatically by relying on styleText; do not add custom color-environment handling in CLI code.
Do not output progress spinners unless there is a TTY check.
Do not assume piped output means agent context when CI is set; CI runners should still receive human-readable output.

src/**/*.ts: Do not re-export symbols from another module in any source file; statements like export { X } from "./other" and export type { X } from "./other" are forbidden.
Import symbols directly from the module that defines them; do not import from a directory path such as ../formats expecting implicit index.ts resolution.

Make large production thresholds injectable via an optional parameter that defaults to the module constant, so tests can supply a small value instead of generating huge fixtures.

src/**/*.ts: Use await Bun.file(path).json() when reading JSON files in Bun TypeScript source code; do not use JSON.parse(await Bun.file(path).text()) or `JSON.parse(fs.readFile...

Files:

  • src/engine/glob-utils.ts
  • src/engine/git-files.ts
  • src/engine/runner.ts
src/**/!(*platform).ts

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

src/**/!(*platform).ts: In src/ TypeScript source files, do not read process.platform directly; use src/helpers/platform.ts (isWindows(), isMacOS(), isLinux(), isWSL(), getPlatformInfo()) for all platform detection.
Use the centralized platform helper instead of duplicating OS/WSL detection logic inline anywhere in src/ TypeScript source.
When behavior differs between Linux and Windows, account for WSL by using isWSL() rather than assuming `

Files:

  • src/engine/glob-utils.ts
  • src/engine/git-files.ts
  • src/engine/runner.ts
src/{helpers,engine}/**/*.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-002-error-handling.md)

Do not use console.log(), console.warn(), or console.info() directly in helper or engine files; use logInfo() or logWarn() instead.

Files:

  • src/engine/glob-utils.ts
  • src/engine/git-files.ts
  • src/engine/runner.ts
src/engine/**/*.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.md)

src/engine/**/*.ts: The rules engine must list project files by matching glob patterns in memory against the git-tracked file set, rather than walking the filesystem.
The glob pattern and every brace-expanded alternative must pass safeGlob validation before matching or scanning.
Per-run caches must share glob results and file text across rule contexts; cached glob arrays must be copied before being returned, while readJSON results must not be cached.
Route new engine file listings through listMatchingFiles for sandboxed rule-facing patterns or matchTrackedFiles for trusted ADR frontmatter patterns.
Do not filter filesystem scan results against the tracked set as a substitute for in-memory matching.
Do not hardcode ignore directories such as node_modules/ or .venv/; use Git's standard ignore handling through git ls-files.

Files:

  • src/engine/glob-utils.ts
  • src/engine/git-files.ts
  • src/engine/runner.ts
src/engine/glob-utils.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.md)

matchTrackedFiles and listMatchingFiles must perform in-memory matching using Bun.Glob#match().

Files:

  • src/engine/glob-utils.ts
src/engine/{glob-utils,git-files}.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.md)

src/engine/{glob-utils,git-files}.ts: Bun.Glob#scan() is permitted only as a fallback when no tracked file set exists, and scan call sites must remain confined to glob-utils.ts and git-files.ts.
Remaining Bun.Glob#scan() fallback calls must pass { dot: true }.

Files:

  • src/engine/glob-utils.ts
  • src/engine/git-files.ts
src/engine/git-files.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.md)

src/engine/git-files.ts: getGitTrackedFiles must define the file universe using git ls-files --cached --others --exclude-standard minus git ls-files --deleted.
Pass the tracked file set from getGitTrackedFiles whenever the target is a Git repository and respectGitignore is not false.

Files:

  • src/engine/git-files.ts
src/engine/runner.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-022-ast-aware-rule-context.md)

src/engine/runner.ts: Implement ctx.ast(path, language) inside createRuleContext() with all dispatch hidden from rule authors.
For language: "typescript" and "javascript", ctx.ast() must reuse the existing in-process meriyah parser; no subprocess may be spawned for these branches.
Factor the duplicated parseModule() logic into a shared helper that is used by both rule-scanner.ts and the ctx.ast() TypeScript/JavaScript branch.
For language: "python" and "ruby", ctx.ast() must use Bun.spawn with array-based arguments only, with no shell interpolation.
Before any Python/Ruby interpreter is invoked, ctx.ast() must run the guardrail sequence in order: path safety, language plausibility check, interpreter availability probe, then guarded invocation.
ctx.ast() must use the same safePath() sandboxing as readFile/glob before accessing a target file.
ctx.ast() must reject files whose extension and/or leading content do not plausibly match the requested language before running an interpreter.
ctx.ast() must probe interpreter availability once per check invocation, cache the result, and reuse the first working candidate executable name.
On Windows, the interpreter probe must consider platform-appropriate candidates in order (including py via isWindows()); on non-Windows, it must use the non-Windows candidate order.
The Python branch must run in isolated mode (python -I -c ...) to prevent the working directory from shadowing standard-library imports.
The Python and Ruby serializers must strip a leading UTF-8 BOM before parsing.
ctx.ast() must throw on missing interpreter or parse failure, and must not return null or any other sentinel value.
The thrown error messages from ctx.ast() must distinguish interpreter-unavailable failures from parse-failure errors.
ctx.ast() must not expose Bun.spawn, child_process, or any other raw subprocess primitive on RuleContext; it is the only sanctioned tooling entrypoint.
`ctx.ast(...

Files:

  • src/engine/runner.ts
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: archgate/cli

Timestamp: 2026-07-13T15:03:45.533Z
Learning: Any exception allowing `Bun.$` in a test must be documented with a comment explaining why it is acceptable.
📚 Learning: 2026-07-11T13:03:15.386Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 467
File: .archgate/adrs/ARCH-011-consistent-project-root-resolution.md:0-0
Timestamp: 2026-07-11T13:03:15.386Z
Learning: For Markdown files formatted by oxfmt (especially ADRs), avoid inline code spans that contain escaped backticks, e.g. `\`...\`` inside a single `` `...` `` span. oxfmt may mis-parse these and, on re-format, can collapse spaces after later inline code spans on the same line, effectively removing any manually re-added spacing. Instead, rephrase the text so the message stays plain quoted text, and put any embedded command/fragment that needs code formatting (e.g., `archgate init`) in its own separate inline code span; keep surrounding punctuation/spacing outside the code span.

Applied to files:

  • .archgate/adrs/ARCH-007-cross-platform-subprocess-execution.md
  • .archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.md
📚 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/project_rules_engine_internals.md
🪛 LanguageTool
.archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.md

[locale-violation] ~12-~12: In American English, ‘afterward’ is the preferred variant. ‘Afterwards’ is more commonly used in British English and other dialects.
Context: ...sults against the git-tracked file set afterwards. The walk cost was paid in full before...

(AFTERWARDS_US)


[style] ~60-~60: Consider an alternative for the overused word “exactly”.
Context: ...source of truth:** The file universe is exactly what git considers part of the project ...

(EXACTLY_PRECISELY)

🪛 markdownlint-cli2 (0.23.0)
.claude/agent-memory/archgate-developer/project_rules_engine_internals.md

[warning] 8-8: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

🔇 Additional comments (10)
.archgate/adrs/ARCH-007-cross-platform-subprocess-execution.md (1)

61-61: LGTM!

Also applies to: 70-70

.archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.md (1)

21-21: 🎯 Functional Correctness

Pin the Bun.Glob#match() dot-segment claim with a regression test — it's restated in two files but only asserted, not shown as tested here.

Both files state that Bun.Glob#match() matches dot-prefixed path segments with no option required, in contrast to scan()'s dot: true requirement (ARCH-020). Public Bun docs corroborate that match(path: string): boolean exposes no options parameter at all (unlike ScanOptions.dot), which is consistent with the claim, but no official example explicitly demonstrates a **-style pattern matching a dot-prefixed segment via .match(). Since this behavior is the entire justification for skipping dot: true on the in-memory fast path, a future Bun release changing it would silently reintroduce the exact class of bug ARCH-020 exists to prevent.

  • .archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.md#L21-L21: consider adding a one-line pointer to where this is regression-tested (e.g. tests/engine/glob-utils.test.ts), or note it explicitly as an assumption to re-verify on Bun upgrades.
  • .archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.md#L59-L59: same — the "Correctness" consequence bullet rests on this same assumption.
  • .claude/agent-memory/archgate-developer/project_rules_engine_internals.md#L9-L9: the "verified 2026-07-13, Bun 1.3.14" note is good practice; consider recording how it was verified (e.g. link to the specific test) so the verification is reproducible on future Bun bumps.
.archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.rules.ts (1)

8-54: LGTM!

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

45-45: LGTM!

tests/engine/runner.test.ts (1)

85-119: LGTM!

src/engine/glob-utils.ts (1)

1-121: LGTM!

src/engine/git-files.ts (1)

8-8: LGTM!

Also applies to: 51-76, 111-135

tests/engine/glob-utils.test.ts (1)

1-100: LGTM!

Also applies to: 113-113

src/engine/runner.ts (1)

35-35: LGTM!

Also applies to: 89-108, 136-165, 214-214, 280-287, 307-308, 320-320, 353-353, 424-427, 465-466

tests/engine/expand-brace-pattern.test.ts (1)

8-10: LGTM!

Comment thread tests/engine/glob-utils.test.ts Outdated
Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
@cursor

cursor Bot commented Jul 13, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_80139812-4849-4268-bdd4-0908adb8aa4c)

… PRs

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

cursor Bot commented Jul 13, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_b2c5be8a-3ee9-4e3d-906e-9e2a4df9bd15)

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/engine/runner.ts (1)

128-138: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

createRuleContext now takes 9 positional parameters.

Readability/maintainability nit: with caches added, callers must track 9 positional args in the right order (trackedFiles, interpreterCache, caches are easy to transpose since they're all reference types). Consider bundling the run-scoped values (interpreterCache, caches, trackedFiles) into a single options object in a future pass.

Also applies to: 457-467

🤖 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 `@src/engine/runner.ts` around lines 128 - 138, Refactor createRuleContext and
its callers to accept a single options object containing the run-scoped values
trackedFiles, interpreterCache, and caches instead of separate positional
parameters. Update every invocation, including the referenced call site, to pass
named properties while preserving the existing RuleContext behavior.
🤖 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
@.archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.md:
- Around line 28-31: Clarify item `#4` so “both paths” explicitly refers only to
the two internal branches of listMatchingFiles: tracked/in-memory matching and
scan fallback. State that safeGlob validation applies to the pattern and every
brace-expanded alternative in those branches, while
resolveScopedFiles/matchTrackedFiles for trusted ADR frontmatter patterns remain
outside this sandbox requirement.

---

Outside diff comments:
In `@src/engine/runner.ts`:
- Around line 128-138: Refactor createRuleContext and its callers to accept a
single options object containing the run-scoped values trackedFiles,
interpreterCache, and caches instead of separate positional parameters. Update
every invocation, including the referenced call site, to pass named properties
while preserving the existing RuleContext behavior.
🪄 Autofix (Beta)

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: 97aaacf7-f791-4316-b112-443b40ba32cd

📥 Commits

Reviewing files that changed from the base of the PR and between 3497c23 and b742068.

📒 Files selected for processing (12)
  • .archgate/adrs/ARCH-007-cross-platform-subprocess-execution.md
  • .archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.md
  • .archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.rules.ts
  • .claude/agent-memory/archgate-developer/MEMORY.md
  • .claude/agent-memory/archgate-developer/feedback_public_repo_privacy.md
  • .claude/agent-memory/archgate-developer/project_rules_engine_internals.md
  • src/engine/git-files.ts
  • src/engine/glob-utils.ts
  • src/engine/runner.ts
  • tests/engine/expand-brace-pattern.test.ts
  • tests/engine/glob-utils.test.ts
  • tests/engine/runner.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Smoke Test (Windows) / Windows
  • GitHub Check: Cloudflare Pages
🧰 Additional context used
📓 Path-based instructions (14)
tests/**/*.test.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-005-testing-standards.md)

tests/**/*.test.ts: Use Bun's built-in test runner (bun:test) for all test files, and place tests under tests/ mirroring the src/ directory structure with <module-name>.test.ts naming.
Use temp directories (mkdtemp) for filesystem tests, and clean them up in afterEach or afterAll.
Close external SDK instances (servers, clients, transports, connections) in afterEach or afterAll, managing their lifecycle in hooks rather than inside individual test bodies.
When a test creates a temporary git repository and runs git commit, configure local user.email and user.name immediately after git init.
Test public module interfaces, not private implementation details.
Use descriptive test names that explain the expected behavior.
Every runnable test()/it() must contain at least one expect() assertion; smoke tests must make the contract explicit with expect(() => fn()).not.toThrow() or await expect(promise).resolves.toBeUndefined().
Use test.skip, test.skipIf, or test.todo for intentionally empty or disabled tests; do not use bare return or empty callbacks to skip work.
If the first expect() is being added to a previously assertion-less test file, add expect to the bun:test import.
When mocking fetch in tests, assign directly to globalThis.fetch and restore the original or use mock.restore() afterward.
Wrap spyOn() and inline mockImplementation() usage in try/finally, or create and restore spies in hooks, so mockRestore() always runs.
Only raise a per-test timeout above the global bun test --timeout 60000; never set a shorter per-test timeout.
Mock first-party modules with import * as mod plus spyOn(mod, "fn"), not mock.module().
When a test needs to redirect user-scope paths, mock os.homedir() instead of relying on HOME/Bun.env.HOME; restore the spy in test hooks.
Do not depend on network access in unit tests.
Do not leave temp files after test runs.
Do not leave external SDK instances open after tests...

Files:

  • tests/engine/expand-brace-pattern.test.ts
  • tests/engine/glob-utils.test.ts
  • tests/engine/runner.test.ts
**/*.{ts,tsx,js,jsx,mjs,cjs}

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

**/*.{ts,tsx,js,jsx,mjs,cjs}: Use Bun built-ins for file I/O (Bun.file, Bun.write), HTTP, subprocess execution (Bun.spawn), globbing (Bun.Glob), and testing (bun:test).
Do not use Node.js-specific APIs when Bun alternatives exist; for example, use Bun.file() instead of fs.readFile() for simple reads.
Prefer node: built-in modules such as node:util, node:path, and node:fs over npm alternatives.
Do not use utility libraries for single functions, such as importing lodash for one helper like pick.
Do not use path aliases (tsconfig paths); use relative imports with Bun's native module resolution.
Do not install packages globally during development; use bunx for one-off tools.

Files:

  • tests/engine/expand-brace-pattern.test.ts
  • tests/engine/glob-utils.test.ts
  • tests/engine/runner.test.ts
  • src/engine/glob-utils.ts
  • src/engine/git-files.ts
  • src/engine/runner.ts
tests/**/*.ts

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

In test files, use _resetPlatformCache() to simulate different platforms instead of mocking or mutating process.platform directly.

Files:

  • tests/engine/expand-brace-pattern.test.ts
  • tests/engine/glob-utils.test.ts
  • tests/engine/runner.test.ts
{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/engine/expand-brace-pattern.test.ts
  • tests/engine/glob-utils.test.ts
  • tests/engine/runner.test.ts
  • src/engine/glob-utils.ts
  • src/engine/git-files.ts
  • src/engine/runner.ts
**

⚙️ CodeRabbit configuration file

**: # CLAUDE.md

Archgate is a CLI tool for AI governance via Architecture Decision Records (ADRs) — combining human-readable docs with machine-checkable rules. The CLI dogfoods itself via ADRs in .archgate/adrs/. AI features are delivered as a Claude Code plugin (../plugins/claude-code), not via direct API calls.

Technology Stack

  • Runtime: Bun (>=1.2.21) — not Node.js compatible
  • Language: TypeScript (strict mode, ESNext, ES modules)
  • CLI framework: Commander.js (@commander-js/extra-typings)
  • Linter: Oxlint | Formatter: Oxfmt | Dead exports: Knip | Commits: Conventional Commits

Commands

bun run src/cli.ts <command>  # run CLI locally
bun run lint                  # oxlint
bun run typecheck             # tsc --build
bun run format                # oxfmt --write
bun run format:check          # oxfmt --check
bun run test                  # all tests (not bare `bun test` — picks up --timeout; see GEN-003)
bun run knip                  # dead export detection
bun run validate              # MANDATORY: lint + typecheck + format:check + test + ADR check + knip + build check
bun run build:check            # verify build compiles (CI builds binaries via release workflow)
bun run commit                # conventional commit wizard

Validation Gate

bun run validate must pass before any task is considered complete. Fail-fast pipeline: lint → typecheck → format:check → test → ADR check → knip → build check. Mirrors CI in .github/workflows/code-pull-request.yml.

Git Hooks (Git 2.54+)

Config-based hooks in .githooks run validation locally before commits and pushes:

  • pre-commit: lint + typecheck + format:check (~15s)
  • pre-push: full bun run validate (~60s, mirrors CI)

Activate once per clone:

git config --local include.path ../.githooks

Opt out of a specific hook: git config --local hook.<name>.enabled false. Skip all hooks for a single commit: git commit --no-verify.

#...

Files:

  • tests/engine/expand-brace-pattern.test.ts
  • tests/engine/glob-utils.test.ts
  • tests/engine/runner.test.ts
  • src/engine/glob-utils.ts
  • src/engine/git-files.ts
  • src/engine/runner.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/engine/expand-brace-pattern.test.ts
  • tests/engine/glob-utils.test.ts
  • tests/engine/runner.test.ts
  • src/engine/glob-utils.ts
  • src/engine/git-files.ts
  • src/engine/runner.ts
.archgate/adrs/**

⚙️ CodeRabbit configuration file

.archgate/adrs/**: ---
id: ARCH-001
title: Command Structure
domain: architecture
rules: true
files: ["src/commands/**/*.ts"]

Context

The CLI needs a consistent pattern for defining and registering commands. As the command surface grows (init, check, adr, review-context, session-context, upgrade, clean), the registration mechanism must scale without introducing hidden coupling or making the dependency graph opaque.

Alternatives considered:

  • Auto-discovery via executableDir() — Commander.js supports automatic command discovery by scanning a directory for executable files. This eliminates manual imports but hides the dependency graph: adding or removing a command has no type-checked reference, making dead command detection impossible. It also requires each command to be a standalone executable, which prevents in-process testing and forces separate process spawning for every subcommand invocation.
  • Plugin-based registration — A plugin system where commands register themselves via a manifest or hook (similar to Oclif or Clipanion). This adds flexibility for third-party extensions but introduces significant complexity for an internal CLI with a known, finite set of commands. The indirection makes it harder to trace which code handles which command.
  • Single-file command map — Define all commands in a single file as a map of name-to-handler. Simple but creates a monolithic file that grows with every command, making merge conflicts frequent and readability poor.

The explicit register pattern strikes the right balance: each command owns its registration logic, the entry point makes all commands visible at a glance, and in-process execution enables straightforward testing without process spawning.

Decision

Commands live in src/commands/ and export a register*Command(program) function. The main entry point (src/cli.ts) explicitly imports and calls each register function. Subcommands (e.g., adr create, adr list) use nested directories wi...

Files:

  • .archgate/adrs/ARCH-007-cross-platform-subprocess-execution.md
  • .archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.rules.ts
  • .archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.md
src/**/*.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-003-output-formatting.md)

src/**/*.ts: Use styleText(format, text) from node:util for all terminal colors and formatting in CLI source files; do not use raw ANSI escape codes or third-party color libraries.
Commands that produce structured results and support --json must emit machine-readable JSON to stdout with no colors or decorative formatting.
When a command supports --json, use formatJSON() from src/helpers/output.ts for JSON serialization, and pass forcePretty: true when the user explicitly provided --json.
Use isAgentContext() from src/helpers/output.ts to enable auto-JSON behavior for commands that support both human-readable and JSON output modes.
CLI output must not include emoji; use text symbols and colors instead.
Send normal command output to stdout with console.log(), and send errors, warnings, and debug messages to stderr via logError(), logWarn(), and logDebug().
Keep CLI output concise and scannable by using whitespace and alignment instead of long text blocks.
Respect NO_COLOR automatically by relying on styleText; do not add custom color-environment handling in CLI code.
Do not output progress spinners unless there is a TTY check.
Do not assume piped output means agent context when CI is set; CI runners should still receive human-readable output.

src/**/*.ts: Do not re-export symbols from another module in any source file; statements like export { X } from "./other" and export type { X } from "./other" are forbidden.
Import symbols directly from the module that defines them; do not import from a directory path such as ../formats expecting implicit index.ts resolution.

Make large production thresholds injectable via an optional parameter that defaults to the module constant, so tests can supply a small value instead of generating huge fixtures.

src/**/*.ts: Use await Bun.file(path).json() when reading JSON files in Bun TypeScript source code; do not use JSON.parse(await Bun.file(path).text()) or `JSON.parse(fs.readFile...

Files:

  • src/engine/glob-utils.ts
  • src/engine/git-files.ts
  • src/engine/runner.ts
src/**/!(*platform).ts

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

src/**/!(*platform).ts: In src/ TypeScript source files, do not read process.platform directly; use src/helpers/platform.ts (isWindows(), isMacOS(), isLinux(), isWSL(), getPlatformInfo()) for all platform detection.
Use the centralized platform helper instead of duplicating OS/WSL detection logic inline anywhere in src/ TypeScript source.
When behavior differs between Linux and Windows, account for WSL by using isWSL() rather than assuming `

Files:

  • src/engine/glob-utils.ts
  • src/engine/git-files.ts
  • src/engine/runner.ts
src/{helpers,engine}/**/*.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-002-error-handling.md)

Do not use console.log(), console.warn(), or console.info() directly in helper or engine files; use logInfo() or logWarn() instead.

Files:

  • src/engine/glob-utils.ts
  • src/engine/git-files.ts
  • src/engine/runner.ts
src/engine/**/*.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.md)

src/engine/**/*.ts: In src/engine/, list project files by matching glob patterns in memory against the git-tracked file set, rather than walking the filesystem.
Before matching or scanning, validate the glob pattern and every brace-expanded alternative with safeGlob; reject absolute paths and .. segments.
Share RunCaches across rule contexts for per-run glob results and file text; cache promises, copy cached glob arrays before returning them, and do not cache mutable readJSON results.
Route new engine file listings through listMatchingFiles for sandboxed rule-facing patterns or matchTrackedFiles for trusted ADR frontmatter patterns.
When the target is a Git repository and respectGitignore is not false, pass the tracked set from getGitTrackedFiles.
Do not filter filesystem scan results against the tracked set as a substitute for in-memory matching.
Do not hardcode ignore lists such as node_modules/ or .venv/; use .gitignore through git ls-files as the authoritative source.

Files:

  • src/engine/glob-utils.ts
  • src/engine/git-files.ts
  • src/engine/runner.ts
src/engine/glob-utils.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.md)

matchTrackedFiles and listMatchingFiles must perform in-memory matching with Bun.Glob#match().

Files:

  • src/engine/glob-utils.ts
src/engine/{glob-utils,git-files}.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.md)

src/engine/{glob-utils,git-files}.ts: Bun.Glob#scan() is permitted only as a fallback when no tracked set exists; no other engine file may call it.
All remaining Bun.Glob#scan() fallbacks must pass { dot: true }.

Files:

  • src/engine/glob-utils.ts
  • src/engine/git-files.ts
src/engine/git-files.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.md)

getGitTrackedFiles must define the file universe using git ls-files --cached --others --exclude-standard, minus git ls-files --deleted.

Files:

  • src/engine/git-files.ts
src/engine/runner.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-022-ast-aware-rule-context.md)

src/engine/runner.ts: Implement ctx.ast(path, language) inside createRuleContext() with all dispatch hidden from rule authors.
For language: "typescript" and "javascript", ctx.ast() must reuse the existing in-process meriyah parser; no subprocess may be spawned for these branches.
Factor the duplicated parseModule() logic into a shared helper that is used by both rule-scanner.ts and the ctx.ast() TypeScript/JavaScript branch.
For language: "python" and "ruby", ctx.ast() must use Bun.spawn with array-based arguments only, with no shell interpolation.
Before any Python/Ruby interpreter is invoked, ctx.ast() must run the guardrail sequence in order: path safety, language plausibility check, interpreter availability probe, then guarded invocation.
ctx.ast() must use the same safePath() sandboxing as readFile/glob before accessing a target file.
ctx.ast() must reject files whose extension and/or leading content do not plausibly match the requested language before running an interpreter.
ctx.ast() must probe interpreter availability once per check invocation, cache the result, and reuse the first working candidate executable name.
On Windows, the interpreter probe must consider platform-appropriate candidates in order (including py via isWindows()); on non-Windows, it must use the non-Windows candidate order.
The Python branch must run in isolated mode (python -I -c ...) to prevent the working directory from shadowing standard-library imports.
The Python and Ruby serializers must strip a leading UTF-8 BOM before parsing.
ctx.ast() must throw on missing interpreter or parse failure, and must not return null or any other sentinel value.
The thrown error messages from ctx.ast() must distinguish interpreter-unavailable failures from parse-failure errors.
ctx.ast() must not expose Bun.spawn, child_process, or any other raw subprocess primitive on RuleContext; it is the only sanctioned tooling entrypoint.
`ctx.ast(...

Files:

  • src/engine/runner.ts
🧠 Learnings (2)
📚 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_public_repo_privacy.md
  • .claude/agent-memory/archgate-developer/MEMORY.md
  • .claude/agent-memory/archgate-developer/project_rules_engine_internals.md
📚 Learning: 2026-07-11T13:03:15.386Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 467
File: .archgate/adrs/ARCH-011-consistent-project-root-resolution.md:0-0
Timestamp: 2026-07-11T13:03:15.386Z
Learning: For Markdown files formatted by oxfmt (especially ADRs), avoid inline code spans that contain escaped backticks, e.g. `\`...\`` inside a single `` `...` `` span. oxfmt may mis-parse these and, on re-format, can collapse spaces after later inline code spans on the same line, effectively removing any manually re-added spacing. Instead, rephrase the text so the message stays plain quoted text, and put any embedded command/fragment that needs code formatting (e.g., `archgate init`) in its own separate inline code span; keep surrounding punctuation/spacing outside the code span.

Applied to files:

  • .archgate/adrs/ARCH-007-cross-platform-subprocess-execution.md
  • .archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.md
🪛 LanguageTool
.archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.md

[locale-violation] ~12-~12: In American English, ‘afterward’ is the preferred variant. ‘Afterwards’ is more commonly used in British English and other dialects.
Context: ...sults against the git-tracked file set afterwards. The walk cost was paid in full before...

(AFTERWARDS_US)


[style] ~60-~60: Consider an alternative for the overused word “exactly”.
Context: ...source of truth:** The file universe is exactly what git considers part of the project ...

(EXACTLY_PRECISELY)

🪛 markdownlint-cli2 (0.23.0)
.claude/agent-memory/archgate-developer/project_rules_engine_internals.md

[warning] 8-8: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

🔇 Additional comments (15)
.claude/agent-memory/archgate-developer/project_rules_engine_internals.md (1)

8-10: MD041 static-analysis hint is a known false positive for this file class — no action needed.

Content itself (expandBracePattern relocation, Bun.Glob match/scan semantics, ARCH-023 rationale, deferred load-cache follow-up) is accurate and consistent with the code changes in this PR.

Based on learnings, agent-memory files under .claude/agent-memory/ use YAML frontmatter with a name: field as the title and don't require a top-level H1; markdownlint isn't part of this repo's bun run validate pipeline (oxlint/oxfmt only), so this MD041 finding should not be flagged.

Source: Learnings

.archgate/adrs/ARCH-007-cross-platform-subprocess-execution.md (1)

61-61: LGTM!

Also applies to: 70-70

.archgate/adrs/ARCH-023-engine-file-listing-via-in-memory-git-tracked-matching.rules.ts (2)

1-19: LGTM!

Also applies to: 23-55


20-22: 🎯 Functional Correctness

No issue here.

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

25-25: LGTM!

Also applies to: 45-45

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

13-14: LGTM!

src/engine/glob-utils.ts (4)

9-23: LGTM!


25-56: LGTM!


58-80: LGTM!


82-121: LGTM!

src/engine/git-files.ts (1)

8-8: LGTM!

Also applies to: 51-76, 111-135

tests/engine/glob-utils.test.ts (1)

1-113: LGTM!

src/engine/runner.ts (1)

91-165: LGTM! The per-run cache design (promise-based in-flight sharing, copy-on-return for ctx.glob, deliberately-uncached readJSON) matches ARCH-023's contract precisely, and the new isolation test in runner.test.ts backs it up.

Also applies to: 214-214, 279-320, 351-359

tests/engine/expand-brace-pattern.test.ts (1)

8-10: LGTM!

tests/engine/runner.test.ts (1)

85-119: LGTM! Solid regression test directly exercising the shared-cache isolation contract added in runner.ts.

…anches

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

cursor Bot commented Jul 13, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_25303b94-c298-4c93-b6f4-dd2569c81f76)

@rhuanbarreto rhuanbarreto merged commit 457b835 into main Jul 13, 2026
23 checks passed
@rhuanbarreto rhuanbarreto deleted the perf/engine-in-memory-glob-matching branch July 13, 2026 16:25
@archgatebot archgatebot Bot mentioned this pull request Jul 13, 2026
rhuanbarreto pushed a commit that referenced this pull request Jul 14, 2026
# archgate

## [0.48.4](v0.48.3...v0.48.4)
(2026-07-13)

### Performance Improvements

* **engine:** match globs in memory against git-tracked files
([#474](#474))
([457b835](457b835)),
closes [oven-sh/bun#32596](oven-sh/bun#32596)

---
This PR was generated with
[simple-release](https://github.com/TrigenSoftware/simple-release).

<details>
<summary>📄 Cheatsheet</summary>
<br>



You can configure the bot's behavior through a pull request comment
using the `!simple-release/set-options` command.

### Command Format

````md
!simple-release/set-options

```json
{
  "bump": {},
  "publish": {}
}
```
````

### Useful Parameters

#### Bump

| Parameter | Type | Description |
|-----------|------|-------------|
| `version` | `string` | Force set specific version |
| `as` | `'major' \| 'minor' \| 'patch' \| 'prerelease'` | Release type
|
| `prerelease` | `string` | Pre-release identifier (e.g., "alpha",
"beta") |
| `firstRelease` | `boolean` | Whether this is the first release |
| `skip` | `boolean` | Skip version bump |
| `byProject` | `Record<string, object>` | Per-project bump options for
monorepos |

#### Publish

| Parameter | Type | Description |
|-----------|------|-------------|
| `skip` | `boolean` | Skip publishing |
| `access` | `'public' \| 'restricted'` | Package access level |
| `tag` | `string` | Tag for npm publication |

### Usage Examples

#### Force specific version

````md
!simple-release/set-options

```json
{
  "bump": {
    "version": "2.0.0"
  }
}
```
````

#### Force major bump

````md
!simple-release/set-options

```json
{
  "bump": {
    "as": "major"
  }
}
```
````

#### Create alpha pre-release

````md
!simple-release/set-options

```json
{
  "bump": {
    "prerelease": "alpha"
  }
}
```
````

#### Publish with specific access and tag

````md
!simple-release/set-options

```json
{
  "bump": {
    "prerelease": "beta"
  },
  "publish": {
    "access": "public",
    "tag": "beta"
  }
}
```
````

### Access Restrictions

The command can only be used by users with permissions:
- repository owner
- organization member
- collaborator

### Notes

- The last comment with `!simple-release/set-options` command takes
priority
- JSON must be valid, otherwise the command will be ignored
- Parameters apply only to the current release execution
- The command can be updated by editing the comment or adding a new one


</details>

<!--
  Please do not edit this comment.
  simple-release-pull-request: true
  simple-release-branch-from: release
  simple-release-branch-to: main
-->

Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
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