feat: ast-aware rule context (ctx.ast) for TS/JS/Python/Ruby#452
feat: ast-aware rule context (ctx.ast) for TS/JS/Python/Ruby#452rhuanbarreto wants to merge 16 commits into
Conversation
RuleContext gains a single ast(path, language) method supporting typescript/javascript (in-process via the shared meriyah parser) and python/ruby (stdlib ast module / Ripper via Bun.spawn subprocesses). Guardrail ordering per ARCH-022: safePath sandbox, language plausibility, cached interpreter probe, array-args-only invocation. Throws (never null) on parse failure or missing interpreter, with distinguishable messages riding the existing per-rule error isolation. The duplicated parseModule() calls in rule-scanner.ts are factored into the shared js-parser.ts helper as mandated by the ADR. Tests for the new modules land in the follow-up commit in this PR (ARCH-005 flags them until then). Claude-Session: https://claude.ai/code/session_012tjbzRdRBG1SLVdXvTA9dX Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
Flips ARCH-022 to rules:true as its own Compliance section mandates once ctx.ast() ships. The guardrail-ordering rule parses runner.ts via ctx.ast() itself — the first structural (non-regex) rule in the repo — plus subprocess-containment and single-method checks. Claude-Session: https://claude.ai/code/session_012tjbzRdRBG1SLVdXvTA9dX Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
Unit coverage for js-parser and ast-support (probe, subprocess runner, serializer programs gated on interpreter availability) plus runChecks-level integration tests for the four guardrails, failure semantics, and real Python/Ruby structural rules. Resolves the ARCH-005 gap from the core commit. Claude-Session: https://claude.ai/code/session_012tjbzRdRBG1SLVdXvTA9dX Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
Replaces the line-stripping barrel heuristic and the single-line .option() regexes with ESTree structural checks — the two fragile rules ARCH-022 names as motivating consumers. The ARCH-008 rules now catch multi-line .option() calls the old per-line regex missed. Claude-Session: https://claude.ai/code/session_012tjbzRdRBG1SLVdXvTA9dX Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
Adds the ast() method reference, AstNode shapes table, interpreter PATH requirement callouts, and a structural-checks guide section with one example rule per supported language, translated into both locales per GEN-002. Claude-Session: https://claude.ai/code/session_012tjbzRdRBG1SLVdXvTA9dX Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
Python's utf-8 codec preserves the BOM as U+FEFF, which ast.parse rejects as a syntax error; utf-8-sig strips it. Ruby gets the equivalent r:bom|utf-8 read mode. Claude-Session: https://claude.ai/code/session_012tjbzRdRBG1SLVdXvTA9dX Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
- Python subprocess runs with -I (isolated mode): without it, `python -c` puts the target project cwd on sys.path, letting a hostile project shadow stdlib modules (ast.py/json.py) and run arbitrary code. Verified end-to-end against a shadow ast.py. - Windows interpreter probe adds the `py` launcher: the python.org installer registers it even when PATH opt-in is unchecked. - .cjs files parse in sloppy script mode (globalReturn) so CommonJS top-level return no longer fails under module grammar. - ARCH-008 choices rule uses args.length < 2, not !== 2, so a fixed-choice .option() with a trailing default value is still flagged (regression vs the old regex). - AstNode JSDoc + ARCH-022 note the transpiled-loc caveat and py. Claude-Session: https://claude.ai/code/session_012tjbzRdRBG1SLVdXvTA9dX Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
Adds coverage for Ruby extensionless basenames (Rakefile/Gemfile), .tsx/.jsx dispatch, .cjs sloppy-script mode vs .mjs rejection, per-language plausibility rejection, and a security regression guard that the Python -I isolation blocks a hostile cwd ast.py shadow. Updates the Windows candidate-order assertion for the py launcher. Claude-Session: https://claude.ai/code/session_012tjbzRdRBG1SLVdXvTA9dX Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
For language "typescript", loc positions refer to Bun.Transpiler output, not the original .ts source, so line numbers drift. Documents that rules must re-locate matches in the original source before reporting a line; loc is source-accurate only for "javascript". Regenerates llms-full.txt. Claude-Session: https://claude.ai/code/session_012tjbzRdRBG1SLVdXvTA9dX Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
Adds a fourth companion rule (python-subprocess-isolated) that statically asserts the Python invocation keeps its -I flag, plus Do/Don't guidance for -I isolation, BOM stripping, and the transpiled-loc caveat — the security/correctness lessons surfaced by the adversarial review, so a future refactor cannot silently drop them. Claude-Session: https://claude.ai/code/session_012tjbzRdRBG1SLVdXvTA9dX Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
Captures the generalizable kernel from the ctx.ast review (python -I cwd-shadow RCE, BOM codec, transpiled loc drift, Windows py launcher) for future interpreter-subprocess work beyond ctx.ast itself. Claude-Session: https://claude.ai/code/session_012tjbzRdRBG1SLVdXvTA9dX Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
|
Warning Review limit reached
Next review available in: 42 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThis PR adds Related PRs: None identified 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
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. Comment |
Deploying archgate-cli with
|
| Latest commit: |
5e2dd8a
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://a2426760.archgate-cli.pages.dev |
| Branch Preview URL: | https://claude-ast-aware-rule-contex.archgate-cli.pages.dev |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 3 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 8c59cd8. Configure here.
There was a problem hiding this comment.
Actionable comments posted: 9
🤖 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-022-ast-aware-rule-context.rules.ts:
- Around line 81-93: Replace the loc-based ordering in the guardrail sequence
logic with a visitation-based ordinal, since ARCH-022 says not to trust node.loc
for "typescript" ASTs. In the firstSeen tracking inside the walk(astMethodBody,
...) loop, use the walk traversal order itself (for example, an incrementing
counter when a guardrail Identifier is first encountered) instead of computing
positions from n.loc.start.line and column. Keep the change localized to the
ordering logic around firstSeen, walk, and GUARDRAIL_SEQUENCE so the rule still
compares first occurrences without relying on transpiled-relative locations.
In `@src/engine/ast-support.ts`:
- Line 16: The timeout in runAstSubprocess is tied to the hardcoded
AST_SUBPROCESS_TIMEOUT_MS constant, which prevents fast coverage of the timeout
branch. Update runAstSubprocess to accept an optional timeout parameter that
defaults to AST_SUBPROCESS_TIMEOUT_MS, then use that parameter everywhere the
subprocess timeout is enforced so tests can inject a tiny value. Keep the
existing AST_SUBPROCESS_TIMEOUT_MS export unchanged for production callers and
adjust the timeout test in tests/engine/ast-support.test.ts to pass a small
timeout and cover the branch deterministically.
- Around line 141-168: The timeout branch in runAstSubprocess kills the
Bun.spawned process but does not wait for proc.exited to settle afterward.
Update the timeout handling so that after proc.kill() you still await
proc.exited before throwing, ensuring the subprocess is fully reaped; keep the
fix localized to runAstSubprocess and preserve the existing stdout/stderr
collection flow.
- Around line 107-133: `probeInterpreter` can hang forever because it only waits
on `proc.exited` and `stdout`, so add the same timeout protection used by
`runAstSubprocess` (or a shared helper) when spawning candidates in
`src/engine/ast-support.ts`. Update the `probeInterpreter` loop to race each
`Bun.spawn` probe against `AST_SUBPROCESS_TIMEOUT_MS`, and on timeout kill the
process and continue to the next candidate so stalled binaries like `python3` on
macOS do not block `ctx.ast()`.
In `@src/engine/runner.ts`:
- Around line 309-331: The TypeScript parsing path in runner.ts treats every
.ts/.tsx input as a module, which breaks supported .cts files that may use
CommonJS-only script syntax. Update the TypeScript branch in the runner’s parse
flow to detect the .cts extension and pass sourceType: "script" into
parseJsModule, similar to the existing .cjs handling; if .cts is not intended to
be supported, remove it from the supported TypeScript extension list instead.
In `@tests/engine/ast-support.test.ts`:
- Around line 61-83: The runAstSubprocess tests only cover stdout and stderr
cases, so the timeout branch that kills the child process and throws remains
untested. Update the tests around runAstSubprocess to exercise the injectable
timeout parameter mentioned in src/engine/ast-support.ts, and add a case that
starts a long-running subprocess, uses a short timeout, and asserts the promise
rejects with a timeout message while the subprocess is killed.
- Around line 130-243: Add a BOM-specific end-to-end case for the AST serializer
paths so `PYTHON_AST_PROGRAM` and `RUBY_AST_PROGRAM` are exercised with a UTF-8
BOM fixture, not just plain UTF-8 input. Update the existing
`describe("PYTHON_AST_PROGRAM end-to-end", ...)` and `describe("RUBY_AST_PROGRAM
end-to-end", ...)` blocks, or add the coverage in
`tests/engine/runner-ast.test.ts`, to verify BOM handling still works for the
subprocess flow. Make sure the test targets the serializer/runner path that uses
`encoding="utf-8-sig"` or `mode: "r:bom|utf-8"` so the regression is caught.
In `@tests/engine/js-parser.test.ts`:
- Around line 1-53: Add coverage for the missing `sourceType: "script"` path in
`parseJsModule` by extending the existing `parseJsModule` tests to verify script
mode behavior, especially that CommonJS-style syntax such as a top-level
`return` is accepted when `sourceType: "script"` is passed and still rejected in
module mode. Use the existing `parseJsModule` helper in
`tests/engine/js-parser.test.ts` to keep the assertion close to the current
module/JSX/syntax tests.
In `@tests/engine/runner-ast.test.ts`:
- Around line 388-429: Add an analogous regression test in the existing
.cjs/.mjs parsing test suite to cover the .cts gap mentioned by runner.ts. Use a
.cts fixture and a construct that only parses in "script" mode, then assert the
parser behavior and sourceType the same way this test does for cjsSourceType and
mjs error handling. Reuse the same test pattern in runner-ast.test.ts so the new
case clearly verifies whether .cts is treated like sloppy CommonJS or still
needs a fix in the AST routing logic.
🪄 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: 25225b9d-ad05-4469-9965-11820e7e8f00
📒 Files selected for processing (21)
.archgate/adrs/ARCH-004-no-barrel-files.rules.ts.archgate/adrs/ARCH-008-typed-command-options.rules.ts.archgate/adrs/ARCH-022-ast-aware-rule-context.md.archgate/adrs/ARCH-022-ast-aware-rule-context.rules.ts.claude/agent-memory/archgate-developer/project_rules_engine_internals.mddocs/public/llms-full.txtdocs/src/content/docs/guides/writing-rules.mdxdocs/src/content/docs/nb/guides/writing-rules.mdxdocs/src/content/docs/nb/reference/rule-api.mdxdocs/src/content/docs/pt-br/guides/writing-rules.mdxdocs/src/content/docs/pt-br/reference/rule-api.mdxdocs/src/content/docs/reference/rule-api.mdxsrc/engine/ast-support.tssrc/engine/js-parser.tssrc/engine/rule-scanner.tssrc/engine/runner.tssrc/formats/rules.tssrc/helpers/rules-shim.tstests/engine/ast-support.test.tstests/engine/js-parser.test.tstests/engine/runner-ast.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
- GitHub Check: Cursor Bugbot
- GitHub Check: Smoke Test (Windows) / Windows
- GitHub Check: Cloudflare Pages
⚠️ CI failures not shown inline (4)
GitHub Actions: DCO / 0_DCO Sign-off Check.txt: feat: AST-aware rule context (ctx.ast) for TS/JS/Python/Ruby
Conclusion: failure
##[group]Run base="21829b56e22cbf7442bc4fe66502ba988073c049"
�[36;1mbase="21829b56e22cbf7442bc4fe66502ba988073c049"�[0m
�[36;1mhead="8c59cd869f6cc1131fa8879969bf3416e6033558"�[0m
�[36;1mfailed=0�[0m
�[36;1m�[0m
�[36;1mfor sha in $(git rev-list --no-merges "$base".."$head"); do�[0m
�[36;1m if ! git log -1 --format='%B' "$sha" | grep -qiE '^Signed-off-by: .+ <.+>'; then�[0m
�[36;1m echo "::error::Commit $sha is missing a DCO Signed-off-by line."�[0m
GitHub Actions: DCO / DCO Sign-off Check: feat: AST-aware rule context (ctx.ast) for TS/JS/Python/Ruby
Conclusion: failure
##[group]Run base="21829b56e22cbf7442bc4fe66502ba988073c049"
�[36;1mbase="21829b56e22cbf7442bc4fe66502ba988073c049"�[0m
�[36;1mhead="8c59cd869f6cc1131fa8879969bf3416e6033558"�[0m
�[36;1mfailed=0�[0m
�[36;1m�[0m
�[36;1mfor sha in $(git rev-list --no-merges "$base".."$head"); do�[0m
�[36;1m if ! git log -1 --format='%B' "$sha" | grep -qiE '^Signed-off-by: .+ <.+>'; then�[0m
�[36;1m echo "::error::Commit $sha is missing a DCO Signed-off-by line."�[0m
GitHub Actions: Code Quality: PR #452 / Analyze (go): Code Quality: PR #452
Conclusion: failure
,"bundleSupportsIncludeLogs":true,"bundleSupportsOverlay":true,"databaseInterpretResultsSupportsSarifRunProperty":true,"featuresInVersionResult":true,"indirectTracingSupportsStaticBinaries":false,"informsAboutUnsupportedPathFilters":true,"supportsPython312":true,"mrvaPackCreate":true,"threatModelOption":true,"traceCommandUseBuildMode":true,"v2ramSizing":true,"mrvaPackCreateMultipleQueries":true,"setsCodeqlRunnerEnvVar":true,"sarifMergeRunsFromEqualCategory":true,"forceOverwrite":true,"generateSummarySymbolMap":true,"pythonDefaultIsToNotExtractStdlib":true,"queryServerRunQueries":true,"queryServerTrimCacheWithMode":true,"builtinExtractorsSpecifyDefaultQueries":true,"bqrsDiffResultSets":true,"bundleSupportsIncludeOption":true,"suppressesMissingFileBaselineWarning":true}}}
CODEQL_ACTION_GO_BINARY: /home/runner/work/_temp/codeql-action-go-tracing/bin/go
CODEQL_RAM: 14575
CODEQL_THREADS: 4
CODEQL_PROXY_HOST:
CODEQL_PROXY_PORT:
CODEQL_PROXY_CA_CERTIFICATE:
CODEQL_PROXY_URLS:
##[endgroup]
##[group]Attempting to automatically build go code
[command]/opt/hostedtoolcache/CodeQL/2.25.6/x64/codeql/codeql database trace-command --use-build-mode --working-dir /home/runner/work/cli/cli /home/runner/work/_temp/codeql_databases/go
Picked up JAVA_TOOL_OPTIONS: -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false
Running command in /home/runner/work/cli/cli: [/opt/hostedtoolcache/CodeQL/2.25.6/x64/codeql/go/tools/autobuild.sh]
[] [build-stderr] 2026/07/05 11:36:28 Autobuilder was built with go1.26.0, environment has go1.24.13
[] [build-stderr] 2026/07/05 11:36:28 LGTM_SRC is /home/runner/work/cli/cli
[] [build-stderr] 2026/07/05 11:36:28 Found no go.work files in the workspace; looking for go.mod files...
[] [build-stderr] 2026/07/05 11:36:28 Found 1 go.mod files in: shims/go/go.mod.
[] [build-stderr] 2026/07/05 11:36:28 Found 1 go.mod file(s).
[] [build-stderr] 2026/07/05 11:36:28 Import path is 'github.com/archgate/cli'
[] [build-stderr] 2026/07/...
GitHub Actions: Code Quality: PR #452 / 0_Analyze (go).txt: Code Quality: PR #452
Conclusion: failure
:36 Extracting types for package crypto/aes.
[] [build-stderr] 2026/07/05 11:36:36 Done extracting types for package crypto/aes.
[] [build-stderr] 2026/07/05 11:36:36 Processing package crypto/des.
[] [build-stderr] 2026/07/05 11:36:36 Extracting types for package crypto/des.
[] [build-stderr] 2026/07/05 11:36:36 Done extracting types for package crypto/des.
[] [build-stderr] 2026/07/05 11:36:36 Processing package crypto/internal/fips140/nistec/fiat.
[] [build-stderr] 2026/07/05 11:36:36 Extracting types for package crypto/internal/fips140/nistec/fiat.
[] [build-stderr] 2026/07/05 11:36:36 Done extracting types for package crypto/internal/fips140/nistec/fiat.
[] [build-stderr] 2026/07/05 11:36:36 Processing package crypto/internal/fips140/nistec.
[] [build-stderr] 2026/07/05 11:36:36 Extracting types for package crypto/internal/fips140/nistec.
[] [build-stderr] 2026/07/05 11:36:36 Done extracting types for package crypto/internal/fips140/nistec.
[] [build-stderr] 2026/07/05 11:36:36 Processing package crypto/internal/fips140/ecdh.
[] [build-stderr] 2026/07/05 11:36:36 Extracting types for package crypto/internal/fips140/ecdh.
[] [build-stderr] 2026/07/05 11:36:36 Done extracting types for package crypto/internal/fips140/ecdh.
[] [build-stderr] 2026/07/05 11:36:36 Processing package crypto/internal/fips140/edwards25519/field.
[] [build-stderr] 2026/07/05 11:36:36 Extracting types for package crypto/internal/fips140/edwards25519/field.
[] [build-stderr] 2026/07/05 11:36:36 Done extracting types for package crypto/internal/fips140/edwards25519/field.
[] [build-stderr] 2026/07/05 11:36:36 Processing package crypto/ecdh.
[] [build-stderr] 2026/07/05 11:36:36 Extracting types for package crypto/ecdh.
[] [build-stderr] 2026/07/05 11:36:36 Done extracting types for package crypto/ecdh.
[] [build-stderr] 2026/07/05 11:36:36 Processing package crypto/elliptic.
[] [build-stderr] 2026/07/05 11:36:36 Extracting types for package crypto/elliptic.
[] [buil...
🧰 Additional context used
📓 Path-based instructions (21)
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 undertests/mirroring thesrc/directory structure with<module-name>.test.tsnaming.
Use temp directories (mkdtemp) for filesystem tests, and clean them up inafterEachorafterAll.
Close external SDK instances (servers, clients, transports, connections) inafterEachorafterAll, managing their lifecycle in hooks rather than inside individual test bodies.
When a test creates a temporary git repository and runsgit commit, configure localuser.emailanduser.nameimmediately aftergit init.
Test public module interfaces, not private implementation details.
Use descriptive test names that explain the expected behavior.
Every runnabletest()/it()must contain at least oneexpect()assertion; smoke tests must make the contract explicit withexpect(() => fn()).not.toThrow()orawait expect(promise).resolves.toBeUndefined().
Usetest.skip,test.skipIf, ortest.todofor intentionally empty or disabled tests; do not use barereturnor empty callbacks to skip work.
If the firstexpect()is being added to a previously assertion-less test file, addexpectto thebun:testimport.
When mockingfetchin tests, assign directly toglobalThis.fetchand restore the original or usemock.restore()afterward.
WrapspyOn()and inlinemockImplementation()usage intry/finally, or create and restore spies in hooks, somockRestore()always runs.
Only raise a per-test timeout above the globalbun test --timeout 60000; never set a shorter per-test timeout.
Mock first-party modules withimport * as modplusspyOn(mod, "fn"), notmock.module().
When a test needs to redirect user-scope paths, mockos.homedir()instead of relying onHOME/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/js-parser.test.tstests/engine/ast-support.test.tstests/engine/runner-ast.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, useBun.file()instead offs.readFile()for simple reads.
Prefernode:built-in modules such asnode:util,node:path, andnode:fsover npm alternatives.
Do not use utility libraries for single functions, such as importing lodash for one helper likepick.
Do not use path aliases (tsconfig paths); use relative imports with Bun's native module resolution.
Do not install packages globally during development; usebunxfor one-off tools.
Files:
tests/engine/js-parser.test.tssrc/engine/js-parser.tssrc/helpers/rules-shim.tssrc/formats/rules.tssrc/engine/rule-scanner.tstests/engine/ast-support.test.tstests/engine/runner-ast.test.tssrc/engine/ast-support.tssrc/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 mutatingprocess.platformdirectly.
Files:
tests/engine/js-parser.test.tstests/engine/ast-support.test.tstests/engine/runner-ast.test.ts
{src,tests}/**/*.ts
📄 CodeRabbit inference engine (.archgate/adrs/LEGAL-001-spdx-license-headers.md)
{src,tests}/**/*.ts: Every TypeScript source file insrc/andtests/must begin with// SPDX-License-Identifier: Apache-2.0followed by// Copyright 2026 Archgate.
If a TypeScript file has a shebang line (for example#!/usr/bin/env buninsrc/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/js-parser.test.tssrc/engine/js-parser.tssrc/helpers/rules-shim.tssrc/formats/rules.tssrc/engine/rule-scanner.tstests/engine/ast-support.test.tstests/engine/runner-ast.test.tssrc/engine/ast-support.tssrc/engine/runner.ts
**
⚙️ CodeRabbit configuration file
**: # CLAUDE.mdArchgate 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 wizardValidation Gate
bun run validatemust 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
.githooksrun 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 ../.githooksOpt 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/js-parser.test.tssrc/engine/js-parser.tssrc/helpers/rules-shim.tssrc/formats/rules.tsdocs/src/content/docs/pt-br/reference/rule-api.mdxsrc/engine/rule-scanner.tsdocs/src/content/docs/pt-br/guides/writing-rules.mdxtests/engine/ast-support.test.tsdocs/src/content/docs/reference/rule-api.mdxdocs/src/content/docs/nb/guides/writing-rules.mdxdocs/public/llms-full.txtdocs/src/content/docs/nb/reference/rule-api.mdxtests/engine/runner-ast.test.tssrc/engine/ast-support.tssrc/engine/runner.tsdocs/src/content/docs/guides/writing-rules.mdx
⚙️ 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.tsfile with automated checks that run viaarchgate check.When reviewing, you must:
- Treat ADR violations as blocking issues, not suggestions.
- Cite the specific ADR ID when flagging a violation (e.g., "Violates ARCH-006").
- Focus on semantic and contextual violations that automated rules cannot catch —
the.rules.tsfiles already cover syntactic/structural patterns.- If you are unsure whether something violates an ADR, flag it as a question
rather than approving it.
Files:
tests/engine/js-parser.test.tssrc/engine/js-parser.tssrc/helpers/rules-shim.tssrc/formats/rules.tsdocs/src/content/docs/pt-br/reference/rule-api.mdxsrc/engine/rule-scanner.tsdocs/src/content/docs/pt-br/guides/writing-rules.mdxtests/engine/ast-support.test.tsdocs/src/content/docs/reference/rule-api.mdxdocs/src/content/docs/nb/guides/writing-rules.mdxdocs/public/llms-full.txtdocs/src/content/docs/nb/reference/rule-api.mdxtests/engine/runner-ast.test.tssrc/engine/ast-support.tssrc/engine/runner.tsdocs/src/content/docs/guides/writing-rules.mdx
src/**/*.ts
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-002-error-handling.md)
src/**/*.ts: UselogError()fromsrc/helpers/log.tsfor user-facing errors instead of printing errors directly.
Exit with code1for expected failures such as missing config, invalid input, or validation violations.
Let unexpected errors crash naturally so they are treated as internal errors with exit code2.
Include actionable suggestions in user-facing error messages whenever possible.
Write user-facing errors to stderr vialogError(); do not send them to stdout.
WhenfindProjectRoot()returnsnullfor commands that do not require.archgate/, fall back toprocess.cwd()as the path key or working directory.
CatchExitPromptErrorfrom Inquirer in the top-level error boundary and treat it as user cancellation: exit with code130without logging an error or sending it to Sentry.
Do not useconsole.error()directly; uselogError()for consistent formatting.
Do not exit with codes other than0,1,2, or130.
Do not send user-cancellation errors such as InquirerExitPromptErrorto Sentry; filter them inbeforeSend.
src/**/*.ts: UsestyleText(format, text)fromnode:utilfor 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--jsonmust emit machine-readable JSON to stdout with no colors or decorative formatting.
When a command supports--json, useformatJSON()fromsrc/helpers/output.tsfor JSON serialization, and passforcePretty: truewhen the user explicitly provided--json.
UseisAgentContext()fromsrc/helpers/output.tsto 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 withconsole.log(), and send errors, warnings, and debug messages to stderr vialogError(),logWarn(), andlogDebug().
Keep CLI output concise and scannabl...
Files:
src/engine/js-parser.tssrc/helpers/rules-shim.tssrc/formats/rules.tssrc/engine/rule-scanner.tssrc/engine/ast-support.tssrc/engine/runner.ts
src/{helpers,engine}/**/*.ts
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-002-error-handling.md)
Do not use
console.log(),console.warn(), orconsole.info()directly in helper or engine files; uselogInfo()orlogWarn()instead. Command files are exempt because they are the I/O layer.
Files:
src/engine/js-parser.tssrc/helpers/rules-shim.tssrc/engine/rule-scanner.tssrc/engine/ast-support.tssrc/engine/runner.ts
src/**/!(*platform).ts
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-009-platform-detection-helper.md)
src/**/!(*platform).ts: Insrc/TypeScript source files, do not readprocess.platformdirectly; usesrc/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 insrc/TypeScript source.
When behavior differs between Linux and Windows, account for WSL by usingisWSL()rather than assuming `
Files:
src/engine/js-parser.tssrc/helpers/rules-shim.tssrc/formats/rules.tssrc/engine/rule-scanner.tssrc/engine/ast-support.tssrc/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-008-typed-command-options.rules.ts.archgate/adrs/ARCH-004-no-barrel-files.rules.ts.archgate/adrs/ARCH-022-ast-aware-rule-context.rules.ts.archgate/adrs/ARCH-022-ast-aware-rule-context.md
src/helpers/rules-shim.ts
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-022-ast-aware-rule-context.md)
The generated
RuleContextshim for.rules.tsauthors must mirror the singleast(path, language)method signature and not add per-language variants.
Files:
src/helpers/rules-shim.ts
src/formats/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Zod schemas are the single source of truth; derive types with
z.infer<>, do not define separate interfaces, usesafeParse(), and reuseAdrFrontmatterSchema.shape.*instead of duplicating enums.
Files:
src/formats/rules.ts
src/formats/rules.ts
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-022-ast-aware-rule-context.md)
RuleContextmust expose a singleast(path: string, language: "typescript" | "javascript" | "python" | "ruby"): Promise<AstNode>method (no per-language variants).
Files:
src/formats/rules.ts
docs/src/content/docs/**/*.mdx
📄 CodeRabbit inference engine (.archgate/adrs/GEN-001-documentation-site.md)
docs/src/content/docs/**/*.mdx: Use MDX (.mdx) for all content pages underdocs/src/content/docs/.
Escape literal curly braces in MDX when showing template syntax; do not use bare{}in prose or code-fence labels.
Files:
docs/src/content/docs/pt-br/reference/rule-api.mdxdocs/src/content/docs/pt-br/guides/writing-rules.mdxdocs/src/content/docs/reference/rule-api.mdxdocs/src/content/docs/nb/guides/writing-rules.mdxdocs/src/content/docs/nb/reference/rule-api.mdxdocs/src/content/docs/guides/writing-rules.mdx
{docs/src/content/docs/**/*.mdx,docs/astro.config.mjs}
📄 CodeRabbit inference engine (.archgate/adrs/GEN-001-documentation-site.md)
When adding a new documentation page, create the MDX file in
docs/src/content/docs/<category>/<slug>.mdxand add the page todocs/astro.config.mjssidebar configuration.
Files:
docs/src/content/docs/pt-br/reference/rule-api.mdxdocs/src/content/docs/pt-br/guides/writing-rules.mdxdocs/src/content/docs/reference/rule-api.mdxdocs/src/content/docs/nb/guides/writing-rules.mdxdocs/src/content/docs/nb/reference/rule-api.mdxdocs/src/content/docs/guides/writing-rules.mdx
docs/src/content/docs/**
📄 CodeRabbit inference engine (.archgate/adrs/GEN-001-documentation-site.md)
Do not create documentation content files outside
docs/src/content/docs/; Starlight content must live in that exact directory structure.
Files:
docs/src/content/docs/pt-br/reference/rule-api.mdxdocs/src/content/docs/pt-br/guides/writing-rules.mdxdocs/src/content/docs/reference/rule-api.mdxdocs/src/content/docs/nb/guides/writing-rules.mdxdocs/src/content/docs/nb/reference/rule-api.mdxdocs/src/content/docs/guides/writing-rules.mdx
docs/src/content/docs/**/*.{mdx,md}
📄 CodeRabbit inference engine (.archgate/adrs/GEN-002-docs-i18n.md)
docs/src/content/docs/**/*.{mdx,md}: For every English docs page underdocs/src/content/docs/, create a translated file in each locale directory with the exact same relative path and filename, and ensure every locale file corresponds to an existing root file (no orphan translations).
When adding or modifying English documentation content, update the corresponding locale files in the same pull request.
Translate all user-facing prose in docs pages, including titles, descriptions, headings, paragraphs, list items, table text, and admonition content.
Translate user-visible text props in Starlight components such as<Card title="...">and<LinkCard description="...">.
Keep code blocks, CLI commands, file paths, TypeScript identifiers, technical terms, import statements, component names, andlink/href/slugattribute values in English.
Keep internal links unchanged and do not add locale prefixes (for example, use/guides/...rather than/pt-br/guides/...).
Preserve MDX curly-brace escaping (\{\}) in translated content.
Preserve Starlight component import statements identically in translated files.
Files:
docs/src/content/docs/pt-br/reference/rule-api.mdxdocs/src/content/docs/pt-br/guides/writing-rules.mdxdocs/src/content/docs/reference/rule-api.mdxdocs/src/content/docs/nb/guides/writing-rules.mdxdocs/src/content/docs/nb/reference/rule-api.mdxdocs/src/content/docs/guides/writing-rules.mdx
docs/src/content/docs/pt-br/**/*.{mdx,md}
📄 CodeRabbit inference engine (.archgate/adrs/GEN-002-docs-i18n.md)
Use correct Portuguese diacritical marks in Brazilian Portuguese translations; never write unaccented Portuguese.
Files:
docs/src/content/docs/pt-br/reference/rule-api.mdxdocs/src/content/docs/pt-br/guides/writing-rules.mdx
src/engine/rule-scanner.ts
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-022-ast-aware-rule-context.md)
Factor the duplicated
parseModule()invocation into a shared exported parse helper, and have the scanner use that helper instead of an inline parse call.
Files:
src/engine/rule-scanner.ts
docs/src/content/docs/reference/**/*.mdx
📄 CodeRabbit inference engine (.archgate/adrs/GEN-001-documentation-site.md)
Keep reference pages accurate to the CLI source code; when CLI APIs change, update the corresponding reference docs in the same PR.
Files:
docs/src/content/docs/reference/rule-api.mdx
docs/src/content/docs/nb/**/*.{mdx,md}
📄 CodeRabbit inference engine (.archgate/adrs/GEN-002-docs-i18n.md)
Use Norwegian Bokmål (not Nynorsk) for Norwegian translations, with the informal
duform and correct Norwegian characters (æ,ø,å).
Files:
docs/src/content/docs/nb/guides/writing-rules.mdxdocs/src/content/docs/nb/reference/rule-api.mdx
src/engine/runner.ts
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-022-ast-aware-rule-context.md)
src/engine/runner.ts: Implementctx.ast()insidecreateRuleContext()with internal dispatch only; rule authors must not see or control the dispatch mechanism.
Forctx.ast()on TypeScript/JavaScript, reuse the existing in-processmeriyahparser; do not spawn a subprocess for these languages.
Forctx.ast()on Python/Ruby, invoke the language's standard-library AST facility viaBun.spawnusing array-based arguments only.
Before any Python/Ruby subprocess is spawned, run guardrails in this order: path safety, language plausibility, interpreter availability probe, then guarded invocation.
Use the samesafePath()sandboxing forctx.ast()that is already applied toreadFileandglob.
Probe interpreter availability once percheckinvocation, cache the result, and use platform-appropriate executable names (python/python3/py,ruby).
Run Python in isolated mode (python -I -c ...) when parsing files throughctx.ast().
Strip a leading UTF-8 BOM before parsing in the Python and Ruby serializers.
ctx.ast()must throw on missing interpreter or parse failure; it must not returnnullor another sentinel.
Do not trustnode.locfor TypeScript ASTs parsed from transpiled output; re-locate positions in the original source before reporting.
Do not useBun.$or shell-interpolated command strings for any subprocess invocation in this feature.
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/project_rules_engine_internals.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/rules-shim.ts
🪛 LanguageTool
docs/src/content/docs/pt-br/reference/rule-api.mdx
[uncategorized] ~180-~180: Pontuação duplicada
Context: ...a árvore retornada difere por linguagem -- veja AstNode. ```typescrip...
(DOUBLE_PUNCTUATION_XML)
[uncategorized] ~196-~196: Pontuação duplicada
Context: ...este arquivo tem um erro de sintaxe". :::caution Regras de Python e Ruby exigem o...
(DOUBLE_PUNCTUATION_XML)
[uncategorized] ~198-~198: Pontuação duplicada
Context: ...chgate e não precisa de interpretador. ::: --- ## RuleReport A interface de rel...
(DOUBLE_PUNCTUATION_XML)
[uncategorized] ~302-~302: Pontuação duplicada
Context: ...a | Arrays aninhados de Ripper.sexp: ["program", [["command", ...]]] com pares de posição ...
(DOUBLE_PUNCTUATION_XML)
docs/src/content/docs/pt-br/guides/writing-rules.mdx
[uncategorized] ~179-~179: Pontuação duplicada
Context: ...falha de parse ou interpretador ausente -- nunca retorna null. ```typescript co...
(DOUBLE_PUNCTUATION_XML)
[uncategorized] ~221-~221: Pontuação duplicada
Context: ... "isto é uma cláusula except: vazia?" -- declarações multilinha, comentários e c...
(DOUBLE_PUNCTUATION_XML)
[uncategorized] ~227-~227: Pontuação duplicada
Context: ...iblioteca padrão: arrays aninhados como ["program", [["command", ...]]] com pares de posição ...
(DOUBLE_PUNCTUATION_XML)
[uncategorized] ~229-~229: Pontuação duplicada
Context: ...ha de parse ou de interpretador ausente -- nunca retorna null. A exceção fica is...
(DOUBLE_PUNCTUATION_XML)
[uncategorized] ~231-~231: Pontuação duplicada
Context: ...a visível em vez de um falso sucesso. :::caution O parsing de Python e Ruby invoc...
(DOUBLE_PUNCTUATION_XML)
[style] ~232-~232: Três frases seguidas começam com a mesma palavra. Considere reformular a frase ou use um dicionário para encontrar um sinônimo.
Context: ...a máquina de desenvolvedor e no CI. O parsing de TypeScript e JavaScript é em...
(PORTUGUESE_WORD_REPEAT_BEGINNING_RULE)
[uncategorized] ~233-~233: Pontuação duplicada
Context: ...chgate e não precisa de interpretador. ::: ### TypeScript: sem barrel files Um b...
(DOUBLE_PUNCTUATION_XML)
[uncategorized] ~280-~280: Se é uma abreviatura, falta um ponto. Se for uma expressão, coloque entre aspas.
Context: ...esloca posições: os números de linha em loc referem-se ao texto transpilado, não a...
(ABREVIATIONS_PUNCTUATION)
[uncategorized] ~280-~280: Esta locução deve ser separada por vírgulas.
Context: ... a construção no código-fonte original (por exemplo com ctx.readFile() e indexOf) antes...
(VERB_COMMA_CONJUNCTION)
[grammar] ~284-~284: Deve-se usar a próclise quando o verbo for precedido imediatamente por palavras de negação e determinados pronomes, conjunções e advérbios.
Context: ...e arrays cobre todos os tipos de nó sem enumerá-los: ```typescript /// <reference path=".....
(COLOCACAO_PRONOMINAL_COM_ATRATOR_SIMPLES)
[uncategorized] ~336-~336: Se é uma abreviatura, falta um ponto. Se for uma expressão, coloque entre aspas.
Context: ...ha no código-fonte original, já que seu loc refere-se à saída transpilada. ### Py...
(ABREVIATIONS_PUNCTUATION)
[uncategorized] ~340-~340: Pontuação duplicada
Context: ...guarda a expressão da exceção capturada -- um except: vazio tem "type": null. ...
(DOUBLE_PUNCTUATION_XML)
[uncategorized] ~389-~389: Pontuação duplicada
Context: ...er.sexprepresentaputs "hello"como["command", ["@ident", "puts", [1, 0]], [...args]]-- o par[linha, coluna]`...
(DOUBLE_PUNCTUATION_XML)
.archgate/adrs/ARCH-022-ast-aware-rule-context.md
[style] ~42-~42: Consider removing “of” to be more concise
Context: ...nandBun.spawnSyncfrom rule code). All of the following MUST execute insidecreateRu...
(ALL_OF_THE)
🔇 Additional comments (22)
docs/public/llms-full.txt (1)
2873-2884: LGTM!Also applies to: 2915-3133, 5121-5121, 5224-5251, 5340-5359
docs/src/content/docs/guides/writing-rules.mdx (1)
177-188: LGTM!Also applies to: 219-234, 235-281, 282-337, 338-385, 387-438
docs/src/content/docs/reference/rule-api.mdx (1)
68-68: LGTM!Also applies to: 171-199, 288-307
docs/src/content/docs/nb/guides/writing-rules.mdx (1)
177-188: LGTM!Also applies to: 219-234, 235-280, 282-337, 338-386, 387-438
docs/src/content/docs/nb/reference/rule-api.mdx (1)
68-68: LGTM!Also applies to: 171-199, 288-307
docs/src/content/docs/pt-br/guides/writing-rules.mdx (1)
177-188: LGTM!Also applies to: 219-234, 235-337, 338-438
docs/src/content/docs/pt-br/reference/rule-api.mdx (1)
68-68: LGTM!Also applies to: 171-199, 288-307
src/engine/js-parser.ts (1)
1-41: LGTM! Correctly centralizes the meriyah call site per ARCH-022, and the.cjs/globalReturnrationale is well documented.src/engine/rule-scanner.ts (1)
3-3: LGTM! Clean migration to the sharedparseJsModulehelper — closes the exact duplication gap ARCH-022 calls out.Also applies to: 73-75, 265-267
src/engine/runner.ts (1)
285-372: Guardrail ordering, throw semantics, and cache sharing all verified compliant with ARCH-022.Path safety → plausibility → (in-process TS/JS or interpreter probe → guarded invocation) matches the mandated order exactly; every failure path throws (never returns null/sentinel) with distinguishable messages; the per-
runChecksinterpreterCacheis created once and shared across all ADRs/rules as required. The-IPython isolation flag is confirmed correct — Python's own docs state-c/REPL invocations otherwise prepend an empty string (cwd) tosys.path, which-Iexplicitly suppresses, closing the shadow-module RCE vector this ADR calls out.Also applies to: 422-425, 462-463
src/formats/rules.ts (1)
65-112: LGTM! Accurate, thorough documentation of the per-language AST shape divergence and the TSloccaveat — matches the actual runner.ts behavior and ARCH-022's failure-semantics requirement.tests/engine/runner-ast.test.ts (1)
1-508: LGTM otherwise! Strong coverage of guardrail ordering, sandbox/plausibility rejection, the exit-code-2 failure-visibility contract, and the-Ishadow-import regression — this last one is a genuinely valuable security regression test.tests/engine/js-parser.test.ts (1)
1-53: LGTM!src/engine/ast-support.ts (2)
22-30: LGTM on the plausibility table, Python/Ruby serializer programs (BOM handling and float/complex/bytes fallback torepr()match the documented contract), andinterpreterCandidatesplatform ordering.Also applies to: 38-83
175-195: LGTM!tests/engine/ast-support.test.ts (1)
1-243: Good coverage ofprobeInterpreter,runAstSubprocess,parseAstJson,parseErrorMessage, and end-to-end serializer behavior for both interpreters, with propertest.skipIfgating and temp-dir cleanup.src/helpers/rules-shim.ts (1)
72-112: LGTM! The generated typings correctly expose a singleast(path, language)method, document the throw-not-null contract, and call out the TypeScriptloccaveat (transpiled output vs. source-accurate JavaScript parsing) exactly as required..archgate/adrs/ARCH-004-no-barrel-files.rules.ts (1)
4-58: LGTM!Also applies to: 72-89
.archgate/adrs/ARCH-008-typed-command-options.rules.ts (1)
3-97: LGTM!Also applies to: 106-139, 148-185
.archgate/adrs/ARCH-022-ast-aware-rule-context.rules.ts (1)
118-182: LGTM!.archgate/adrs/ARCH-022-ast-aware-rule-context.md (1)
5-9: LGTM!Also applies to: 40-46, 63-64, 77-78, 110-115
.claude/agent-memory/archgate-developer/project_rules_engine_internals.md (1)
12-12: LGTM! Consistent with the ADR-022 doc and directly informs the loc-usage concern flagged inARCH-022-ast-aware-rule-context.rules.ts.Based on learnings, agent-memory files under
.claude/agent-memory/are not required to have a top-level H1 heading given their established frontmatter convention.Source: Learnings
RuleContext.ast() is now overloaded on the language literal so authors get a precise, autocompletable return type instead of the loose AstNode union: ast(p, "typescript"|"javascript") -> EsTreeProgram ast(p, "python") -> PythonAstModule ast(p, "ruby") -> RubyAstNode ast(p, AstLanguage) -> AstNode (fallback) Adds self-contained public shapes (EsTreeNode/EsTreeProgram, PythonAstNode/PythonAstModule, RubyAstNode) to the ambient rules.d.ts so .rules.ts authors get them with zero imports. The internal meriyah return type is renamed MeriyahProgram to avoid clashing with the public EsTreeProgram. Existing ast() tests now access per-language fields without casts, so a regression to the broad union fails typecheck. The guardrail-ordering rule follows the impl into its new const form. Claude-Session: https://claude.ai/code/session_012tjbzRdRBG1SLVdXvTA9dX Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
There was a problem hiding this comment.
♻️ Duplicate comments (1)
src/engine/runner.ts (1)
196-216: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
.ctsfiles are always parsed as ESM modules, breaking CommonJS-only.ctssyntax.
.ctsis listed as a supported TypeScript extension inAST_LANGUAGE_EXTENSIONS, but the TypeScript branch (lines 199-203) always feeds the transpiled output toparseJsModule(js)with nosourceType, so it defaults to strict module grammar. A.ctsfile using CommonJS-only syntax (e.g. top-levelreturn,with) will fail to parse, exactly the same gap already identified for.js/.cjsand fixed viasourceType: "script"a few lines below (line 209) for the plain-JS branch. This was flagged in a prior review round and is still present in this revision.🐛 Proposed fix — mirror the `.cjs` handling for `.cts`
if (language === "typescript") { const loader = lowerPath.endsWith(".tsx") ? "tsx" : "ts"; const js = new Bun.Transpiler({ loader }).transformSync(source); - return parseJsModule(js) as unknown as EsTreeProgram; + return parseJsModule(js, { + sourceType: lowerPath.endsWith(".cts") ? "script" : "module", + }) as unknown as EsTreeProgram; }Based on learnings, a prior review already raised this exact gap for
.ctsin this file (and suggested a companion test inrunner-ast.test.ts).🤖 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 196 - 216, The TypeScript parsing path in runner.ts still treats .cts output as a module, so CommonJS-only syntax can fail to parse. Update the TypeScript branch in the runner logic that calls Bun.Transpiler.transformSync and parseJsModule so .cts files are parsed with script sourceType instead of the default module grammar, mirroring the existing .cjs handling in the JavaScript branch. Also add or adjust a test in runner-ast.test.ts to cover a .cts file using CommonJS-only syntax.
🤖 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.
Duplicate comments:
In `@src/engine/runner.ts`:
- Around line 196-216: The TypeScript parsing path in runner.ts still treats
.cts output as a module, so CommonJS-only syntax can fail to parse. Update the
TypeScript branch in the runner logic that calls Bun.Transpiler.transformSync
and parseJsModule so .cts files are parsed with script sourceType instead of the
default module grammar, mirroring the existing .cjs handling in the JavaScript
branch. Also add or adjust a test in runner-ast.test.ts to cover a .cts file
using CommonJS-only syntax.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: c92e39af-084e-4fe2-b475-1e1f94c8529d
📒 Files selected for processing (7)
.archgate/adrs/ARCH-022-ast-aware-rule-context.rules.tssrc/engine/js-parser.tssrc/engine/rule-scanner.tssrc/engine/runner.tssrc/formats/rules.tssrc/helpers/rules-shim.tstests/engine/runner-ast.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
- GitHub Check: Cursor Bugbot
- GitHub Check: Smoke Test (Windows) / Windows
⚠️ CI failures not shown inline (4)
GitHub Actions: DCO / 0_DCO Sign-off Check.txt: feat: AST-aware rule context (ctx.ast) for TS/JS/Python/Ruby
Conclusion: failure
##[group]Run base="21829b56e22cbf7442bc4fe66502ba988073c049"
�[36;1mbase="21829b56e22cbf7442bc4fe66502ba988073c049"�[0m
�[36;1mhead="7514ccd76a902bee80b3f40b2a83e10391697728"�[0m
�[36;1mfailed=0�[0m
�[36;1m�[0m
�[36;1mfor sha in $(git rev-list --no-merges "$base".."$head"); do�[0m
�[36;1m if ! git log -1 --format='%B' "$sha" | grep -qiE '^Signed-off-by: .+ <.+>'; then�[0m
�[36;1m echo "::error::Commit $sha is missing a DCO Signed-off-by line."�[0m
GitHub Actions: DCO / DCO Sign-off Check: feat: AST-aware rule context (ctx.ast) for TS/JS/Python/Ruby
Conclusion: failure
##[group]Run base="21829b56e22cbf7442bc4fe66502ba988073c049"
�[36;1mbase="21829b56e22cbf7442bc4fe66502ba988073c049"�[0m
�[36;1mhead="7514ccd76a902bee80b3f40b2a83e10391697728"�[0m
�[36;1mfailed=0�[0m
�[36;1m�[0m
�[36;1mfor sha in $(git rev-list --no-merges "$base".."$head"); do�[0m
�[36;1m if ! git log -1 --format='%B' "$sha" | grep -qiE '^Signed-off-by: .+ <.+>'; then�[0m
�[36;1m echo "::error::Commit $sha is missing a DCO Signed-off-by line."�[0m
GitHub Actions: Code Quality: PR #452 / Analyze (go): Code Quality: PR #452
Conclusion: failure
,"bundleSupportsIncludeLogs":true,"bundleSupportsOverlay":true,"databaseInterpretResultsSupportsSarifRunProperty":true,"featuresInVersionResult":true,"indirectTracingSupportsStaticBinaries":false,"informsAboutUnsupportedPathFilters":true,"supportsPython312":true,"mrvaPackCreate":true,"threatModelOption":true,"traceCommandUseBuildMode":true,"v2ramSizing":true,"mrvaPackCreateMultipleQueries":true,"setsCodeqlRunnerEnvVar":true,"sarifMergeRunsFromEqualCategory":true,"forceOverwrite":true,"generateSummarySymbolMap":true,"pythonDefaultIsToNotExtractStdlib":true,"queryServerRunQueries":true,"queryServerTrimCacheWithMode":true,"builtinExtractorsSpecifyDefaultQueries":true,"bqrsDiffResultSets":true,"bundleSupportsIncludeOption":true,"suppressesMissingFileBaselineWarning":true}}}
CODEQL_ACTION_GO_BINARY: /home/runner/work/_temp/codeql-action-go-tracing/bin/go
CODEQL_RAM: 14575
CODEQL_THREADS: 4
CODEQL_PROXY_HOST:
CODEQL_PROXY_PORT:
CODEQL_PROXY_CA_CERTIFICATE:
CODEQL_PROXY_URLS:
##[endgroup]
##[group]Attempting to automatically build go code
[command]/opt/hostedtoolcache/CodeQL/2.25.6/x64/codeql/codeql database trace-command --use-build-mode --working-dir /home/runner/work/cli/cli /home/runner/work/_temp/codeql_databases/go
Picked up JAVA_TOOL_OPTIONS: -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false
Running command in /home/runner/work/cli/cli: [/opt/hostedtoolcache/CodeQL/2.25.6/x64/codeql/go/tools/autobuild.sh]
[] [build-stderr] 2026/07/05 13:01:00 Autobuilder was built with go1.26.0, environment has go1.24.13
[] [build-stderr] 2026/07/05 13:01:00 LGTM_SRC is /home/runner/work/cli/cli
[] [build-stderr] 2026/07/05 13:01:00 Found no go.work files in the workspace; looking for go.mod files...
[] [build-stderr] 2026/07/05 13:01:00 Found 1 go.mod files in: shims/go/go.mod.
[] [build-stderr] 2026/07/05 13:01:00 Found 1 go.mod file(s).
[] [build-stderr] 2026/07/05 13:01:00 Import path is 'github.com/archgate/cli'
[] [build-stderr] 2026/07/...
GitHub Actions: Code Quality: PR #452 / 5_Analyze (go).txt: Code Quality: PR #452
Conclusion: failure
8 Extracting types for package crypto/aes.
[] [build-stderr] 2026/07/05 13:01:08 Done extracting types for package crypto/aes.
[] [build-stderr] 2026/07/05 13:01:08 Processing package crypto/des.
[] [build-stderr] 2026/07/05 13:01:08 Extracting types for package crypto/des.
[] [build-stderr] 2026/07/05 13:01:08 Done extracting types for package crypto/des.
[] [build-stderr] 2026/07/05 13:01:08 Processing package crypto/internal/fips140/nistec/fiat.
[] [build-stderr] 2026/07/05 13:01:08 Extracting types for package crypto/internal/fips140/nistec/fiat.
[] [build-stderr] 2026/07/05 13:01:08 Done extracting types for package crypto/internal/fips140/nistec/fiat.
[] [build-stderr] 2026/07/05 13:01:08 Processing package crypto/internal/fips140/nistec.
[] [build-stderr] 2026/07/05 13:01:08 Extracting types for package crypto/internal/fips140/nistec.
[] [build-stderr] 2026/07/05 13:01:08 Done extracting types for package crypto/internal/fips140/nistec.
[] [build-stderr] 2026/07/05 13:01:08 Processing package crypto/internal/fips140/ecdh.
[] [build-stderr] 2026/07/05 13:01:08 Extracting types for package crypto/internal/fips140/ecdh.
[] [build-stderr] 2026/07/05 13:01:08 Done extracting types for package crypto/internal/fips140/ecdh.
[] [build-stderr] 2026/07/05 13:01:08 Processing package crypto/internal/fips140/edwards25519/field.
[] [build-stderr] 2026/07/05 13:01:08 Extracting types for package crypto/internal/fips140/edwards25519/field.
[] [build-stderr] 2026/07/05 13:01:08 Done extracting types for package crypto/internal/fips140/edwards25519/field.
[] [build-stderr] 2026/07/05 13:01:08 Processing package crypto/ecdh.
[] [build-stderr] 2026/07/05 13:01:08 Extracting types for package crypto/ecdh.
[] [build-stderr] 2026/07/05 13:01:08 Done extracting types for package crypto/ecdh.
[] [build-stderr] 2026/07/05 13:01:08 Processing package crypto/elliptic.
[] [build-stderr] 2026/07/05 13:01:08 Extracting types for package crypto/elliptic.
[] [build-...
🧰 Additional context used
📓 Path-based instructions (15)
src/**/*.ts
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-002-error-handling.md)
src/**/*.ts: UselogError()fromsrc/helpers/log.tsfor user-facing errors instead of printing errors directly.
Exit with code1for expected failures such as missing config, invalid input, or validation violations.
Let unexpected errors crash naturally so they are treated as internal errors with exit code2.
Include actionable suggestions in user-facing error messages whenever possible.
Write user-facing errors to stderr vialogError(); do not send them to stdout.
WhenfindProjectRoot()returnsnullfor commands that do not require.archgate/, fall back toprocess.cwd()as the path key or working directory.
CatchExitPromptErrorfrom Inquirer in the top-level error boundary and treat it as user cancellation: exit with code130without logging an error or sending it to Sentry.
Do not useconsole.error()directly; uselogError()for consistent formatting.
Do not exit with codes other than0,1,2, or130.
Do not send user-cancellation errors such as InquirerExitPromptErrorto Sentry; filter them inbeforeSend.
src/**/*.ts: UsestyleText(format, text)fromnode:utilfor 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--jsonmust emit machine-readable JSON to stdout with no colors or decorative formatting.
When a command supports--json, useformatJSON()fromsrc/helpers/output.tsfor JSON serialization, and passforcePretty: truewhen the user explicitly provided--json.
UseisAgentContext()fromsrc/helpers/output.tsto 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 withconsole.log(), and send errors, warnings, and debug messages to stderr vialogError(),logWarn(), andlogDebug().
Keep CLI output concise and scannabl...
Files:
src/helpers/rules-shim.tssrc/engine/js-parser.tssrc/engine/rule-scanner.tssrc/engine/runner.tssrc/formats/rules.ts
src/{helpers,engine}/**/*.ts
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-002-error-handling.md)
Do not use
console.log(),console.warn(), orconsole.info()directly in helper or engine files; uselogInfo()orlogWarn()instead. Command files are exempt because they are the I/O layer.
Files:
src/helpers/rules-shim.tssrc/engine/js-parser.tssrc/engine/rule-scanner.tssrc/engine/runner.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, useBun.file()instead offs.readFile()for simple reads.
Prefernode:built-in modules such asnode:util,node:path, andnode:fsover npm alternatives.
Do not use utility libraries for single functions, such as importing lodash for one helper likepick.
Do not use path aliases (tsconfig paths); use relative imports with Bun's native module resolution.
Do not install packages globally during development; usebunxfor one-off tools.
Files:
src/helpers/rules-shim.tssrc/engine/js-parser.tssrc/engine/rule-scanner.tssrc/engine/runner.tssrc/formats/rules.tstests/engine/runner-ast.test.ts
src/**/!(*platform).ts
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-009-platform-detection-helper.md)
src/**/!(*platform).ts: Insrc/TypeScript source files, do not readprocess.platformdirectly; usesrc/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 insrc/TypeScript source.
When behavior differs between Linux and Windows, account for WSL by usingisWSL()rather than assuming `
Files:
src/helpers/rules-shim.tssrc/engine/js-parser.tssrc/engine/rule-scanner.tssrc/engine/runner.tssrc/formats/rules.ts
{src,tests}/**/*.ts
📄 CodeRabbit inference engine (.archgate/adrs/LEGAL-001-spdx-license-headers.md)
{src,tests}/**/*.ts: Every TypeScript source file insrc/andtests/must begin with// SPDX-License-Identifier: Apache-2.0followed by// Copyright 2026 Archgate.
If a TypeScript file has a shebang line (for example#!/usr/bin/env buninsrc/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/rules-shim.tssrc/engine/js-parser.tssrc/engine/rule-scanner.tssrc/engine/runner.tssrc/formats/rules.tstests/engine/runner-ast.test.ts
src/helpers/rules-shim.ts
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-022-ast-aware-rule-context.md)
The generated
.rules.tsshim must mirrorRuleContextwith the same singleast(path, language)method.
Files:
src/helpers/rules-shim.ts
**
⚙️ CodeRabbit configuration file
**: # CLAUDE.mdArchgate 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 wizardValidation Gate
bun run validatemust 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
.githooksrun 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 ../.githooksOpt out of a specific hook:
git config --local hook.<name>.enabled false. Skip all hooks for a single commit:git commit --no-verify.#...
Files:
src/helpers/rules-shim.tssrc/engine/js-parser.tssrc/engine/rule-scanner.tssrc/engine/runner.tssrc/formats/rules.tstests/engine/runner-ast.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.tsfile with automated checks that run viaarchgate check.When reviewing, you must:
- Treat ADR violations as blocking issues, not suggestions.
- Cite the specific ADR ID when flagging a violation (e.g., "Violates ARCH-006").
- Focus on semantic and contextual violations that automated rules cannot catch —
the.rules.tsfiles already cover syntactic/structural patterns.- If you are unsure whether something violates an ADR, flag it as a question
rather than approving it.
Files:
src/helpers/rules-shim.tssrc/engine/js-parser.tssrc/engine/rule-scanner.tssrc/engine/runner.tssrc/formats/rules.tstests/engine/runner-ast.test.ts
src/engine/
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-022-ast-aware-rule-context.md)
Do not expose or use raw subprocess primitives such as
Bun.spawn,Bun.spawnSync, orchild_processdirectly outside the sanctioned AST and git helpers.
Files:
src/engine/js-parser.tssrc/engine/rule-scanner.tssrc/engine/runner.ts
src/engine/rule-scanner.ts
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-022-ast-aware-rule-context.md)
Factor the duplicated
parseModule()invocation into a shared exported parse helper instead of keeping inline copies in the scanner.
Files:
src/engine/rule-scanner.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-022-ast-aware-rule-context.rules.ts
src/engine/runner.ts
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-022-ast-aware-rule-context.md)
src/engine/runner.ts: Implementctx.ast()insidecreateRuleContext()with internal dispatch only; rule authors must not see the dispatch mechanism.
Forlanguage: "typescript"andlanguage: "javascript",ctx.ast()must reuse the existing in-processmeriyahparser and must not spawn a subprocess.
Forlanguage: "python"andlanguage: "ruby",ctx.ast()must invoke the language's standard-library AST facility throughBun.spawnusing array-based arguments only.
ctx.ast()must perform guardrails in this order before any subprocess is spawned: path safety, language plausibility, interpreter availability probe, then guarded invocation.
ctx.ast()must throw on missing interpreter or parse failure; it must not returnnullor another sentinel.
The Python branch must run in isolated mode with-I, and must strip a leading UTF-8 BOM before parsing.
The Python/Ruby interpreter availability probe must be cached once percheckinvocation and must use platform-appropriate candidate executable names.
Do not trustnode.locforlanguage: "typescript"; re-locate reported constructs in the original source before reporting line numbers.
Do not drop the Python-Iflag when refactoring the guarded invocation.
Do not add per-languageRuleContextmethods such aspythonAst()orrubyAst(); expose onlyast(path, language).
Files:
src/engine/runner.ts
src/formats/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Zod schemas are the single source of truth; derive types with
z.infer<>, do not define separate interfaces, usesafeParse(), and reuseAdrFrontmatterSchema.shape.*instead of duplicating enums.
Files:
src/formats/rules.ts
src/formats/rules.ts
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-022-ast-aware-rule-context.md)
RuleContextmust expose a singleast(path, language)method.
Files:
src/formats/rules.ts
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 undertests/mirroring thesrc/directory structure with<module-name>.test.tsnaming.
Use temp directories (mkdtemp) for filesystem tests, and clean them up inafterEachorafterAll.
Close external SDK instances (servers, clients, transports, connections) inafterEachorafterAll, managing their lifecycle in hooks rather than inside individual test bodies.
When a test creates a temporary git repository and runsgit commit, configure localuser.emailanduser.nameimmediately aftergit init.
Test public module interfaces, not private implementation details.
Use descriptive test names that explain the expected behavior.
Every runnabletest()/it()must contain at least oneexpect()assertion; smoke tests must make the contract explicit withexpect(() => fn()).not.toThrow()orawait expect(promise).resolves.toBeUndefined().
Usetest.skip,test.skipIf, ortest.todofor intentionally empty or disabled tests; do not use barereturnor empty callbacks to skip work.
If the firstexpect()is being added to a previously assertion-less test file, addexpectto thebun:testimport.
When mockingfetchin tests, assign directly toglobalThis.fetchand restore the original or usemock.restore()afterward.
WrapspyOn()and inlinemockImplementation()usage intry/finally, or create and restore spies in hooks, somockRestore()always runs.
Only raise a per-test timeout above the globalbun test --timeout 60000; never set a shorter per-test timeout.
Mock first-party modules withimport * as modplusspyOn(mod, "fn"), notmock.module().
When a test needs to redirect user-scope paths, mockos.homedir()instead of relying onHOME/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/runner-ast.test.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 mutatingprocess.platformdirectly.
Files:
tests/engine/runner-ast.test.ts
🧠 Learnings (1)
📚 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/rules-shim.ts
🔇 Additional comments (7)
.archgate/adrs/ARCH-022-ast-aware-rule-context.rules.ts (1)
59-86: LGTM!src/engine/js-parser.ts (1)
27-45: LGTM!src/engine/runner.ts (1)
143-195: LGTM!Also applies to: 217-251, 373-375, 425-428, 465-466
src/formats/rules.ts (1)
65-133: LGTM!Also applies to: 146-168
src/helpers/rules-shim.ts (1)
72-137: LGTM!Also applies to: 148-169
tests/engine/runner-ast.test.ts (1)
93-511: LGTM!src/engine/rule-scanner.ts (1)
3-3: 🎯 Functional CorrectnessClean type-level consolidation. The shared
parseJsModule()helper keeps the scanner behavior unchanged.
…ds narrow
With per-language overloads returning EsTreeProgram / PythonAstModule
/ RubyAstNode, the `as { body: ... }` and `as { _type: ... }` casts
throughout rules, tests, and docs examples are dead weight. Removes
them and replaces local EsNode/EstreeStatement interfaces with the
ambient EsTreeNode/PythonAstNode from rules.d.ts, so consumers see
how the typed API is intended to be used — cast-free.
Walk helpers still cast `unknown` children to the node type; these
are inherent to tree traversal and cannot be removed without a
polymorphic tree library.
Claude-Session: https://claude.ai/code/session_012tjbzRdRBG1SLVdXvTA9dX
Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
Bugbot couldn't run - usage limit reachedBugbot 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_24840329-e88a-41a6-9e7f-cc1987a3538a) |
Claude-Session: https://claude.ai/code/session_012tjbzRdRBG1SLVdXvTA9dX Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
Bugbot couldn't run - usage limit reachedBugbot 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_80d9a19d-dbbd-4d98-9254-9ee5744e11f1) |
- probeInterpreter: add 5s timeout so a stalled binary (e.g. macOS Xcode dialog on python3) cannot hang the cached probe indefinitely - runAstSubprocess: await proc.exited after kill on timeout so the subprocess is confirmed dead before throwing - ARCH-008 rules: catch per-file parse errors so one unparseable file does not abort the entire rule via Promise.all rejection - ARCH-004 barrel fallback: split on newlines instead of semicolons so a semicolon inside a module specifier string does not break the type-only barrel heuristic Claude-Session: https://claude.ai/code/session_012tjbzRdRBG1SLVdXvTA9dX Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
rhuanbarreto
left a comment
There was a problem hiding this comment.
Re: guardrail-ordering rule uses loc — This is intentional. The rule compares loc positions against each other to verify ordering (is safePath before AST_LANGUAGE_EXTENSIONS before probeInterpreter before runAstSubprocess?). Transpilation shifts absolute line numbers but preserves relative ordering — statement A before statement B in the source is still before it after transpilation. The ARCH-022 Don't ("don't trust loc for reporting") applies to reporting lines to the user, not to internal ordinal comparisons.
Re: inject timeout for testing / no timeout test — The 15s timeout path is intentionally untested (confirmed in the adversarial review as an acceptable gap). Making it injectable is a valid follow-up but out of scope for this PR.
Re: missing sourceType: "script" test in js-parser.test.ts — This is tested through runChecks in tests/engine/runner-ast.test.ts (the .cjs sloppy-mode test), which exercises the full sourceType: "script" path end-to-end including globalReturn.
Re: .cts gap — Agreed this is a valid follow-up. .cts files are rare in practice; tracking separately.
Re: BOM test — The BOM codec fix is tested indirectly through the Python/Ruby serializer end-to-end tests (any BOM-bearing file on a Windows machine with default notepad encoding would exercise it). A dedicated BOM fixture test is nice-to-have but not blocking.
Bugbot couldn't run - usage limit reachedBugbot 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_75d988a3-cf61-47fb-b34c-ecae28afed06) |
Code Coverage
Full HTML report available in workflow artifacts. Per-directory breakdown
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
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 @.archgate/adrs/ARCH-004-no-barrel-files.rules.ts:
- Around line 43-48: The fallback in isTypeOnlyBarrel only checks each trimmed
line prefix, so multi-line type-only re-exports like export type { ... } from
"./x" are incorrectly rejected when continuation lines are present. Update the
detection logic in isTypeOnlyBarrel to treat the whole statement as one unit
before checking, or use token-based parsing, so grouped export type blocks still
count as barrel files even when formatted across multiple lines.
🪄 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: a667b701-8226-41cc-8ec3-05abe8878803
📒 Files selected for processing (11)
.archgate/adrs/ARCH-004-no-barrel-files.rules.ts.archgate/adrs/ARCH-008-typed-command-options.rules.tsdocs/public/llms-full.txtdocs/src/content/docs/guides/writing-rules.mdxdocs/src/content/docs/nb/guides/writing-rules.mdxdocs/src/content/docs/nb/reference/rule-api.mdxdocs/src/content/docs/pt-br/guides/writing-rules.mdxdocs/src/content/docs/pt-br/reference/rule-api.mdxdocs/src/content/docs/reference/rule-api.mdxsrc/engine/ast-support.tstests/engine/runner-ast.test.ts
💤 Files with no reviewable changes (8)
- docs/src/content/docs/reference/rule-api.mdx
- docs/src/content/docs/pt-br/reference/rule-api.mdx
- docs/src/content/docs/pt-br/guides/writing-rules.mdx
- docs/public/llms-full.txt
- docs/src/content/docs/guides/writing-rules.mdx
- docs/src/content/docs/nb/reference/rule-api.mdx
- tests/engine/runner-ast.test.ts
- docs/src/content/docs/nb/guides/writing-rules.mdx
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
- GitHub Check: Cloudflare Pages
⚠️ CI failures not shown inline (7)
GitHub Actions: DCO / 0_DCO Sign-off Check.txt: feat: ast-aware rule context (ctx.ast) for TS/JS/Python/Ruby
Conclusion: failure
##[group]Run base="21829b56e22cbf7442bc4fe66502ba988073c049"
�[36;1mbase="21829b56e22cbf7442bc4fe66502ba988073c049"�[0m
�[36;1mhead="1ede26be4da7ad38d82e7967a9cc7bfa90e77f92"�[0m
�[36;1mfailed=0�[0m
�[36;1m�[0m
�[36;1mfor sha in $(git rev-list --no-merges "$base".."$head"); do�[0m
�[36;1m if ! git log -1 --format='%B' "$sha" | grep -qiE '^Signed-off-by: .+ <.+>'; then�[0m
�[36;1m echo "::error::Commit $sha is missing a DCO Signed-off-by line."�[0m
GitHub Actions: Validate / 0_Validate Code.txt: feat: ast-aware rule context (ctx.ast) for TS/JS/Python/Ruby
Conclusion: failure
##[group]Run # shim-tests is skipped when no shim files changed — treat skipped as success.
�[36;1m# shim-tests is skipped when no shim files changed — treat skipped as success.�[0m
�[36;1mSHIM_OK="skipped"�[0m
�[36;1mif [[ "$SHIM_OK" == "skipped" ]]; then SHIM_OK="success"; fi�[0m
�[36;1m�[0m
�[36;1mif [[ "success" != "success" ]] || \�[0m
�[36;1m [[ "$SHIM_OK" != "success" ]] || \�[0m
�[36;1m [[ "success" != "success" ]] || \�[0m
�[36;1m [[ "success" != "success" ]] || \�[0m
�[36;1m [[ "success" != "success" ]] || \�[0m
�[36;1m [[ "success" != "success" ]] || \�[0m
�[36;1m [[ "success" != "success" ]]; then�[0m
�[36;1m echo "::error::One or more jobs failed:"�[0m
GitHub Actions: Validate / 1_Coverage Report.txt: feat: ast-aware rule context (ctx.ast) for TS/JS/Python/Ruby
Conclusion: failure
##[group]Run COVERAGE="90.1"
�[36;1mCOVERAGE="90.1"�[0m
�[36;1mMIN_COVERAGE=90�[0m
�[36;1mBELOW=$(awk "BEGIN{print ($COVERAGE < $MIN_COVERAGE) ? 1 : 0}")�[0m
�[36;1mif [ "$BELOW" = "1" ]; then�[0m
�[36;1m echo "::error::Code coverage is ${COVERAGE}%, which is below the minimum threshold of ${MIN_COVERAGE}%."�[0m
GitHub Actions: Validate / 3_Smoke Test (Linux) _ Linux.txt: feat: ast-aware rule context (ctx.ast) for TS/JS/Python/Ruby
Conclusion: failure
##[group]Run if [ -z "$ARCHGATE_VERSION" ]; then
�[36;1mif [ -z "$ARCHGATE_VERSION" ]; then�[0m
�[36;1m # Find the newest release that already has the Linux asset uploaded.�[0m
�[36;1m # On release-commit pushes the Validate and Release workflows start�[0m
�[36;1m # concurrently, so the latest tag may exist before its binaries are�[0m
�[36;1m # uploaded by release-binaries.yml — hitting a 404.�[0m
�[36;1m asset="archgate-linux-x64.tar.gz"�[0m
�[36;1m tags="$(gh release list --limit 5 --json tagName --jq '.[].tagName' 2>/dev/null || true)"�[0m
�[36;1m if [ -z "$tags" ]; then�[0m
�[36;1m echo "::warning::No releases found, skipping install.sh smoke test"�[0m
�[36;1m exit 0�[0m
�[36;1m fi�[0m
�[36;1m found=""�[0m
�[36;1m for tag in $tags; do�[0m
�[36;1m assets="$(gh release view "$tag" --json assets --jq '.assets[].name' 2>/dev/null || true)"�[0m
�[36;1m if echo "$assets" | grep -qF "$asset"; then�[0m
�[36;1m ARCHGATE_VERSION="$tag"�[0m
�[36;1m found=1�[0m
�[36;1m break�[0m
�[36;1m fi�[0m
�[36;1m done�[0m
�[36;1m if [ -z "$found" ]; then�[0m
�[36;1m echo "::warning::No release with $asset found, skipping install.sh smoke test"�[0m
�[36;1m exit 0�[0m
�[36;1m fi�[0m
�[36;1m export ARCHGATE_VERSION�[0m
�[36;1mfi�[0m
�[36;1mecho "Testing install.sh with version $ARCHGATE_VERSION"�[0m
�[36;1m�[0m
�[36;1mARCHGATE_INSTALL_DIR="$(mktemp -d)"�[0m
�[36;1mexport ARCHGATE_INSTALL_DIR�[0m
�[36;1m�[0m
�[36;1msh install.sh�[0m
�[36;1m�[0m
�[36;1mif [ ! -f "$ARCHGATE_INSTALL_DIR/archgate" ]; then�[0m
�[36;1m echo "::error::archgate binary not found at $ARCHGATE_INSTALL_DIR/archgate after install"�[0m
GitHub Actions: Validate / 4_Lint, Test & Check.txt: feat: ast-aware rule context (ctx.ast) for TS/JS/Python/Ruby
Conclusion: failure
##[group]Run bun run docs/scripts/generate-llms-full.ts
�[36;1mbun run docs/scripts/generate-llms-full.ts�[0m
�[36;1mgit diff --exit-code docs/public/llms-full.txt || {�[0m
�[36;1m echo "::error::docs/public/llms-full.txt is out of date. Run 'bun run docs/scripts/generate-llms-full.ts' and commit the result."�[0m
GitHub Actions: Validate / 5_Zizmor (Workflow Security).txt: feat: ast-aware rule context (ctx.ast) for TS/JS/Python/Ruby
Conclusion: failure
##[group]Run "${GITHUB_ACTION_PATH}/action.sh"
�[36;1m"${GITHUB_ACTION_PATH}/action.sh"�[0m
shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0}
env:
ARCHGATE_TELEMETRY: 0
GHA_ZIZMOR_INPUTS: .
GHA_ZIZMOR_ONLINE_AUDITS: true
GHA_ZIZMOR_PERSONA: regular
GHA_ZIZMOR_MIN_SEVERITY:
GHA_ZIZMOR_MIN_CONFIDENCE:
GHA_ZIZMOR_VERSION: latest
GHA_ZIZMOR_***REDACTED***
GHA_ZIZMOR_ADVANCED_SECURITY: true
GHA_ZIZMOR_COLOR: true
GHA_ZIZMOR_ANNOTATIONS: false
GHA_ZIZMOR_CONFIG: zizmor.yml
GHA_ZIZMOR_FAIL_ON_NO_INPUTS: true
##[endgroup]
Unable to find image 'ghcr.io/zizmorcore/zizmor:latest@sha256:***REDACTED***' locally
ghcr.io/zizmorcore/zizmor@sha256:***REDACTED***: Pulling from zizmorcore/zizmor
0514fcfd8680: Pulling fs layer
7677d1f93ece: Pulling fs layer
d3cc9d64a2f9: Pulling fs layer
c920184a3208: Pulling fs layer
9201e51fe8c1: Pulling fs layer
69cbafd2417b: Pulling fs layer
9041c2dcdd9f: Pulling fs layer
8d99fd8fea9c: Pulling fs layer
5f460b97a17e: Pulling fs layer
cdea145aa115: Pulling fs layer
5bb3233a44f9: Pulling fs layer
85c67978dd22: Pulling fs layer
8d99fd8fea9c: Waiting
5f460b97a17e: Waiting
5bb3233a44f9: Waiting
c920184a3208: Waiting
85c67978dd22: Waiting
9201e51fe8c1: Waiting
cdea145aa115: Waiting
69cbafd2417b: Waiting
9041c2dcdd9f: Waiting
0514fcfd8680: Verifying Checksum
0514fcfd8680: Download complete
d3cc9d64a2f9: Verifying Checksum
d3cc9d64a2f9: Download complete
7677d1f93ece: Verifying Checksum
7677d1f93ece: Download complete
0514fcfd8680: Pull complete
c920184a3208: Download complete
9201e51fe8c1: Download complete
69cbafd2417b: Verifying Checksum
69cbafd2417b: Download complete
5f460b97a17e: Verifying Checksum
5f460b97a17e: Download complete
8d99fd8fea9c: Verifying Checksum
8d99fd8fea9c: Download complete
9041c2dcdd9f: Verifying Checksum
9041c2dcdd9f: Download complete
7677d1f93ece: Pull complete
cdea145aa115: Verifying Checksum
cdea145aa115: Download complete
5bb32...
GitHub Actions: Code Quality: PR #452 / 1_Analyze (go).txt: Code Quality: PR #452
Conclusion: failure
Extracting types for package crypto/aes.
[] [build-stderr] 2026/07/05 16:04:16 Done extracting types for package crypto/aes.
[] [build-stderr] 2026/07/05 16:04:16 Processing package crypto/des.
[] [build-stderr] 2026/07/05 16:04:16 Extracting types for package crypto/des.
[] [build-stderr] 2026/07/05 16:04:16 Done extracting types for package crypto/des.
[] [build-stderr] 2026/07/05 16:04:16 Processing package crypto/internal/fips140/nistec/fiat.
[] [build-stderr] 2026/07/05 16:04:16 Extracting types for package crypto/internal/fips140/nistec/fiat.
[] [build-stderr] 2026/07/05 16:04:16 Done extracting types for package crypto/internal/fips140/nistec/fiat.
[] [build-stderr] 2026/07/05 16:04:16 Processing package crypto/internal/fips140/nistec.
[] [build-stderr] 2026/07/05 16:04:16 Extracting types for package crypto/internal/fips140/nistec.
[] [build-stderr] 2026/07/05 16:04:16 Done extracting types for package crypto/internal/fips140/nistec.
[] [build-stderr] 2026/07/05 16:04:16 Processing package crypto/internal/fips140/ecdh.
[] [build-stderr] 2026/07/05 16:04:16 Extracting types for package crypto/internal/fips140/ecdh.
[] [build-stderr] 2026/07/05 16:04:16 Done extracting types for package crypto/internal/fips140/ecdh.
[] [build-stderr] 2026/07/05 16:04:16 Processing package crypto/internal/fips140/edwards25519/field.
[] [build-stderr] 2026/07/05 16:04:16 Extracting types for package crypto/internal/fips140/edwards25519/field.
[] [build-stderr] 2026/07/05 16:04:16 Done extracting types for package crypto/internal/fips140/edwards25519/field.
[] [build-stderr] 2026/07/05 16:04:16 Processing package crypto/ecdh.
[] [build-stderr] 2026/07/05 16:04:16 Extracting types for package crypto/ecdh.
[] [build-stderr] 2026/07/05 16:04:16 Done extracting types for package crypto/ecdh.
[] [build-stderr] 2026/07/05 16:04:16 Processing package crypto/elliptic.
[] [build-stderr] 2026/07/05 16:04:16 Extracting types for package crypto/elliptic.
[] [build-st...
🧰 Additional context used
📓 Path-based instructions (8)
.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-004-no-barrel-files.rules.ts.archgate/adrs/ARCH-008-typed-command-options.rules.ts
src/**/*.ts
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-002-error-handling.md)
src/**/*.ts: UselogError()fromsrc/helpers/log.tsfor user-facing errors instead of printing errors directly.
Exit with code1for expected failures such as missing config, invalid input, or validation violations.
Let unexpected errors crash naturally so they are treated as internal errors with exit code2.
Include actionable suggestions in user-facing error messages whenever possible.
Write user-facing errors to stderr vialogError(); do not send them to stdout.
WhenfindProjectRoot()returnsnullfor commands that do not require.archgate/, fall back toprocess.cwd()as the path key or working directory.
CatchExitPromptErrorfrom Inquirer in the top-level error boundary and treat it as user cancellation: exit with code130without logging an error or sending it to Sentry.
Do not useconsole.error()directly; uselogError()for consistent formatting.
Do not exit with codes other than0,1,2, or130.
Do not send user-cancellation errors such as InquirerExitPromptErrorto Sentry; filter them inbeforeSend.
src/**/*.ts: UsestyleText(format, text)fromnode:utilfor 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--jsonmust emit machine-readable JSON to stdout with no colors or decorative formatting.
When a command supports--json, useformatJSON()fromsrc/helpers/output.tsfor JSON serialization, and passforcePretty: truewhen the user explicitly provided--json.
UseisAgentContext()fromsrc/helpers/output.tsto 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 withconsole.log(), and send errors, warnings, and debug messages to stderr vialogError(),logWarn(), andlogDebug().
Keep CLI output concise and scannabl...
Files:
src/engine/ast-support.ts
src/{helpers,engine}/**/*.ts
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-002-error-handling.md)
Do not use
console.log(),console.warn(), orconsole.info()directly in helper or engine files; uselogInfo()orlogWarn()instead. Command files are exempt because they are the I/O layer.
Files:
src/engine/ast-support.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, useBun.file()instead offs.readFile()for simple reads.
Prefernode:built-in modules such asnode:util,node:path, andnode:fsover npm alternatives.
Do not use utility libraries for single functions, such as importing lodash for one helper likepick.
Do not use path aliases (tsconfig paths); use relative imports with Bun's native module resolution.
Do not install packages globally during development; usebunxfor one-off tools.
Files:
src/engine/ast-support.ts
src/**/!(*platform).ts
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-009-platform-detection-helper.md)
src/**/!(*platform).ts: Insrc/TypeScript source files, do not readprocess.platformdirectly; usesrc/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 insrc/TypeScript source.
When behavior differs between Linux and Windows, account for WSL by usingisWSL()rather than assuming `
Files:
src/engine/ast-support.ts
{src,tests}/**/*.ts
📄 CodeRabbit inference engine (.archgate/adrs/LEGAL-001-spdx-license-headers.md)
{src,tests}/**/*.ts: Every TypeScript source file insrc/andtests/must begin with// SPDX-License-Identifier: Apache-2.0followed by// Copyright 2026 Archgate.
If a TypeScript file has a shebang line (for example#!/usr/bin/env buninsrc/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/engine/ast-support.ts
src/engine/**
📄 CodeRabbit inference engine (.archgate/adrs/ARCH-022-ast-aware-rule-context.md)
Do not introduce unsanctioned
Bun.spawn/Bun.spawnSynccalls orchild_processimports anywhere in the engine outside the approved helpers.
Files:
src/engine/ast-support.ts
**
⚙️ CodeRabbit configuration file
**: # CLAUDE.mdArchgate 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 wizardValidation Gate
bun run validatemust 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
.githooksrun 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 ../.githooksOpt out of a specific hook:
git config --local hook.<name>.enabled false. Skip all hooks for a single commit:git commit --no-verify.#...
Files:
src/engine/ast-support.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.tsfile with automated checks that run viaarchgate check.When reviewing, you must:
- Treat ADR violations as blocking issues, not suggestions.
- Cite the specific ADR ID when flagging a violation (e.g., "Violates ARCH-006").
- Focus on semantic and contextual violations that automated rules cannot catch —
the.rules.tsfiles already cover syntactic/structural patterns.- If you are unsure whether something violates an ADR, flag it as a question
rather than approving it.
Files:
src/engine/ast-support.ts
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: archgate/cli
Timestamp: 2026-07-06T01:59:20.557Z
Learning: Do not add tree-sitter, web-tree-sitter, or any other new production dependency for this ADR.
Learnt from: CR
Repo: archgate/cli
Timestamp: 2026-07-06T01:59:20.557Z
Learning: Do not normalize Python or Ruby AST output into a single ESTree-like shape; `ctx.ast()` may return language-native node shapes.
🔇 Additional comments (3)
.archgate/adrs/ARCH-008-typed-command-options.rules.ts (1)
101-106: LGTM!Also applies to: 145-150
src/engine/ast-support.ts (2)
110-150: LGTM!
176-182: 🩺 Stability & AvailabilityTimeout branch still leaves stream readers unsettled. If
new Response(proc.stdout).text()ornew Response(proc.stderr).text()can reject afterproc.kill(), this path can turn a timeout into an unhandled rejection.await Promise.allSettled([stdoutPromise, stderrPromise])before throwing.
Track brace depth in isTypeOnlyBarrel so continuation lines inside
destructured export type { A, B, } blocks are recognized as part of
the enclosing import/export statement, not as separate non-barrel lines.
Claude-Session: https://claude.ai/code/session_01H7XPPVhN1t6HzH8F3xN4DP
Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
Bugbot couldn't run - usage limit reachedBugbot 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_c0897a2b-6629-48c7-81bf-7951b26299db) |
|
@coderabbitai approve |
✅ Action performedComments resolved and changes approved. |

Summary
Implements the AST-Aware Rule Context feature from
.archgate/PRDs/ast-aware-rule-context.md, governed by ARCH-022..rules.tsauthors get a single newRuleContextmethod:Structural (AST-based) checks now replace fragile regex/line heuristics — for TypeScript/JavaScript in-process, and for Python/Ruby via each interpreter's own standard-library AST facility.
Delivers the full four-language scope (TS/JS + Python/Ruby together), satisfying every PRD success criterion in one PR.
What's included
Engine
ctx.ast()onRuleContext, dispatched entirely insidecreateRuleContext().meriyahparser (src/engine/js-parser.ts) — the duplicatedparseModule()calls inrule-scanner.tsare factored out as ARCH-022 mandates. TypeScript is transpiled first;.cjsparses in sloppy script mode.Bun.spawnsubprocesses running the stdlibastmodule /Ripper, serialized to JSON. Zero new dependencies.null) on parse failure or missing interpreter, with distinguishable messages; rides the existing per-rule isolation + exit-code-2 category.Dogfooding
ctx.ast()— the two rules ARCH-022 names as motivating consumers. The ARCH-008 rewrite now catches multi-line.option()calls the old per-line regex missed.rules: truewith four companion checks (guardrail ordering — itself parsed viactx.ast()— subprocess containment, single-method, and Python-Iisolation).Docs:
ctx.ast()reference +AstNodeshapes, interpreter-PATH callouts, and one example rule per language, translated intonb+pt-br.Tests: unit coverage for the parser/probe/subprocess/serializers +
runChecksintegration (all four guardrails, failure semantics, real Python/Ruby structural rules).Adversarial review
A multi-lens review (ADR compliance, sandbox/security, cross-platform, serializer correctness, test quality, docs) with per-finding adversarial verification surfaced and fixed:
sys.pathcwd shadowing (a plantedast.py). Fixed withpython -Iisolated mode; verified end-to-end against a live shadow, and now guarded by both an integration test and the staticpython-subprocess-isolatedrule.pylauncher probing;.cjssloppy-mode parsing; UTF-8 BOM stripping; ARCH-008 3-arg.option()coverage regression; and the transpiled-loccaveat documented across code, ADR, and all locales.Verification
bun run validategreen: 1388 pass / 0 fail, 43/43 ADR rules pass, 0 warnings, build compiles.@reviewerskill: APPROVED across Architecture / General / Legal domains.Closes the PRD; ARCH-022 amended to reflect shipped state.
https://claude.ai/code/session_012tjbzRdRBG1SLVdXvTA9dX