Skip to content

refactor: remove all as type assertions from source code#459

Merged
rhuanbarreto merged 10 commits into
mainfrom
refactor/remove-as-type-assertions
Jul 7, 2026
Merged

refactor: remove all as type assertions from source code#459
rhuanbarreto merged 10 commits into
mainfrom
refactor/remove-as-type-assertions

Conversation

@rhuanbarreto

Copy link
Copy Markdown
Contributor

Summary

  • Remove every as Type assertion from src/ (70+ instances across 23 files), replacing with type-safe alternatives
  • Add Zod schemas for all external data boundaries (HTTP responses, config files, session transcripts)
  • Add type guard functions for AST walking in the rule scanner engine
  • Use Proxy for function patching instead of signature casts
  • Properly type Commander option returns and function overloads

Approach by category

Category Approach Files
HTTP responses Zod schemas auth, repo-probe, binary-upgrade, signup
Config files (read-modify-write) Typed Zod schemas with .passthrough() claude-settings, vscode-settings, opencode-settings
YAML/JSONL parsing Zod schemas session-context, session-context-copilot, adr, pack, telemetry-config
AST walking Type guard functions (isAstNode, childNode, strProp, boolProp) rule-scanner
Function overloads Promise<any> return for implementation runner
Function patching Proxy with apply/get traps prompt
Commander options Proper type narrowing, typed getExitCode return cli, check, reporter
Property access .some(), Object.entries(), Object.fromEntries() project-config, adr-writer, stack-detect

Result

  • 0 as Type assertions in src/ (only as const and import as remain)
  • 1390/1390 tests pass
  • 43/43 ADR rules pass

Test plan

  • bun run validate passes (lint, typecheck, format, test, ADR check, knip, build)
  • All 43 ADR automated rules pass
  • All 1390 tests pass with 0 failures
  • Verify no behavioral regressions in CLI commands

https://claude.ai/code/session_01P2kzr4bcBvg7AtaRhyHdMa

@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

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

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

(requestId: serverGenReqId_48d4a800-b42c-46a1-b3e4-2735d2185602)

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

Next review available in: 9 minutes

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

How can I continue?

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

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

How do review limits work?

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

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

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9c8cb3dd-ba8c-4404-b6e9-6c184ef7f4b6

📥 Commits

Reviewing files that changed from the base of the PR and between 985c4a5 and 7562794.

📒 Files selected for processing (33)
  • .archgate/adrs/ARCH-022-ast-aware-rule-context.rules.ts
  • src/cli.ts
  • src/commands/adr/create.ts
  • src/commands/check.ts
  • src/commands/init.ts
  • src/commands/plugin/install.ts
  • src/commands/plugin/url.ts
  • src/engine/ast-support.ts
  • src/engine/git-files.ts
  • src/engine/reporter.ts
  • src/engine/rule-scanner.ts
  • src/engine/runner.ts
  • src/formats/adr.ts
  • src/formats/pack.ts
  • src/helpers/adr-writer.ts
  • src/helpers/auth.ts
  • src/helpers/binary-upgrade.ts
  • src/helpers/claude-settings.ts
  • src/helpers/init-project.ts
  • src/helpers/login-flow.ts
  • src/helpers/opencode-settings.ts
  • src/helpers/project-config.ts
  • src/helpers/prompt.ts
  • src/helpers/repo-probe.ts
  • src/helpers/session-context-copilot.ts
  • src/helpers/session-context-opencode.ts
  • src/helpers/session-context.ts
  • src/helpers/signup.ts
  • src/helpers/stack-detect.ts
  • src/helpers/telemetry-config.ts
  • src/helpers/vscode-settings.ts
  • tests/helpers/claude-settings.test.ts
  • tests/helpers/vscode-settings.test.ts
📝 Walkthrough

Walkthrough

Changes

This PR replaces multiple unsafe casts with Zod-based validation and stricter typing across CLI, engine, formats, auth, settings, session parsing, and repo probing. It also adds a --max-warnings option to check, refines AST handling in the rule scanner and runner, adjusts prompt stream patching, updates binary download buffer handling, and revises related tests for the new parsing behavior.

Compact Metadata
Type: Refactor / Validation
Impact area: CLI, engine, formats, helpers, tests
Suggested labels: refactor, type-safety, zod-validation

Sequence Diagram(s)

Not applicable beyond the hidden artifact diagrams.

Related Issues: None specified
Related PRs: None specified
Suggested reviewers: Not determined from provided information

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main refactor: removing type assertions from source code.
Description check ✅ Passed The description directly matches the changeset and explains the refactor categories and goals.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

❤️ Share

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

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 7, 2026

Copy link
Copy Markdown

Deploying archgate-cli with  Cloudflare Pages  Cloudflare Pages

Latest commit: 7562794
Status: ✅  Deploy successful!
Preview URL: https://6e20b831.archgate-cli.pages.dev
Branch Preview URL: https://refactor-remove-as-type-asse.archgate-cli.pages.dev

View logs

@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

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

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

(requestId: serverGenReqId_4e1b9df2-95d1-4e5b-aef9-abf7ffe1fbc5)

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Code Coverage

Metric Value
Lines 90.5% (7773 / 8588)
Threshold 90% minimum — met
Platforms Linux + Windows

Full HTML report available in workflow artifacts.

Per-directory breakdown
Directory Coverage Lines
src/commands/ 88.7% 2004 / 2259
src/engine/ 91.3% 1646 / 1803
src/formats/ 98.7% 148 / 150
src/helpers/ 90.8% 3975 / 4376

@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

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

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

(requestId: serverGenReqId_47f7c7a1-b8e9-4be8-b133-c075b1238216)

@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

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

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

(requestId: serverGenReqId_a123ed46-0475-4f87-85f4-5d19ff8226a1)

@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

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

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

(requestId: serverGenReqId_041989e0-60ca-44b9-b998-bef323c5573a)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/formats/pack.ts`:
- Around line 33-35: Update parsePackMetadata() to treat malformed pack metadata
as a user-facing validation error instead of letting PackMetadataSchema.parse()
throw a ZodError. Mirror the pattern used by parseAdr(): use
PackMetadataSchema.safeParse() on Bun.YAML.parse(raw), and when validation fails
throw a UserError populated with the schema’s error messages. Keep the fix
localized to parsePackMetadata() and any nearby error handling that formats the
validation message.

In `@src/helpers/auth.ts`:
- Around line 213-215: The error-body handling in the auth flow should not
assume response.json() returns an object, since ErrorResponseSchema.parse(...)
can throw and mask the intended UserError/signup path. Update the parsing in the
relevant auth helper around ErrorResponseSchema to use safeParse on the JSON
result, and fall back to an empty object when the body is a string, array, or
otherwise invalid. Keep the fix localized to the response parsing logic so the
existing error flow continues through the same auth/signup functions.

In `@src/helpers/binary-upgrade.ts`:
- Around line 95-97: `fetchLatestGitHubVersion()` currently uses
`GitHubReleaseSchema.parse(...)`, which can throw and break the null-on-failure
contract; update the schema handling in this function to use `safeParse()`
instead, and return `null` when validation fails so the existing `upgrade.ts`
`if (!tag)` flow continues to handle bad responses gracefully. Keep the change
localized around `GitHubReleaseSchema` parsing and preserve the existing
`logDebug`/return behavior for valid responses.

In `@src/helpers/claude-settings.ts`:
- Around line 8-23: The whole-object validation in ClaudeSettingsSchema can
cause configureClaudeSettings to treat a partially invalid settings file as
empty and overwrite unrelated user data. Update the settings read/merge flow to
validate fields independently, or otherwise preserve the existing object when
agent or permissions fail validation, so existing custom keys and valid
permission entries are not discarded. Use ClaudeSettingsSchema,
ClaudePermissionsSchema, and the configureClaudeSettings merge logic to locate
the fix.

In `@src/helpers/opencode-settings.ts`:
- Around line 27-31: The whole-object safeParse flow in OpencodeConfigSchema
causes data loss when default_agent is invalid because the entire parsed object
is discarded and configureOpencodeSettings then rewrites opencode.json without
the user’s other passthrough keys. Update the parsing/merge logic in
configureOpencodeSettings to preserve the existing config object even when
default_agent fails validation, and only sanitize or drop the invalid
default_agent field while keeping all other keys intact.

In `@src/helpers/signup.ts`:
- Around line 70-72: `requestSignup()` currently uses `z.object(...).parse(...)`
on the response body, which can still throw for valid JSON with an invalid
`token` and break the `SignupResult` flow. Replace this parsing step with
`safeParse()` on the existing zod schema so the function handles malformed
`token` values gracefully and continues returning the expected `{ ok, token }`
shape without escaping errors.

In `@src/helpers/vscode-settings.ts`:
- Around line 16-20: The whole-object validation in VscodeSettingsSchema and
addMarketplaceToUserSettings can erase unrelated VS Code settings when parsing
fails. Change the flow so malformed or unexpected values for
"chat.plugins.marketplaces" do not reset the entire settings object; instead,
preserve the existing settings and only normalize/repair that single key before
writing. Use VscodeSettingsSchema, VscodeUserSettings, and
addMarketplaceToUserSettings as the main touchpoints, and keep passthrough
behavior so unrelated entries in settings.json are never dropped.
- Around line 152-156: The VS Code settings loader in the helper that reads
settingsPath should handle malformed JSONC the same way as the sibling helpers
do. Wrap the Bun.JSONC.parse/content read path in try/catch inside the existing
settings merge flow, and on parse or read failure leave existing as an empty
object so the merge remains additive and resilient. Use the symbols
VscodeSettingsSchema, Bun.JSONC.parse, and settingsPath to locate the fix.
🪄 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: 3f4e7bca-44fa-47e7-a11b-64bdee2be669

📥 Commits

Reviewing files that changed from the base of the PR and between 67d0e2a and 17fd869.

📒 Files selected for processing (25)
  • src/cli.ts
  • src/commands/adr/create.ts
  • src/commands/check.ts
  • src/engine/ast-support.ts
  • src/engine/reporter.ts
  • src/engine/rule-scanner.ts
  • src/engine/runner.ts
  • src/formats/adr.ts
  • src/formats/pack.ts
  • src/helpers/adr-writer.ts
  • src/helpers/auth.ts
  • src/helpers/binary-upgrade.ts
  • src/helpers/claude-settings.ts
  • src/helpers/opencode-settings.ts
  • src/helpers/project-config.ts
  • src/helpers/prompt.ts
  • src/helpers/repo-probe.ts
  • src/helpers/session-context-copilot.ts
  • src/helpers/session-context.ts
  • src/helpers/signup.ts
  • src/helpers/stack-detect.ts
  • src/helpers/telemetry-config.ts
  • src/helpers/vscode-settings.ts
  • tests/helpers/claude-settings.test.ts
  • tests/helpers/vscode-settings.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Smoke Test (Windows) / Windows
  • GitHub Check: Cloudflare Pages
⚠️ CI failures not shown inline (4)

GitHub Actions: DCO / DCO Sign-off Check: refactor: remove all as type assertions from source code

Conclusion: failure

View job details

##[group]Run base="67d0e2ace0607873c32894f24c2d5e6c6bb13f1d"
 �[36;1mbase="67d0e2ace0607873c32894f24c2d5e6c6bb13f1d"�[0m
 �[36;1mhead="17fd8691cb78f93605b330398db67d9799712bdf"�[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 / 0_DCO Sign-off Check.txt: refactor: remove all as type assertions from source code

Conclusion: failure

View job details

##[group]Run base="67d0e2ace0607873c32894f24c2d5e6c6bb13f1d"
 �[36;1mbase="67d0e2ace0607873c32894f24c2d5e6c6bb13f1d"�[0m
 �[36;1mhead="17fd8691cb78f93605b330398db67d9799712bdf"�[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 #459 / Analyze (go): Code Quality: PR #459

Conclusion: failure

View job details

,"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/07 00:48:45 Autobuilder was built with go1.26.0, environment has go1.24.13
 [] [build-stderr] 2026/07/07 00:48:45 LGTM_SRC is /home/runner/work/cli/cli
 [] [build-stderr] 2026/07/07 00:48:45 Found no go.work files in the workspace; looking for go.mod files...
 [] [build-stderr] 2026/07/07 00:48:45 Found 1 go.mod files in: shims/go/go.mod.
 [] [build-stderr] 2026/07/07 00:48:45 Found 1 go.mod file(s).
 [] [build-stderr] 2026/07/07 00:48:45 Import path is 'github.com/archgate/cli'
 [] [build-stderr] 2026/07/...

GitHub Actions: Code Quality: PR #459 / 1_Analyze (go).txt: Code Quality: PR #459

Conclusion: failure

View job details

xtracting types for package crypto/aes.
 [] [build-stderr] 2026/07/07 00:48:54 Done extracting types for package crypto/aes.
 [] [build-stderr] 2026/07/07 00:48:54 Processing package crypto/des.
 [] [build-stderr] 2026/07/07 00:48:54 Extracting types for package crypto/des.
 [] [build-stderr] 2026/07/07 00:48:54 Done extracting types for package crypto/des.
 [] [build-stderr] 2026/07/07 00:48:54 Processing package crypto/internal/fips140/nistec/fiat.
 [] [build-stderr] 2026/07/07 00:48:54 Extracting types for package crypto/internal/fips140/nistec/fiat.
 [] [build-stderr] 2026/07/07 00:48:54 Done extracting types for package crypto/internal/fips140/nistec/fiat.
 [] [build-stderr] 2026/07/07 00:48:54 Processing package crypto/internal/fips140/nistec.
 [] [build-stderr] 2026/07/07 00:48:54 Extracting types for package crypto/internal/fips140/nistec.
 [] [build-stderr] 2026/07/07 00:48:54 Done extracting types for package crypto/internal/fips140/nistec.
 [] [build-stderr] 2026/07/07 00:48:54 Processing package crypto/internal/fips140/ecdh.
 [] [build-stderr] 2026/07/07 00:48:54 Extracting types for package crypto/internal/fips140/ecdh.
 [] [build-stderr] 2026/07/07 00:48:54 Done extracting types for package crypto/internal/fips140/ecdh.
 [] [build-stderr] 2026/07/07 00:48:54 Processing package crypto/internal/fips140/edwards25519/field.
 [] [build-stderr] 2026/07/07 00:48:54 Extracting types for package crypto/internal/fips140/edwards25519/field.
 [] [build-stderr] 2026/07/07 00:48:54 Done extracting types for package crypto/internal/fips140/edwards25519/field.
 [] [build-stderr] 2026/07/07 00:48:54 Processing package crypto/ecdh.
 [] [build-stderr] 2026/07/07 00:48:54 Extracting types for package crypto/ecdh.
 [] [build-stderr] 2026/07/07 00:48:54 Done extracting types for package crypto/ecdh.
 [] [build-stderr] 2026/07/07 00:48:54 Processing package crypto/elliptic.
 [] [build-stderr] 2026/07/07 00:48:54 Extracting types for package crypto/elliptic.
 [] [build-std...
🧰 Additional context used
📓 Path-based instructions (13)
tests/**/*.test.ts

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

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

Files:

  • tests/helpers/claude-settings.test.ts
  • tests/helpers/vscode-settings.test.ts
**/*.{ts,tsx,js,jsx,mjs,cjs}

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

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

Files:

  • tests/helpers/claude-settings.test.ts
  • src/commands/adr/create.ts
  • src/helpers/opencode-settings.ts
  • src/formats/pack.ts
  • src/commands/check.ts
  • src/helpers/adr-writer.ts
  • tests/helpers/vscode-settings.test.ts
  • src/engine/ast-support.ts
  • src/helpers/prompt.ts
  • src/formats/adr.ts
  • src/helpers/signup.ts
  • src/cli.ts
  • src/helpers/binary-upgrade.ts
  • src/engine/reporter.ts
  • src/helpers/stack-detect.ts
  • src/helpers/session-context-copilot.ts
  • src/helpers/vscode-settings.ts
  • src/helpers/auth.ts
  • src/helpers/repo-probe.ts
  • src/helpers/session-context.ts
  • src/engine/rule-scanner.ts
  • src/helpers/claude-settings.ts
  • src/helpers/project-config.ts
  • src/engine/runner.ts
  • src/helpers/telemetry-config.ts
tests/**/*.ts

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

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

Files:

  • tests/helpers/claude-settings.test.ts
  • tests/helpers/vscode-settings.test.ts
{src,tests}/**/*.ts

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

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

Files:

  • tests/helpers/claude-settings.test.ts
  • src/commands/adr/create.ts
  • src/helpers/opencode-settings.ts
  • src/formats/pack.ts
  • src/commands/check.ts
  • src/helpers/adr-writer.ts
  • tests/helpers/vscode-settings.test.ts
  • src/engine/ast-support.ts
  • src/helpers/prompt.ts
  • src/formats/adr.ts
  • src/helpers/signup.ts
  • src/cli.ts
  • src/helpers/binary-upgrade.ts
  • src/engine/reporter.ts
  • src/helpers/stack-detect.ts
  • src/helpers/session-context-copilot.ts
  • src/helpers/vscode-settings.ts
  • src/helpers/auth.ts
  • src/helpers/repo-probe.ts
  • src/helpers/session-context.ts
  • src/engine/rule-scanner.ts
  • src/helpers/claude-settings.ts
  • src/helpers/project-config.ts
  • src/engine/runner.ts
  • src/helpers/telemetry-config.ts
**

⚙️ CodeRabbit configuration file

**: # CLAUDE.md

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

Technology Stack

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

Commands

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

Validation Gate

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

Git Hooks (Git 2.54+)

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

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

Activate once per clone:

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

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

#...

Files:

  • tests/helpers/claude-settings.test.ts
  • src/commands/adr/create.ts
  • src/helpers/opencode-settings.ts
  • src/formats/pack.ts
  • src/commands/check.ts
  • src/helpers/adr-writer.ts
  • tests/helpers/vscode-settings.test.ts
  • src/engine/ast-support.ts
  • src/helpers/prompt.ts
  • src/formats/adr.ts
  • src/helpers/signup.ts
  • src/cli.ts
  • src/helpers/binary-upgrade.ts
  • src/engine/reporter.ts
  • src/helpers/stack-detect.ts
  • src/helpers/session-context-copilot.ts
  • src/helpers/vscode-settings.ts
  • src/helpers/auth.ts
  • src/helpers/repo-probe.ts
  • src/helpers/session-context.ts
  • src/engine/rule-scanner.ts
  • src/helpers/claude-settings.ts
  • src/helpers/project-config.ts
  • src/engine/runner.ts
  • src/helpers/telemetry-config.ts

⚙️ CodeRabbit configuration file

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

When reviewing, you must:

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

Files:

  • tests/helpers/claude-settings.test.ts
  • src/commands/adr/create.ts
  • src/helpers/opencode-settings.ts
  • src/formats/pack.ts
  • src/commands/check.ts
  • src/helpers/adr-writer.ts
  • tests/helpers/vscode-settings.test.ts
  • src/engine/ast-support.ts
  • src/helpers/prompt.ts
  • src/formats/adr.ts
  • src/helpers/signup.ts
  • src/cli.ts
  • src/helpers/binary-upgrade.ts
  • src/engine/reporter.ts
  • src/helpers/stack-detect.ts
  • src/helpers/session-context-copilot.ts
  • src/helpers/vscode-settings.ts
  • src/helpers/auth.ts
  • src/helpers/repo-probe.ts
  • src/helpers/session-context.ts
  • src/engine/rule-scanner.ts
  • src/helpers/claude-settings.ts
  • src/helpers/project-config.ts
  • src/engine/runner.ts
  • src/helpers/telemetry-config.ts
src/commands/**/*.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-001-command-structure.md)

src/commands/**/*.ts: Each command module under src/commands/ must export a register*Command(program) function, with one command per file (or one command group via index.ts).
Command files must be thin: they should only parse arguments, call engine/helpers, and format output; business logic must live in src/engine/, src/helpers/, or src/formats/.
Command modules must not use executableDir() for command discovery, must not call .parse() themselves, and must not spawn child processes for subcommand execution.
Subcommand groups should live in nested directories with an index.ts that composes child commands (for example, src/commands/adr/index.ts).

src/commands/**/*.ts: In command files under src/commands/**/*.ts, options that need type narrowing beyond plain strings MUST use new Option() with .addOption() instead of plain .option().
In src/commands/**/*.ts, import Option from @commander-js/extra-typings alongside Command so typed options get full type inference.
In src/commands/**/*.ts, enum-like options with a fixed set of allowed values must use .choices() on an Option to provide runtime validation and compile-time type narrowing.
In src/commands/**/*.ts, options that require type conversion must use .argParser() on an Option object, not a parser function passed as the third argument to .option().
In src/commands/**/*.ts, register typed options with .addOption() rather than .option().
In src/commands/**/*.ts, pass both the choices array and default values with as const when using .choices() and .default() to preserve literal types.
In src/commands/**/*.ts, do not add manual validation for choice options (for example if (!VALID.includes(val))), because Commander handles invalid-value rejection automatically.

src/commands/**/*.ts: Commands that operate on .archgate/ project resources must use findProjectRoot() from src/helpers/paths.ts to locate the project root; direct use of `process.cw...

Files:

  • src/commands/adr/create.ts
  • src/commands/check.ts
src/**/*.ts

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

src/**/*.ts: Use logError() from src/helpers/log.ts for user-facing errors instead of printing errors directly.
Exit with code 1 for 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 code 2.
Include actionable suggestions in user-facing error messages whenever possible.
Write user-facing errors to stderr via logError(); do not send them to stdout.
When findProjectRoot() returns null for commands that do not require .archgate/, fall back to process.cwd() as the path key or working directory.
Catch ExitPromptError from Inquirer in the top-level error boundary and treat it as user cancellation: exit with code 130 without logging an error or sending it to Sentry.
Do not use console.error() directly; use logError() for consistent formatting.
Do not exit with codes other than 0, 1, 2, or 130.
Do not send user-cancellation errors such as Inquirer ExitPromptError to Sentry; filter them in beforeSend.

src/**/*.ts: Use styleText(format, text) from node:util for all terminal colors and formatting in CLI source files; do not use raw ANSI escape codes or third-party color libraries.
Commands that produce structured results and support --json must emit machine-readable JSON to stdout with no colors or decorative formatting.
When a command supports --json, use formatJSON() from src/helpers/output.ts for JSON serialization, and pass forcePretty: true when the user explicitly provided --json.
Use isAgentContext() from src/helpers/output.ts to enable auto-JSON behavior for commands that support both human-readable and JSON output modes.
CLI output must not include emoji; use text symbols and colors instead.
Send normal command output to stdout with console.log(), and send errors, warnings, and debug messages to stderr via logError(), logWarn(), and logDebug().
Keep CLI output concise and scannabl...

Files:

  • src/commands/adr/create.ts
  • src/helpers/opencode-settings.ts
  • src/formats/pack.ts
  • src/commands/check.ts
  • src/helpers/adr-writer.ts
  • src/engine/ast-support.ts
  • src/helpers/prompt.ts
  • src/formats/adr.ts
  • src/helpers/signup.ts
  • src/cli.ts
  • src/helpers/binary-upgrade.ts
  • src/engine/reporter.ts
  • src/helpers/stack-detect.ts
  • src/helpers/session-context-copilot.ts
  • src/helpers/vscode-settings.ts
  • src/helpers/auth.ts
  • src/helpers/repo-probe.ts
  • src/helpers/session-context.ts
  • src/engine/rule-scanner.ts
  • src/helpers/claude-settings.ts
  • src/helpers/project-config.ts
  • src/engine/runner.ts
  • src/helpers/telemetry-config.ts
src/**/!(*platform).ts

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

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

Files:

  • src/commands/adr/create.ts
  • src/helpers/opencode-settings.ts
  • src/formats/pack.ts
  • src/commands/check.ts
  • src/helpers/adr-writer.ts
  • src/engine/ast-support.ts
  • src/helpers/prompt.ts
  • src/formats/adr.ts
  • src/helpers/signup.ts
  • src/cli.ts
  • src/helpers/binary-upgrade.ts
  • src/engine/reporter.ts
  • src/helpers/stack-detect.ts
  • src/helpers/session-context-copilot.ts
  • src/helpers/vscode-settings.ts
  • src/helpers/auth.ts
  • src/helpers/repo-probe.ts
  • src/helpers/session-context.ts
  • src/engine/rule-scanner.ts
  • src/helpers/claude-settings.ts
  • src/helpers/project-config.ts
  • src/engine/runner.ts
  • src/helpers/telemetry-config.ts
src/{helpers,engine}/**/*.ts

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

Do not use console.log(), console.warn(), or console.info() directly in helper or engine files; use logInfo() or logWarn() instead. Command files are exempt because they are the I/O layer.

Files:

  • src/helpers/opencode-settings.ts
  • src/helpers/adr-writer.ts
  • src/engine/ast-support.ts
  • src/helpers/prompt.ts
  • src/helpers/signup.ts
  • src/helpers/binary-upgrade.ts
  • src/engine/reporter.ts
  • src/helpers/stack-detect.ts
  • src/helpers/session-context-copilot.ts
  • src/helpers/vscode-settings.ts
  • src/helpers/auth.ts
  • src/helpers/repo-probe.ts
  • src/helpers/session-context.ts
  • src/engine/rule-scanner.ts
  • src/helpers/claude-settings.ts
  • src/helpers/project-config.ts
  • src/engine/runner.ts
  • src/helpers/telemetry-config.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, use safeParse(), and reuse AdrFrontmatterSchema.shape.* instead of duplicating enums.

Files:

  • src/formats/pack.ts
  • src/formats/adr.ts
src/commands/{*.ts,*/index.ts}

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

Every top-level CLI command registered via register*Command(program) must live in src/commands/<name>.ts or src/commands/<name>/index.ts so it can be matched to a docs page.

Files:

  • src/commands/check.ts
{src/commands/{*.ts,*/index.ts},docs/src/content/docs/reference/cli/!(index).mdx}

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

{src/commands/{*.ts,*/index.ts},docs/src/content/docs/reference/cli/!(index).mdx}: When a top-level command is added, its docs page must be added in the same change; when a top-level command is removed, its matching docs page must be deleted in the same change.
Keep command and documentation stems aligned when renaming a top-level command file; renaming src/commands/<name>.ts or src/commands/<name>/index.ts must be paired with renaming docs/src/content/docs/reference/cli/<name>.mdx.

Files:

  • src/commands/check.ts
src/cli.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-001-command-structure.md)

src/cli.ts: The main entry point must explicitly import and register every command; no auto-discovery is allowed.
All async bootstrap logic in src/cli.ts must be wrapped in an async main() function and invoked with main().catch((err) => { logError(String(err)); process.exit(2); }); top-level await is forbidden.
The CLI must run in-process in the same Bun process as the entry point; command execution should not rely on child processes.

Files:

  • src/cli.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/opencode-settings.ts
  • src/helpers/adr-writer.ts
  • src/helpers/prompt.ts
  • src/helpers/signup.ts
  • src/helpers/binary-upgrade.ts
  • src/helpers/stack-detect.ts
  • src/helpers/session-context-copilot.ts
  • src/helpers/vscode-settings.ts
  • src/helpers/auth.ts
  • src/helpers/repo-probe.ts
  • src/helpers/session-context.ts
  • src/helpers/claude-settings.ts
  • src/helpers/project-config.ts
  • src/helpers/telemetry-config.ts
🔇 Additional comments (30)
src/engine/ast-support.ts (1)

192-204: Cast removal is a no-op here — same lack of validation as before.

JSON.parse(stdout) can technically return a scalar (not the declared Record<string, unknown> | unknown[]), but this was equally true with the previous cast. No new risk introduced by this change; flagging only as an observation, not a regression.

src/cli.ts (1)

4-8: LGTM!

Also applies to: 31-31, 118-140, 176-188

src/commands/adr/create.ts (1)

88-88: LGTM!

src/commands/check.ts (1)

23-26: LGTM!

Also applies to: 171-171

src/engine/reporter.ts (1)

179-194: LGTM!

Also applies to: 315-331

src/engine/runner.ts (1)

163-252: LGTM! Verified against ARCH-022's four-guardrail ordering, shared parseJsModule reuse, isolated-mode Python invocation, and the single-door ast: astImpl contract — all preserved.

src/engine/rule-scanner.ts (2)

124-252: Faithful mechanical refactor — behavior preserved.

Cast-based property access replaced with type-guard helpers (childNode/strProp/boolProp); every violation condition and message text is unchanged.

Also applies to: 315-388


36-61: 🎯 Functional Correctness

No issue: AstNode already has an index signature, so node[key] is valid here.

			> Likely an incorrect or invalid review comment.
src/formats/adr.ts (1)

60-68: LGTM!

src/helpers/adr-writer.ts (1)

94-101: LGTM!

src/helpers/auth.ts (2)

35-60: LGTM!


86-86: LGTM!

Also applies to: 129-129, 183-183, 226-226

src/helpers/telemetry-config.ts (2)

26-33: LGTM!


88-100: 🎯 Functional Correctness

JSON.parse(readFileSync(...)) conflicts with the Bun JSON-read guideline — likely an intentional sync exception.

The coding guidelines require await Bun.file(path).json() for JSON reads and explicitly forbid JSON.parse(fs.readFileSync(path, "utf-8")). This function is deliberately synchronous ("Synchronous read for startup-path performance"), and Bun has no synchronous equivalent to Bun.file().json(), so readFileSync + JSON.parse appears to be the only viable option here — similar to the previously-accepted exception for using existsSync in sync-constrained helpers.

Worth confirming this is an intentional, accepted exception to the "no JSON.parse+readFileSync" guideline rather than an oversight, since the rule has no explicit carve-out for sync-only code paths.

Based on learnings, sync-only helper constraints can justify deviating from the async-preferred pattern (as previously accepted for existsSync in a synchronous helper), and per coding guidelines "Use await Bun.file(path).json() when reading JSON files in Bun TypeScript source code; do not use ... JSON.parse(fs.readFileSync(path, "utf-8")) for file reads."

Sources: Coding guidelines, Learnings

src/helpers/stack-detect.ts (1)

29-42: LGTM!

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

175-178: LGTM!

src/helpers/claude-settings.ts (1)

65-66: LGTM!

tests/helpers/claude-settings.test.ts (1)

73-79: LGTM!

src/helpers/vscode-settings.ts (1)

55-55: LGTM!

tests/helpers/vscode-settings.test.ts (1)

70-79: LGTM!

src/helpers/project-config.ts (2)

17-17: LGTM!

Also applies to: 147-148, 159-167, 196-203


43-59: 🎯 Functional Correctness

No action hereloadProjectConfig() is still a synchronous helper used by other synchronous readers (getMergedDomainPrefixes(), resolveDomainPrefix(), getConfiguredBaseBranch(), listDomainEntries()), so moving it to Bun.file().json() would require a broader async refactor outside this change.

			> Likely an incorrect or invalid review comment.
src/helpers/prompt.ts (2)

83-97: LGTM!


112-125: 📐 Maintainability & Code Quality

Keep the suppression. Oxlint accepts // eslint-disable-next-line @typescript-eslint/no-explicit-any``, so this isn’t a lint issue here. unknown[] is optional cleanup, not required.

			> Likely an incorrect or invalid review comment.
src/helpers/repo-probe.ts (1)

19-30: LGTM!

Also applies to: 76-76, 120-140, 142-160, 162-179, 190-213

src/helpers/session-context.ts (3)

7-8: LGTM!


101-129: LGTM!


55-63: 🩺 Stability & Availability

No change needed here. TranscriptEntrySchema is used behind a hard-fail JSONL parse, and the other session readers follow the same pattern. Filtering invalid entries or switching to nullish() would change the current error behavior rather than fix a regression.

			> Likely an incorrect or invalid review comment.
src/helpers/session-context-copilot.ts (2)

6-7: LGTM!

Also applies to: 18-26, 118-120, 211-212


192-195: 🩺 Stability & Availability

No issue here. events.jsonl already falls back to the generic transcript error, and the malformed-file case is covered; the safeParse change isn’t needed.

			> Likely an incorrect or invalid review comment.

Comment thread src/formats/pack.ts
Comment thread src/helpers/auth.ts Outdated
Comment thread src/helpers/binary-upgrade.ts Outdated
Comment thread src/helpers/claude-settings.ts
Comment thread src/helpers/opencode-settings.ts
Comment thread src/helpers/signup.ts Outdated
Comment thread src/helpers/vscode-settings.ts Outdated
Comment thread src/helpers/vscode-settings.ts
@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

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

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

(requestId: serverGenReqId_318df59e-47dc-44e5-b638-f55296b33d34)

Comment thread src/cli.ts Outdated
Comment thread src/engine/runner.ts Outdated
Comment thread src/formats/adr.ts Outdated
Comment thread src/helpers/claude-settings.ts Outdated
Comment thread src/helpers/session-context.ts Outdated
Comment thread src/helpers/stack-detect.ts Outdated
@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

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

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

(requestId: serverGenReqId_1ac8c139-f860-476f-81cc-918de9de4ed8)

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 7, 2026
Comment thread src/helpers/session-context-copilot.ts Outdated
Comment thread src/helpers/claude-settings.ts Outdated
Comment thread tests/helpers/claude-settings.test.ts Outdated
Comment thread tests/helpers/vscode-settings.test.ts
@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

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

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

(requestId: serverGenReqId_8a25750c-16ac-4dcf-b651-a756aacd3504)

@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

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

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

(requestId: serverGenReqId_135dc3b8-06a5-4108-b5cc-ca6895f4794e)

Replace every `as Type` assertion in src/ with type-safe alternatives:

- HTTP responses: Zod schemas (auth, repo-probe, binary-upgrade, signup)
- Config files: typed Zod schemas with .passthrough() for read-modify-write
  (claude-settings, vscode-settings, opencode-settings)
- Session data: Zod array/object schemas (session-context, session-context-copilot)
- AST walking: type guard functions — isAstNode(), childNode(), strProp(),
  boolProp() (rule-scanner)
- Function overloads: Promise<any> return type for implementation (runner)
- Function patching: Proxy with apply/get traps (prompt)
- Commander options: proper type narrowing, typed return (cli, check, reporter)
- Property access: type-safe alternatives — .some(), Object.entries(),
  Object.fromEntries() (project-config, adr-writer, stack-detect)
- Telemetry config: Zod schema replacing manual field checks

Zero `as Type` assertions remain in src/. Only `as const` (safe literal
narrowing) and `import { X as Y }` (renaming) remain.

Claude-Session: https://claude.ai/code/session_01P2kzr4bcBvg7AtaRhyHdMa
Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
- project-config: pass JSON.parse directly to safeParse, no intermediate
- pack: pass YAML.parse directly to Zod .parse(), no intermediate
- stack-detect: let spread result type be inferred
- session-context: use `in` narrowing instead of annotated Record cast

Claude-Session: https://claude.ai/code/session_01P2kzr4bcBvg7AtaRhyHdMa
Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
- cli: use CommandUnknownOpts from Commander instead of custom
  CommandLike interface
- reporter: use `as const` on ternary branches instead of explicit
  type annotation for sevColor

Claude-Session: https://claude.ai/code/session_01P2kzr4bcBvg7AtaRhyHdMa
Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
Use Object.entries().find() instead of widening DOMAIN_PREFIXES to
Record<string, string | undefined> for string key indexing.

Claude-Session: https://claude.ai/code/session_01P2kzr4bcBvg7AtaRhyHdMa
Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
Replace isAstNode/childNode/strProp/boolProp type guard functions
with a recursive AstNodeSchema Zod schema. The schema validates
node structure including type, name, value, computed, and recursive
child fields (source, object, property, callee, left). The value
field accepts both literals and child nodes (Property.value can be
an ObjectExpression).

Walk functions now access typed fields directly from the
schema-validated node instead of manual property extraction.

Claude-Session: https://claude.ai/code/session_01P2kzr4bcBvg7AtaRhyHdMa
Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
- cli: remove redundant type annotation on `current` variable
- runner: replace Promise<any> with overloaded function declaration
  matching RuleContext["ast"] signatures — no any or as needed
- adr: use Zod PlainObjectSchema instead of manual isPlainObject guard
- pack: use safeParse + UserError (matching parseAdr pattern)
- auth: use safeParse for error body to handle non-object JSON
- binary-upgrade: use safeParse to preserve null-on-failure contract
- signup: use safeParse for response validation
- claude/vscode/opencode settings: add .catch(undefined) on schema
  fields to prevent whole-object wipe when a single field is invalid
- vscode-settings: wrap JSONC parse in try/catch for corrupted files
- session-context: use discriminated union schema for content blocks
  (text/tool_use/tool_result) — replaces manual in-checks
- stack-detect: use PackageJsonSchema with typed deps/devDeps fields
- ARCH-022 rule: add FunctionDeclaration case for overloaded astImpl

Claude-Session: https://claude.ai/code/session_01P2kzr4bcBvg7AtaRhyHdMa
Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
Address review feedback: set defaults in Zod schemas instead of using
?? at consumer sites.

- session-context: type/role default to "" via schema, removing ?? ""
  and non-null assertions (!) from consumers
- session-context-copilot: event type defaults to "" via schema
- auth: email defaults to null via .nullable().default(null)
- signup: token defaults to null via .nullable().default(null)
- claude-settings: permissions.allow/deny default to [] via schema,
  catch invalid permissions to { allow: [], deny: [] }
- vscode-settings: catch invalid marketplaces to []
- Add regression tests for invalid-data graceful handling through
  schema .catch() (claude-settings, vscode-settings)

Claude-Session: https://claude.ai/code/session_01P2kzr4bcBvg7AtaRhyHdMa
Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
@rhuanbarreto rhuanbarreto force-pushed the refactor/remove-as-type-assertions branch from 985c4a5 to 60b414a Compare July 7, 2026 09:02
@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

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

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

(requestId: serverGenReqId_721ebbe0-f96d-404a-b161-da5dec1ad67e)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (3)
tests/helpers/claude-settings.test.ts (1)

10-93: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Test coverage doesn't exercise a malformed (non-string) agent field.

Given the "agent" in merged behavior flagged in src/helpers/claude-settings.ts (lines 15-23), a test like mergeClaudeSettings(parse({ agent: 123 }), ARCHGATE_CLAUDE_SETTINGS) would have caught that the archgate default agent is silently skipped when agent is present but invalid. Consider adding this case alongside the existing "handles non-object permissions gracefully" test once the source-side fix lands.

🤖 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 `@tests/helpers/claude-settings.test.ts` around lines 10 - 93, Add a test case
in claude-settings.test.ts for a malformed agent value, using
mergeClaudeSettings and parse to cover something like a non-string agent input.
This should verify that ClaudeSettingsSchema/mergeClaudeSettings do not let an
invalid agent presence block the archgate default from being applied, alongside
the existing non-object permissions coverage. Reference the mergeClaudeSettings
behavior around the "agent" in merged check so the test fails until the source
fix lands.
src/helpers/session-context-copilot.ts (1)

195-198: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Whole-transcript data loss on a single malformed event.

CopilotEventsSchema.parse(...) hard-fails the entire array if even one entry doesn't match CopilotEventSchema (e.g. data present but not an object). The catch block then reports "Session exists but has no conversation transcript" for the whole session, even though only one event was malformed. Contrast with lines 214-215 just below, where individual content values are validated with safeParse and fall back gracefully — the same graceful-degradation pattern should apply at the event level.

🐛 Proposed fix
-  let rawEntries: z.infer<typeof CopilotEventsSchema>;
+  let rawEntries: z.infer<typeof CopilotEventSchema>[];
   try {
     const raw = await Bun.file(eventsFile).text();
-    rawEntries = CopilotEventsSchema.parse(Bun.JSONL.parse(raw));
+    const parsed = Bun.JSONL.parse(raw);
+    rawEntries = Array.isArray(parsed)
+      ? parsed
+          .map((e) => CopilotEventSchema.safeParse(e))
+          .filter((r) => r.success)
+          .map((r) => r.data)
+      : [];
   } catch {
🤖 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/helpers/session-context-copilot.ts` around lines 195 - 198, The
transcript load in session-context-copilot is too strict because
CopilotEventsSchema.parse() fails the entire session on one malformed event.
Update the event parsing around the rawEntries assignment to validate each
parsed JSONL entry individually, using a safeParse-style fallback like the
content validation below, so one bad CopilotEventSchema item is skipped or
sanitized instead of discarding the whole transcript.
src/helpers/auth.ts (1)

86-86: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Remaining raw .parse() calls on external HTTP responses can crash with exit code 2 instead of a graceful UserError.

Unlike the ErrorResponseSchema.safeParse(...) fix applied at lines 213-216 in this same file, DeviceCodeResponseSchema.parse(...) (86), DeviceTokenResponseSchema.parse(...) (129), GitHubUserSchema.parse(...) (183), and TokenResponseSchema.parse(...) (227) still use .parse(). If GitHub or the plugins service ever returns a shape these schemas don't cover (e.g. an additional device-flow error code like slow_down during polling, or a malformed 200 response), the thrown ZodError propagates uncaught — bypassing UserError/exit-code-1 handling and surfacing as an unexpected internal error (exit 2) instead of an expected external-call failure.

For consistency with the already-fixed error path in this file, convert these to safeParse() and throw a UserError (or handle gracefully) on failure.

As per coding guidelines: "Exit with code 1 for expected failures such as missing config, invalid input, or validation violations" and "Let unexpected errors crash naturally so they are treated as internal errors with exit code 2" — a malformed external API response is an expected-failure case, not an internal bug.

Also applies to: 129-129, 183-183, 227-227

🤖 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/helpers/auth.ts` at line 86, The remaining external-response validations
in auth helpers still use direct schema parsing, which can throw uncaught
validation errors instead of a graceful UserError. Update the response handling
in DeviceCodeResponseSchema.parse, DeviceTokenResponseSchema.parse,
GitHubUserSchema.parse, and TokenResponseSchema.parse to use safeParse and
explicitly convert failures into a handled UserError (or equivalent
expected-failure path), matching the existing ErrorResponseSchema safeParse
pattern in src/helpers/auth.ts.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/formats/pack.ts`:
- Around line 5-6: The Zod error formatting logic in pack.ts duplicates the same
path-to-message mapping already used in adr.ts, so extract that formatter into a
shared helper and reuse it from both places. Update the validation error
handling in the pack formatting flow to call the shared helper instead of
building the message inline, keeping the formatter behavior consistent across
Pack and ADR.

In `@src/helpers/claude-settings.ts`:
- Around line 15-23: The archgate default check for ClaudeSettingsSchema.agent
currently uses an in-operator presence test, which fails when agent is present
but parsed to undefined by ClaudeSettingsSchema. Update the merge logic that
reads from merged to use a nullish check on merged.agent instead of checking key
presence, so invalid or missing values are handled consistently and the default
is applied when agent is undefined.

In `@src/helpers/session-context.ts`:
- Around line 55-75: `ContentBlockSchema` is too restrictive and causes
`TranscriptEntrySchema` to reject newer Anthropic transcript blocks. Update the
discriminated union in `session-context.ts` to accept the additional block types
used by current Claude sessions, or add a safe fallback that preserves unknown
blocks instead of failing validation. Keep the fix centered around
`ContentBlockSchema`, `MessageContentSchema`, and the session reader path that
parses JSONL so unreadable transcripts no longer fall back to "Failed to read
session file".

---

Outside diff comments:
In `@src/helpers/auth.ts`:
- Line 86: The remaining external-response validations in auth helpers still use
direct schema parsing, which can throw uncaught validation errors instead of a
graceful UserError. Update the response handling in
DeviceCodeResponseSchema.parse, DeviceTokenResponseSchema.parse,
GitHubUserSchema.parse, and TokenResponseSchema.parse to use safeParse and
explicitly convert failures into a handled UserError (or equivalent
expected-failure path), matching the existing ErrorResponseSchema safeParse
pattern in src/helpers/auth.ts.

In `@src/helpers/session-context-copilot.ts`:
- Around line 195-198: The transcript load in session-context-copilot is too
strict because CopilotEventsSchema.parse() fails the entire session on one
malformed event. Update the event parsing around the rawEntries assignment to
validate each parsed JSONL entry individually, using a safeParse-style fallback
like the content validation below, so one bad CopilotEventSchema item is skipped
or sanitized instead of discarding the whole transcript.

In `@tests/helpers/claude-settings.test.ts`:
- Around line 10-93: Add a test case in claude-settings.test.ts for a malformed
agent value, using mergeClaudeSettings and parse to cover something like a
non-string agent input. This should verify that
ClaudeSettingsSchema/mergeClaudeSettings do not let an invalid agent presence
block the archgate default from being applied, alongside the existing non-object
permissions coverage. Reference the mergeClaudeSettings behavior around the
"agent" in merged check so the test fails until the source fix lands.
🪄 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: 20cf7437-559b-4a04-b8d1-1f0f0fd6fd69

📥 Commits

Reviewing files that changed from the base of the PR and between 17fd869 and 985c4a5.

📒 Files selected for processing (18)
  • .archgate/adrs/ARCH-022-ast-aware-rule-context.rules.ts
  • src/cli.ts
  • src/engine/rule-scanner.ts
  • src/engine/runner.ts
  • src/formats/adr.ts
  • src/formats/pack.ts
  • src/helpers/auth.ts
  • src/helpers/binary-upgrade.ts
  • src/helpers/claude-settings.ts
  • src/helpers/opencode-settings.ts
  • src/helpers/session-context-copilot.ts
  • src/helpers/session-context-opencode.ts
  • src/helpers/session-context.ts
  • src/helpers/signup.ts
  • src/helpers/stack-detect.ts
  • src/helpers/vscode-settings.ts
  • tests/helpers/claude-settings.test.ts
  • tests/helpers/vscode-settings.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Cloudflare Pages
⚠️ CI failures not shown inline (2)

GitHub Actions: Code Quality: PR #459 / Analyze (go): Code Quality: PR #459

Conclusion: failure

View job details

,"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/07 08:46:07 Autobuilder was built with go1.26.0, environment has go1.24.13
 [] [build-stderr] 2026/07/07 08:46:07 LGTM_SRC is /home/runner/work/cli/cli
 [] [build-stderr] 2026/07/07 08:46:07 Found no go.work files in the workspace; looking for go.mod files...
 [] [build-stderr] 2026/07/07 08:46:07 Found 1 go.mod files in: shims/go/go.mod.
 [] [build-stderr] 2026/07/07 08:46:07 Found 1 go.mod file(s).
 [] [build-stderr] 2026/07/07 08:46:07 Import path is 'github.com/archgate/cli'
 [] [build-stderr] 2026/07/...

GitHub Actions: Code Quality: PR #459 / 2_Analyze (go).txt: Code Quality: PR #459

Conclusion: failure

View job details

6 Extracting types for package crypto/aes.
 [] [build-stderr] 2026/07/07 08:46:16 Done extracting types for package crypto/aes.
 [] [build-stderr] 2026/07/07 08:46:16 Processing package crypto/des.
 [] [build-stderr] 2026/07/07 08:46:16 Extracting types for package crypto/des.
 [] [build-stderr] 2026/07/07 08:46:16 Done extracting types for package crypto/des.
 [] [build-stderr] 2026/07/07 08:46:16 Processing package crypto/internal/fips140/nistec/fiat.
 [] [build-stderr] 2026/07/07 08:46:16 Extracting types for package crypto/internal/fips140/nistec/fiat.
 [] [build-stderr] 2026/07/07 08:46:16 Done extracting types for package crypto/internal/fips140/nistec/fiat.
 [] [build-stderr] 2026/07/07 08:46:16 Processing package crypto/internal/fips140/nistec.
 [] [build-stderr] 2026/07/07 08:46:16 Extracting types for package crypto/internal/fips140/nistec.
 [] [build-stderr] 2026/07/07 08:46:16 Done extracting types for package crypto/internal/fips140/nistec.
 [] [build-stderr] 2026/07/07 08:46:16 Processing package crypto/internal/fips140/ecdh.
 [] [build-stderr] 2026/07/07 08:46:16 Extracting types for package crypto/internal/fips140/ecdh.
 [] [build-stderr] 2026/07/07 08:46:16 Done extracting types for package crypto/internal/fips140/ecdh.
 [] [build-stderr] 2026/07/07 08:46:16 Processing package crypto/internal/fips140/edwards25519/field.
 [] [build-stderr] 2026/07/07 08:46:16 Extracting types for package crypto/internal/fips140/edwards25519/field.
 [] [build-stderr] 2026/07/07 08:46:16 Done extracting types for package crypto/internal/fips140/edwards25519/field.
 [] [build-stderr] 2026/07/07 08:46:16 Processing package crypto/ecdh.
 [] [build-stderr] 2026/07/07 08:46:16 Extracting types for package crypto/ecdh.
 [] [build-stderr] 2026/07/07 08:46:16 Done extracting types for package crypto/ecdh.
 [] [build-stderr] 2026/07/07 08:46:16 Processing package crypto/elliptic.
 [] [build-stderr] 2026/07/07 08:46:16 Extracting types for package crypto/elliptic.
 [] [build-...
🧰 Additional context used
📓 Path-based instructions (13)
tests/**/*.test.ts

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

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

Files:

  • tests/helpers/claude-settings.test.ts
  • tests/helpers/vscode-settings.test.ts
**/*.{ts,tsx,js,jsx,mjs,cjs}

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

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

Files:

  • tests/helpers/claude-settings.test.ts
  • src/cli.ts
  • src/helpers/opencode-settings.ts
  • src/formats/pack.ts
  • src/helpers/binary-upgrade.ts
  • tests/helpers/vscode-settings.test.ts
  • src/helpers/signup.ts
  • src/helpers/session-context-opencode.ts
  • src/helpers/stack-detect.ts
  • src/helpers/vscode-settings.ts
  • src/helpers/session-context-copilot.ts
  • src/formats/adr.ts
  • src/engine/runner.ts
  • src/helpers/claude-settings.ts
  • src/helpers/session-context.ts
  • src/helpers/auth.ts
  • src/engine/rule-scanner.ts
tests/**/*.ts

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

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

Files:

  • tests/helpers/claude-settings.test.ts
  • tests/helpers/vscode-settings.test.ts
{src,tests}/**/*.ts

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

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

Files:

  • tests/helpers/claude-settings.test.ts
  • src/cli.ts
  • src/helpers/opencode-settings.ts
  • src/formats/pack.ts
  • src/helpers/binary-upgrade.ts
  • tests/helpers/vscode-settings.test.ts
  • src/helpers/signup.ts
  • src/helpers/session-context-opencode.ts
  • src/helpers/stack-detect.ts
  • src/helpers/vscode-settings.ts
  • src/helpers/session-context-copilot.ts
  • src/formats/adr.ts
  • src/engine/runner.ts
  • src/helpers/claude-settings.ts
  • src/helpers/session-context.ts
  • src/helpers/auth.ts
  • src/engine/rule-scanner.ts
**

⚙️ CodeRabbit configuration file

**: # CLAUDE.md

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

Technology Stack

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

Commands

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

Validation Gate

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

Git Hooks (Git 2.54+)

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

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

Activate once per clone:

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

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

#...

Files:

  • tests/helpers/claude-settings.test.ts
  • src/cli.ts
  • src/helpers/opencode-settings.ts
  • src/formats/pack.ts
  • src/helpers/binary-upgrade.ts
  • tests/helpers/vscode-settings.test.ts
  • src/helpers/signup.ts
  • src/helpers/session-context-opencode.ts
  • src/helpers/stack-detect.ts
  • src/helpers/vscode-settings.ts
  • src/helpers/session-context-copilot.ts
  • src/formats/adr.ts
  • src/engine/runner.ts
  • src/helpers/claude-settings.ts
  • src/helpers/session-context.ts
  • src/helpers/auth.ts
  • src/engine/rule-scanner.ts

⚙️ CodeRabbit configuration file

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

When reviewing, you must:

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

Files:

  • tests/helpers/claude-settings.test.ts
  • src/cli.ts
  • src/helpers/opencode-settings.ts
  • src/formats/pack.ts
  • src/helpers/binary-upgrade.ts
  • tests/helpers/vscode-settings.test.ts
  • src/helpers/signup.ts
  • src/helpers/session-context-opencode.ts
  • src/helpers/stack-detect.ts
  • src/helpers/vscode-settings.ts
  • src/helpers/session-context-copilot.ts
  • src/formats/adr.ts
  • src/engine/runner.ts
  • src/helpers/claude-settings.ts
  • src/helpers/session-context.ts
  • src/helpers/auth.ts
  • 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/cli.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-001-command-structure.md)

src/cli.ts: The main entry point must explicitly import and register every command; no auto-discovery is allowed.
All async bootstrap logic in src/cli.ts must be wrapped in an async main() function and invoked with main().catch((err) => { logError(String(err)); process.exit(2); }); top-level await is forbidden.
The CLI must run in-process in the same Bun process as the entry point; command execution should not rely on child processes.

The CLI entry point must be src/cli.ts and use the #!/usr/bin/env bun shebang.

Files:

  • src/cli.ts
src/**/*.ts

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

src/**/*.ts: Use logError() from src/helpers/log.ts for user-facing errors instead of printing errors directly.
Exit with code 1 for 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 code 2.
Include actionable suggestions in user-facing error messages whenever possible.
Write user-facing errors to stderr via logError(); do not send them to stdout.
When findProjectRoot() returns null for commands that do not require .archgate/, fall back to process.cwd() as the path key or working directory.
Catch ExitPromptError from Inquirer in the top-level error boundary and treat it as user cancellation: exit with code 130 without logging an error or sending it to Sentry.
Do not use console.error() directly; use logError() for consistent formatting.
Do not exit with codes other than 0, 1, 2, or 130.
Do not send user-cancellation errors such as Inquirer ExitPromptError to Sentry; filter them in beforeSend.

src/**/*.ts: Use styleText(format, text) from node:util for all terminal colors and formatting in CLI source files; do not use raw ANSI escape codes or third-party color libraries.
Commands that produce structured results and support --json must emit machine-readable JSON to stdout with no colors or decorative formatting.
When a command supports --json, use formatJSON() from src/helpers/output.ts for JSON serialization, and pass forcePretty: true when the user explicitly provided --json.
Use isAgentContext() from src/helpers/output.ts to enable auto-JSON behavior for commands that support both human-readable and JSON output modes.
CLI output must not include emoji; use text symbols and colors instead.
Send normal command output to stdout with console.log(), and send errors, warnings, and debug messages to stderr via logError(), logWarn(), and logDebug().
Keep CLI output concise and scannabl...

Files:

  • src/cli.ts
  • src/helpers/opencode-settings.ts
  • src/formats/pack.ts
  • src/helpers/binary-upgrade.ts
  • src/helpers/signup.ts
  • src/helpers/session-context-opencode.ts
  • src/helpers/stack-detect.ts
  • src/helpers/vscode-settings.ts
  • src/helpers/session-context-copilot.ts
  • src/formats/adr.ts
  • src/engine/runner.ts
  • src/helpers/claude-settings.ts
  • src/helpers/session-context.ts
  • src/helpers/auth.ts
  • src/engine/rule-scanner.ts
src/**/!(*platform).ts

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

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

Files:

  • src/cli.ts
  • src/helpers/opencode-settings.ts
  • src/formats/pack.ts
  • src/helpers/binary-upgrade.ts
  • src/helpers/signup.ts
  • src/helpers/session-context-opencode.ts
  • src/helpers/stack-detect.ts
  • src/helpers/vscode-settings.ts
  • src/helpers/session-context-copilot.ts
  • src/formats/adr.ts
  • src/engine/runner.ts
  • src/helpers/claude-settings.ts
  • src/helpers/session-context.ts
  • src/helpers/auth.ts
  • src/engine/rule-scanner.ts
src/{helpers,engine}/**/*.ts

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

Do not use console.log(), console.warn(), or console.info() directly in helper or engine files; use logInfo() or logWarn() instead. Command files are exempt because they are the I/O layer.

Files:

  • src/helpers/opencode-settings.ts
  • src/helpers/binary-upgrade.ts
  • src/helpers/signup.ts
  • src/helpers/session-context-opencode.ts
  • src/helpers/stack-detect.ts
  • src/helpers/vscode-settings.ts
  • src/helpers/session-context-copilot.ts
  • src/engine/runner.ts
  • src/helpers/claude-settings.ts
  • src/helpers/session-context.ts
  • src/helpers/auth.ts
  • src/engine/rule-scanner.ts
src/formats/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use Zod schemas as the single source of truth; derive types with z.infer<> instead of defining separate interfaces, use safeParse(), and reuse AdrFrontmatterSchema.shape.* to avoid duplicating enums.

Files:

  • src/formats/pack.ts
  • src/formats/adr.ts
src/engine/runner.ts

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

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

Files:

  • src/engine/runner.ts
src/engine/rule-scanner.ts

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

src/engine/rule-scanner.ts: Keep rule-scanner.ts using the shared parseModule() helper instead of duplicating the parser call inline again.
rule-scanner.ts must continue blocking banned imports, dangerous Bun.* property access, eval/Function, non-literal dynamic import(), and globalThis/process.env mutation in .rules.ts source.
The sandbox must continue to block Bun.spawn and Bun.spawnSync from .rules.ts code.
The sandbox must continue to forbid .rules.ts code from reaching subprocess primitives directly, leaving ctx.ast() as the only sanctioned path.

Files:

  • src/engine/rule-scanner.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/opencode-settings.ts
  • src/helpers/binary-upgrade.ts
  • src/helpers/signup.ts
  • src/helpers/session-context-opencode.ts
  • src/helpers/stack-detect.ts
  • src/helpers/vscode-settings.ts
  • src/helpers/session-context-copilot.ts
  • src/helpers/claude-settings.ts
  • src/helpers/session-context.ts
  • src/helpers/auth.ts
🔇 Additional comments (21)
src/cli.ts (1)

4-8: LGTM!

Also applies to: 31-31, 120-124, 177-188

src/engine/runner.ts (2)

8-11: LGTM! Guardrail ordering (path safety → language plausibility → interpreter probe → guarded invocation) matches ARCH-022 exactly, and the python -I isolation flag is preserved.

Also applies to: 166-257


258-259: 🎯 Functional Correctness

No cast required. parseAstJson already returns Record<string, unknown> | unknown[], so this branch does not depend on overload-based narrowing.

			> Likely an incorrect or invalid review comment.
src/engine/rule-scanner.ts (2)

44-70: 🔒 Security & Privacy | 🏗️ Heavy lift

Zod schema's cascading validation failure lets crafted computed-access/assignment expressions silently evade the sandbox scanner.

AstNodeSchema recursively validates object, property, callee, left, and source via z.lazy(() => AstNodeSchema), and constrains value to string | number | boolean | null | AstNode. In Zod, if any declared field on an object fails to validate, .safeParse() fails for the entire object — there's no partial/lenient pass-through for a single bad field.

meriyah follows ESTree and constructs real RegExp objects for regex-literal value (validated via the JS runtime) and native bigint for BigIntLiteral.value — neither matches the value union. Because object/property/left/source are validated eagerly and recursively as part of the parent's own schema check (not just via the generic Object.values() walk), a regex/BigInt literal embedded anywhere inside one of these specific chains poisons every ancestor reachable through them.

Concrete bypass of the "Block computed access: Bun[x], globalThis[x]" check (lines 171-177):

Bun[/spawn/.source](cmd); // computed access to Bun.spawn

property is (/spawn/).source — a MemberExpression whose own object is the regex Literal. Validating that inner object fails (regex value mismatch) → the inner MemberExpression fails → the outer MemberExpression's property field fails → parseNode() on the entire Bun[...] node returns nullwalk() never visits it, so the computed-access guard never fires.

The same technique bypasses the AssignmentExpression/globalThis mutation check via left (e.g. globalThis[/x/] = 1;) and the ImportExpression non-literal-source check via source.

The previous hand-rolled guards (isAstNode/childNode/strProp/boolProp, per the summary) did lightweight duck-typing and didn't have this "one bad field fails the whole subtree" propagation — this is a regression introduced by switching to strict recursive Zod validation for a security-sandboxing scanner whose entire job is to catch exactly these constructs.

Suggested direction: don't require value/object/property/callee/left/source to satisfy the full recursive AstNodeSchema when merely inspecting one level of shape. For example, loosen value to z.unknown() (it's only ever compared with typeof x === "string" in this file) and validate the named node-shaped fields shallowly (just { type: z.string() }.passthrough()) rather than via z.lazy(() => AstNodeSchema), so an incompatible literal buried deeper in the tree can't fail an unrelated ancestor's own shape check.

🔒 Illustrative fix direction (loosen recursive fields to avoid cascading failure)
 const AstNodeSchema: z.ZodType<AstNode> = z
   .object({
     type: z.string(),
     name: z.string().optional(),
-    value: z
-      .union([
-        z.string(),
-        z.number(),
-        z.boolean(),
-        z.null(),
-        z.lazy(() => AstNodeSchema),
-      ])
-      .optional(),
+    // Only ever compared with `typeof x === "string"` in this file — don't
+    // let non-string literal value shapes (RegExp, bigint) fail the whole node.
+    value: z.unknown().optional(),
     computed: z.boolean().optional(),
-    source: z.lazy(() => AstNodeSchema).optional(),
-    object: z.lazy(() => AstNodeSchema).optional(),
-    property: z.lazy(() => AstNodeSchema).optional(),
-    callee: z.lazy(() => AstNodeSchema).optional(),
-    left: z.lazy(() => AstNodeSchema).optional(),
+    // Shallow shape check only — do NOT recurse into the full AstNodeSchema,
+    // or an incompatible literal buried deeper in this subtree fails this
+    // unrelated ancestor's own validation and hides it from the walker.
+    source: z.object({ type: z.string() }).passthrough().optional(),
+    object: z.object({ type: z.string() }).passthrough().optional(),
+    property: z.object({ type: z.string() }).passthrough().optional(),
+    callee: z.object({ type: z.string() }).passthrough().optional(),
+    left: z.object({ type: z.string() }).passthrough().optional(),
   })
   .passthrough();

Please add regression tests for Bun[/x/.source](...) and globalThis[/x/] = 1 style computed-access payloads to scanRuleSource/scanImportedRuleSource — as per coding guidelines, rule-scanner.ts "must continue blocking dangerous Bun.* property access" and the sandbox "must continue to forbid .rules.ts code from reaching subprocess primitives directly."

Also applies to: 154-179, 209-216, 218-238, 245-256, 326-386

Source: Coding guidelines


83-153: LGTM otherwise — the narrowing to direct field access (node.callee?.name, node.source?.value, etc.) is a clean replacement for the removed cast-based guards for the non-computed/non-nested cases.

Also applies to: 180-208, 239-244, 257-373

.archgate/adrs/ARCH-022-ast-aware-rule-context.rules.ts (1)

69-76: LGTM! Correctly extends detection to the overloaded function astImpl declaration form now used in runner.ts.

src/helpers/claude-settings.ts (1)

70-75: LGTM!

src/helpers/vscode-settings.ts (2)

16-21: LGTM!


156-163: LGTM!

tests/helpers/vscode-settings.test.ts (1)

15-15: LGTM!

Also applies to: 71-90

src/helpers/session-context.ts (1)

244-244: These segments are internally correct given the current schema (proper narrowing of entry.type/entry.role, no more optional chaining/non-null assertions). The only concern is the shared root-cause issue on ContentBlockSchema/MessageContentSchema flagged above (lines 55-75), which affects the .parse() calls at lines 244 and 391 directly.

Also applies to: 255-257, 391-391, 402-404

src/helpers/opencode-settings.ts (1)

27-32: LGTM!

src/helpers/stack-detect.ts (1)

6-24: LGTM!

Also applies to: 42-57

src/helpers/session-context-copilot.ts (2)

19-26: LGTM!

Also applies to: 121-123


210-219: LGTM!

src/helpers/session-context-opencode.ts (1)

282-286: LGTM!

src/formats/adr.ts (1)

59-63: LGTM!

src/helpers/auth.ts (1)

213-216: LGTM!

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

95-101: LGTM!


179-182: 🗄️ Data Integrity & Integration

Redundant buffer slice The Uint8Array is allocated with downloadedBytes, so combined.buffer.slice(combined.byteOffset, combined.byteOffset + combined.byteLength) always returns the full payload and cannot truncate or extend it.

			> Likely an incorrect or invalid review comment.
src/helpers/signup.ts (1)

70-78: LGTM!

Comment thread src/formats/pack.ts
Comment thread src/helpers/claude-settings.ts
Comment thread src/helpers/session-context.ts Outdated
@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

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

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

(requestId: serverGenReqId_b6b199d2-1b78-4cc8-a069-bb4271ec0ff0)

@rhuanbarreto rhuanbarreto force-pushed the refactor/remove-as-type-assertions branch from 63283e3 to ff9836f Compare July 7, 2026 09:16
@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

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

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

(requestId: serverGenReqId_185272d3-5d04-45ec-bc91-654f05dfd560)

@rhuanbarreto rhuanbarreto force-pushed the refactor/remove-as-type-assertions branch from ff9836f to b9716f7 Compare July 7, 2026 09:24
@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

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

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

(requestId: serverGenReqId_c0ac2aa3-3f54-4843-8e8a-b60a2303991a)

- git-files: use optional chaining instead of ! on adrFileGlobs
- login-flow: type SignupEditor as literal union of editor values,
  eliminating the ! assertion and the ?? fallback
- init: use SignupEditor type for SIGNUP_EDITORS map

Zero non-null assertions (!) remain in src/.

Claude-Session: https://claude.ai/code/session_01P2kzr4bcBvg7AtaRhyHdMa
Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
@rhuanbarreto rhuanbarreto force-pushed the refactor/remove-as-type-assertions branch from b9716f7 to a16d2f9 Compare July 7, 2026 09:29
@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

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

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

(requestId: serverGenReqId_cc83c3ee-48b2-44ff-a468-80b51165b9a5)

- Extract formatZodErrors into shared export from adr.ts, reuse in
  pack.ts instead of duplicating the path:message mapping
- claude-settings: use nullish check on merged.agent instead of `in`
  operator, so invalid values caught to undefined get the default
- session-context: expand ContentBlockSchema with catch-all for
  unknown Anthropic block types (thinking, image, etc.) so newer
  Claude sessions don't fail parsing. Known blocks (text, tool_use,
  tool_result) are still typed; unknown blocks are silently skipped
  in content preview

Claude-Session: https://claude.ai/code/session_01P2kzr4bcBvg7AtaRhyHdMa
Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

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

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

(requestId: serverGenReqId_faaafde5-781b-4446-935c-8b55f083b603)

@rhuanbarreto rhuanbarreto merged commit c9c25f3 into main Jul 7, 2026
23 checks passed
@rhuanbarreto rhuanbarreto deleted the refactor/remove-as-type-assertions branch July 7, 2026 09:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant