Skip to content

feat(engine): add ctx.readYAML and ctx.checkCase rule helpers - #497

Merged
rhuanbarreto merged 9 commits into
mainfrom
feat/ctx-read-yaml-check-case
Jul 25, 2026
Merged

feat(engine): add ctx.readYAML and ctx.checkCase rule helpers#497
rhuanbarreto merged 9 commits into
mainfrom
feat/ctx-read-yaml-check-case

Conversation

@rhuanbarreto

@rhuanbarreto rhuanbarreto commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Closes #490.

Context

#490 asked for shared helper modules importable from .rules.ts files. That was declined in #491 — following relative imports would turn the scanner's single-file view into a recursive resolver plus containment checker, which becomes the security boundary (exactly what ARCH-024 declined and named as a tracked risk). The agreed path for the underlying duplication pain was to move the common helpers onto ctx instead, and readYAML (with frontmatter support) plus checkCase were the two named there.

This PR adds both. The sandbox boundary is untouched — no new subprocess site, no new resolver, no new dependency (Bun.YAML is a runtime built-in, per ARCH-006).

ctx.readYAML(path)

Returns one object covering both YAML documents and Markdown frontmatter:

interface ReadYamlResult {
  frontmatter: Record<string, YamlValue> | null;
  content: YamlValue;
}

Dispatch is extension-based, not content-sniffing:

  • .yml / .yaml — the whole document parses as YAML. frontmatter is always null, content is the parsed value. Because dispatch is by extension, a multi-document stream's --- separators (Kubernetes manifests, CI configs) are never misread as a frontmatter block.
  • Every other file (typically Markdown) — the leading ----delimited block parses as frontmatter: null when absent, {} when present but empty. content is the remaining body text, trimmed, and is not parsed as YAML.

null for "no frontmatter" is deliberate and mirrors fileAtBase(): "does this file have frontmatter?" is ordinary rule control flow, checkable with one null test rather than a try/catch.

Values are typed YamlValue (the JSON-like data YAML's core schema produces) rather than unknown, so after a typeof check a rule can index into mappings and sequences without casting.

Fail-closed like ast(): a malformed .yml file, or a frontmatter block that is invalid YAML or parses to a non-mapping (scalar/sequence), throws — surfacing as a rule execution error (exit 2) instead of a silent false pass. Reads go through the same safePath sandbox as readFile, and share the per-run file-text cache; the parsed value is deliberately not cached, matching readJSON (rules receive a mutable object, and sharing one instance would leak mutations between rules).

ctx.checkCase(value, scheme)

Pure and synchronous — no await. Schemes: kebab-case, camelCase, PascalCase, snake_case, SCREAMING_SNAKE_CASE. Matching is all-or-nothing and ASCII-only; the empty string matches no scheme.

camelCase/PascalCase follow the ecosystem convention (typescript-eslint's naming-convention): a leading lower/uppercase letter followed by any ASCII alphanumerics, so acronym runs (parseURL, HTTPServer) match. An unrecognized scheme name throws rather than returning false, so a typo surfaces as a rule error instead of a silent false pass/fail.

Incidental: ARCH-022 gains a fifth rule

Wiring these helpers made it concrete that the RuleContext mirror between src/formats/rules.ts and the generated ambient shim in src/helpers/rules-shim.ts is maintained entirely by hand, and that only the ast() signature was enforced (single-ast-method). A drifted shim silently hands rule authors wrong types with no signal.

The new rulecontext-shim-parity rule extracts the interface RuleContext member names from both surfaces and fails on any member present in one but missing from the other, in both directions. Extraction is a regex over raw text rather than ctx.ast()Bun.Transpiler erases type-only interface declarations before the tree is built, so the AST never contains them. Both failure directions were fire-tested against a deliberately drifted shim before committing; the rule is member-name parity only, so full signature/JSDoc parity stays a documented manual review item.

Stacked on #499

Base branch is fix/safepath-ancestor-symlink (#499), not main. Merge #499 first; GitHub retargets this PR to main automatically.

safePath/isWithinRoot/resolveUserPath move out of runner.ts into src/engine/safe-path.ts because runner.ts sits exactly at the 500-line max-lines cap. Review flagged that the extracted safePath only lstats the final path component, so a symlinked ancestor directory escapes the sandbox.

That gap is pre-existing — git diff against main shows the extraction is byte-for-byte apart from line-wrapping — so the fix went to its own fast-trackable PR (#499), per the standing preference for pre-existing security findings.

But this is the PR that creates safe-path.ts, so merging it first would land a freshly-added file containing a known escape. Rather than leave that to merge-order convention, this PR is stacked on #499: the fix is present in this branch, the review thread is addressed in this PR's own diff, and the ordering hazard is structural rather than advisory.

Tests

  • tests/engine/yaml-utils.test.ts — whole-document vs frontmatter dispatch, multi-document stream not misread as frontmatter, BOM, CRLF, empty block, body-never-parsed-as-YAML, invalid YAML, non-mapping block
  • tests/engine/check-case.test.ts — accept/reject tables per scheme, empty string, non-ASCII, unknown-scheme throw
  • tests/engine/runner-yaml-case.test.ts — end-to-end through runChecks, including the safePath sandbox on readYAML and a realistic kebab-case filename rule
  • tests/engine/safe-path.test.ts — extracted sandbox helpers

Docs

reference/rule-api.mdx in all three locales (en, nb, pt-br) per GEN-002: the RuleContext interface listing plus new readYAML and checkCase sections.

bun run validate passes (lint, typecheck, format:check, 1600+ tests, archgate check 45/45, knip, build:check).

@coderabbitai

coderabbitai Bot commented Jul 24, 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: e08635fe-1baf-46a3-99fb-61d254fd9b6d

📥 Commits

Reviewing files that changed from the base of the PR and between ff25b30 and 6ede380.

📒 Files selected for processing (6)
  • docs/public/llms-full.txt
  • docs/src/content/docs/nb/reference/rule-api.mdx
  • docs/src/content/docs/pt-br/reference/rule-api.mdx
  • docs/src/content/docs/reference/rule-api.mdx
  • src/engine/check-case.ts
  • tests/engine/check-case.test.ts
📝 Walkthrough

Walkthrough

Adds RuleContext.readYAML() and RuleContext.checkCase() contracts, implementations, generated shim declarations, documentation, and runtime wiring. YAML support handles full YAML files and Markdown frontmatter with validation and fail-closed errors. Casing support validates defined ASCII naming schemes and rejects unknown schemes. Tests cover parsing, frontmatter, sandboxing, casing, and rule execution. ADR enforcement now checks parity between the source context and generated shim.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning This PR adds ctx helpers instead of the requested opt-in .archgate relative-import support and containment checks from #490. Implement the .archgate config opt-in for allowed relative imports, with strict containment checks that reject paths outside .archgate/.
Out of Scope Changes check ⚠️ Warning It also bundles unrelated changes for ARCH-022, safe-path extraction, docs, tests, and llms-full generation beyond #490. Split unrelated ARCH-022, safe-path, docs, and generator changes into separate PRs or link matching issues for them.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding ctx.readYAML and ctx.checkCase helpers.
Description check ✅ Passed The description is directly related to the changes and explains the new RuleContext helpers and related context.
Docstring Coverage ✅ Passed Docstring coverage is 84.62% which is sufficient. The required threshold is 80.00%.

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

❤️ Share

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

@cloudflare-workers-and-pages

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

Copy link
Copy Markdown

Deploying archgate-cli with  Cloudflare Pages  Cloudflare Pages

Latest commit: 6ede380
Status: ✅  Deploy successful!
Preview URL: https://1a38d32a.archgate-cli.pages.dev
Branch Preview URL: https://feat-ctx-read-yaml-check-cas.archgate-cli.pages.dev

View logs

@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Code Coverage

Metric Value
Lines 91.4% (8253 / 9031)
Threshold 90% minimum — met
Platforms Linux + Windows

Full HTML report available in workflow artifacts.

Per-directory breakdown
Directory Coverage Lines
src/commands/ 89.0% 1959 / 2200
src/engine/ 94.1% 2136 / 2270
src/formats/ 98.7% 148 / 150
src/helpers/ 90.9% 4010 / 4411

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

🤖 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 `@docs/public/llms-full.txt`:
- Line 5223: Restore the actual parameterized return type for readYAML in both
documented locations: update docs/public/llms-full.txt lines 5223 and 5355-5369
so the signature renders Promise<ReadYamlResult> instead of Promise, including
the detailed API section.

In `@src/engine/safe-path.ts`:
- Around line 37-58: The safePath function only rejects symlinks at the final
path, so update it to inspect every existing path component from resolvedRoot
through absPath and reject any symbolic-link ancestor before returning. Preserve
the existing error handling for missing paths and access-denied UserErrors. Add
a test in tests/engine/safe-path.test.ts covering an ancestor-directory symlink
that points outside tempDir and asserting safePath rejects it.

In `@src/engine/yaml-utils.ts`:
- Line 11: Update FRONTMATTER_REGEX so the closing frontmatter delimiter must be
followed by the line ending or end of input, rejecting forms such as “----” and
“---note” while preserving valid closing fences.

In `@src/helpers/rules-shim.ts`:
- Around line 72-107: Update the generated CaseScheme JSDoc in the rules shim to
match the source contract, including the documented degenerate-value behavior
when a value matches multiple casing schemes. Preserve the existing CaseScheme
union and ensure its ambient documentation remains in parity with the
corresponding RuleContext surface.
🪄 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: cbe56a94-01d6-48d4-a372-c1747b98f5a4

📥 Commits

Reviewing files that changed from the base of the PR and between 0015542 and 1858dd6.

📒 Files selected for processing (17)
  • .archgate/adrs/ARCH-022-ast-aware-rule-context.md
  • .archgate/adrs/ARCH-022-ast-aware-rule-context.rules.ts
  • .claude/agent-memory/archgate-developer/project_oxlint_gotchas.md
  • docs/public/llms-full.txt
  • docs/src/content/docs/nb/reference/rule-api.mdx
  • docs/src/content/docs/pt-br/reference/rule-api.mdx
  • docs/src/content/docs/reference/rule-api.mdx
  • src/engine/check-case.ts
  • src/engine/runner.ts
  • src/engine/safe-path.ts
  • src/engine/yaml-utils.ts
  • src/formats/rules.ts
  • src/helpers/rules-shim.ts
  • tests/engine/check-case.test.ts
  • tests/engine/runner-yaml-case.test.ts
  • tests/engine/safe-path.test.ts
  • tests/engine/yaml-utils.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (5)
  • GitHub Check: Smoke Test (Windows) / Windows
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (csharp)
  • GitHub Check: Cloudflare Pages
🧰 Additional context used
📓 Path-based instructions (22)
src/**/*.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-004-no-barrel-files.md)

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

src/**/*.ts: Use await Bun.file(path).json() when reading JSON files in Bun TypeScript source code; do not use JSON.parse(await Bun.file(path).text()) or JSON.parse(fs.readFileSync(path, "utf-8")) for file reads.
Use Bun.JSONC.parse() when reading files that may contain comments, such as tsconfig.json, instead of plain JSON.parse() on file contents.
Reserve JSON.parse() for parsing JSON strings from non-file sources such as API responses or string variables; do not use it as the default for reading JSON files.

src/**/*.ts: In TypeScript source files under src/, use Bun.env instead of process.env for all environment variable reads and writes; process.env must not be used.
In TypeScript source files under src/, use nullish coalescing for environment-variable defaults, e.g. Bun.env.NODE_ENV ?? "production".
In TypeScript source files under src/, use Boolean(Bun.env.CI) for truthy checks on environment flags.
In TypeScript source files under src/, do not destructure Bun.env (for example, const { HOME } = Bun.env); access properties individually instead.
In TypeScript source files under src/, do not reference process.env even in comments that suggest using it.

src/**/*.ts: Heavy dependencies such as inquirer, posthog-node, @sentry/*, and any similarly expensive runtime-only dependency MUST be loaded with dynamic import() at the point of use, never as a static value import at module top level.
Type-only references to heavy modules must use import type (for example, import type { PostHog } from "posthog-node" or import type * as SentryNs from "@sentry/node-core/light"); type-only...

Files:

  • src/engine/check-case.ts
  • src/engine/safe-path.ts
  • src/engine/yaml-utils.ts
  • src/helpers/rules-shim.ts
  • src/formats/rules.ts
  • src/engine/runner.ts
**/*.{ts,tsx,js,jsx,mjs,cjs}

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

**/*.{ts,tsx,js,jsx,mjs,cjs}: Use Bun built-ins for file I/O (Bun.file, Bun.write), HTTP, subprocess execution (Bun.spawn), globbing (Bun.Glob), and testing (bun:test).
Do not use Node.js-specific APIs when Bun alternatives exist; for example, 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:

  • src/engine/check-case.ts
  • tests/engine/check-case.test.ts
  • src/engine/safe-path.ts
  • tests/engine/safe-path.test.ts
  • tests/engine/yaml-utils.test.ts
  • src/engine/yaml-utils.ts
  • src/helpers/rules-shim.ts
  • tests/engine/runner-yaml-case.test.ts
  • src/formats/rules.ts
  • src/engine/runner.ts
src/**/!(*platform).ts

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

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

Files:

  • src/engine/check-case.ts
  • src/engine/safe-path.ts
  • src/engine/yaml-utils.ts
  • src/helpers/rules-shim.ts
  • src/formats/rules.ts
  • src/engine/runner.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:

  • src/engine/check-case.ts
  • tests/engine/check-case.test.ts
  • src/engine/safe-path.ts
  • tests/engine/safe-path.test.ts
  • tests/engine/yaml-utils.test.ts
  • src/engine/yaml-utils.ts
  • src/helpers/rules-shim.ts
  • tests/engine/runner-yaml-case.test.ts
  • src/formats/rules.ts
  • src/engine/runner.ts
src/{helpers,engine}/**/*.ts

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

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

Files:

  • src/engine/check-case.ts
  • src/engine/safe-path.ts
  • src/engine/yaml-utils.ts
  • src/helpers/rules-shim.ts
  • src/engine/runner.ts
src/engine/**/*.ts

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

src/engine/**/*.ts: In the rules engine, list project files by matching glob patterns in memory against the git-tracked file set rather than walking the filesystem.
Route new engine file listings through listMatchingFiles for rule-facing inputs or matchTrackedFiles for trusted ADR frontmatter patterns.
When the target is a Git repository and respectGitignore is not false, pass the tracked set from getGitTrackedFiles to matching operations.
Per-run RunCaches must share glob results keyed by pattern and tracked mode and file text keyed by absolute path; cached promises should share in-flight work, and returned glob arrays must be copied before exposing them to rules.
Do not cache readJSON results because rules receive mutable objects whose shared mutation could leak between rules.
Do not filter filesystem scan results against the tracked set as a substitute for in-memory matching, and do not hardcode ignore directories; use the Git-derived tracked set instead.

src/engine/**/*.ts: Do not add Bun.spawn/Bun.spawnSync or child_process subprocess sites outside the sanctioned AST support and git-files implementations.
Do not normalize Python or Ruby AST output into ESTree; preserve each language's native AST shape.
Do not invoke Python or Ruby interpreters before language plausibility validation, and do not re-probe interpreter availability for every file.
Use ctx.ast(path, language, { rev: "base" }) or fileAtBase() for base comparisons; rule code must never shell out to git or an interpreter directly.

Files:

  • src/engine/check-case.ts
  • src/engine/safe-path.ts
  • src/engine/yaml-utils.ts
  • src/engine/runner.ts
**

⚙️ CodeRabbit configuration file

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

When reviewing, you must:

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

Files:

  • src/engine/check-case.ts
  • tests/engine/check-case.test.ts
  • src/engine/safe-path.ts
  • tests/engine/safe-path.test.ts
  • tests/engine/yaml-utils.test.ts
  • docs/public/llms-full.txt
  • src/engine/yaml-utils.ts
  • src/helpers/rules-shim.ts
  • docs/src/content/docs/pt-br/reference/rule-api.mdx
  • tests/engine/runner-yaml-case.test.ts
  • docs/src/content/docs/reference/rule-api.mdx
  • docs/src/content/docs/nb/reference/rule-api.mdx
  • src/formats/rules.ts
  • src/engine/runner.ts
tests/**/*.ts

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

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

tests/**/*.ts: Use Bun's built-in bun:test runner for all tests; do not import test utilities from node:test.
Shared test helpers must also use restoreEnv when restoring environment variables.

Files:

  • tests/engine/check-case.test.ts
  • tests/engine/safe-path.test.ts
  • tests/engine/yaml-utils.test.ts
  • tests/engine/runner-yaml-case.test.ts
tests/**/*.test.ts

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

tests/**/*.test.ts: Place tests in tests/ mirroring the src/ directory structure, and name test files with the .test.ts suffix.
Test public module interfaces rather than private implementation details, use descriptive test names, and do not depend on network access.
Use isolated temporary directories created with mkdtemp for filesystem tests, and clean them up in afterEach or afterAll. Do not use hardcoded user or system paths.
Close external SDK instances such as servers, clients, and transports in afterEach or afterAll; manage their lifecycle in test hooks rather than individual test bodies.
When a temporary git repository performs commits, configure local user.email and user.name immediately after git init; do not rely on global git identity.
Every runnable test() or it() must contain at least one expect() assertion. Make implicit no-throw contracts explicit with not.toThrow() or resolves.toBeUndefined(). Use test.skip or test.todo for intentional placeholders.
Use test.skipIf(condition), test.skip, or test.todo for conditional or intentionally disabled tests; do not use bare early returns or empty callbacks to skip tests, and do not skip without a tracking issue.
When adding assertions to an older test file, import expect from bun:test.
Mock HTTP requests by assigning directly to globalThis.fetch, and restore the original fetch implementation or mock in afterEach; do not mock node:fetch.
Mock first-party modules with import * as mod and spyOn(mod, "fn"), restoring spies after each test; do not use process-global mock.module() for first-party modules.
Wrap inline spyOn or mockImplementation usage in try/finally so mockRestore() always runs, or manage spies in beforeEach and afterEach.
When redirecting user-scope paths, mock node:os's homedir() rather than relying on runtime HOME overrides; use environment overrides only for code that reads environment variables directly at call time.
Re...

Files:

  • tests/engine/check-case.test.ts
  • tests/engine/safe-path.test.ts
  • tests/engine/yaml-utils.test.ts
  • tests/engine/runner-yaml-case.test.ts
src/helpers/rules-shim.ts

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

Mirror every RuleContext surface change, including ast, fileAtBase, ambient types, signatures, and accompanying JSDoc, in the generated rules shim.

Files:

  • src/helpers/rules-shim.ts
docs/src/content/docs/**/*.mdx

📄 CodeRabbit inference engine (.archgate/adrs/GEN-001-documentation-site.md)

docs/src/content/docs/**/*.mdx: Use MDX (.mdx) for all content pages under docs/src/content/docs/.
Escape literal curly braces in MDX when showing template syntax; do not use bare {} in prose or code-fence labels.

Files:

  • docs/src/content/docs/pt-br/reference/rule-api.mdx
  • docs/src/content/docs/reference/rule-api.mdx
  • docs/src/content/docs/nb/reference/rule-api.mdx
{docs/src/content/docs/**/*.mdx,docs/astro.config.mjs}

📄 CodeRabbit inference engine (.archgate/adrs/GEN-001-documentation-site.md)

When adding a new documentation page, create the MDX file in docs/src/content/docs/<category>/<slug>.mdx and add the page to docs/astro.config.mjs sidebar configuration.

Files:

  • docs/src/content/docs/pt-br/reference/rule-api.mdx
  • docs/src/content/docs/reference/rule-api.mdx
  • docs/src/content/docs/nb/reference/rule-api.mdx
docs/src/content/docs/**

📄 CodeRabbit inference engine (.archgate/adrs/GEN-001-documentation-site.md)

Do not create documentation content files outside docs/src/content/docs/; Starlight content must live in that exact directory structure.

Files:

  • docs/src/content/docs/pt-br/reference/rule-api.mdx
  • docs/src/content/docs/reference/rule-api.mdx
  • docs/src/content/docs/nb/reference/rule-api.mdx
docs/src/content/docs/**/*.{mdx,md}

📄 CodeRabbit inference engine (.archgate/adrs/GEN-002-docs-i18n.md)

docs/src/content/docs/**/*.{mdx,md}: For every English docs page under docs/src/content/docs/, create a translated file in each locale directory with the exact same relative path and filename, and ensure every locale file corresponds to an existing root file (no orphan translations).
When adding or modifying English documentation content, update the corresponding locale files in the same pull request.
Translate all user-facing prose in docs pages, including titles, descriptions, headings, paragraphs, list items, table text, and admonition content.
Translate user-visible text props in Starlight components such as <Card title="..."> and <LinkCard description="...">.
Keep code blocks, CLI commands, file paths, TypeScript identifiers, technical terms, import statements, component names, and link/href/slug attribute values in English.
Keep internal links unchanged and do not add locale prefixes (for example, use /guides/... rather than /pt-br/guides/...).
Preserve MDX curly-brace escaping (\{\}) in translated content.
Preserve Starlight component import statements identically in translated files.

Files:

  • docs/src/content/docs/pt-br/reference/rule-api.mdx
  • docs/src/content/docs/reference/rule-api.mdx
  • docs/src/content/docs/nb/reference/rule-api.mdx
docs/src/content/docs/pt-br/**/*.{mdx,md}

📄 CodeRabbit inference engine (.archgate/adrs/GEN-002-docs-i18n.md)

Use correct Portuguese diacritical marks in Brazilian Portuguese translations; never write unaccented Portuguese.

Files:

  • docs/src/content/docs/pt-br/reference/rule-api.mdx
docs/src/content/docs/reference/**/*.mdx

📄 CodeRabbit inference engine (.archgate/adrs/GEN-001-documentation-site.md)

Keep reference pages accurate to the CLI source code; when CLI APIs change, update the corresponding reference docs in the same PR.

Files:

  • docs/src/content/docs/reference/rule-api.mdx
docs/src/content/docs/nb/**/*.{mdx,md}

📄 CodeRabbit inference engine (.archgate/adrs/GEN-002-docs-i18n.md)

Use Norwegian Bokmål (not Nynorsk) for Norwegian translations, with the informal du form and correct Norwegian characters (æ, ø, å).

Files:

  • docs/src/content/docs/nb/reference/rule-api.mdx
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/rules.ts
src/formats/rules.ts

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

Expose exactly one RuleContext.ast(path: string, language: AstLanguage, opts?: AstOptions): Promise<AstNode> method, with language-specific dispatch hidden from rule authors; document that returned AST shapes differ by language.

Files:

  • src/formats/rules.ts
src/engine/runner.ts

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

src/engine/runner.ts: Implement ctx.ast() inside createRuleContext() and execute path safety, language plausibility, interpreter availability probing, and guarded invocation in exactly that order before any subprocess is spawned.
Do not trust node.loc for TypeScript AST nodes because parsing transpiled output changes locations; re-locate constructs in the original source before reporting lines. JavaScript locations are source-accurate.

Files:

  • src/engine/runner.ts
src/engine/{runner,ast-support}.ts

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

src/engine/{runner,ast-support}.ts: Use array-based Bun.spawn arguments for Python/Ruby AST invocations; never expose raw subprocess or filesystem primitives through RuleContext.
Python AST subprocesses must use isolated mode (python -I -c ...) to prevent the target project's working directory from shadowing standard-library modules.
For TypeScript and JavaScript, reuse the in-process meriyah parser; factor the duplicated parseModule() logic into one shared exported helper used by both rule-scanner.ts and ctx.ast().
For Python and Ruby, use only the interpreters' standard-library AST facilities (ast and Ripper), with no tree-sitter, WASM grammar, native binding, or other new production dependency.
Probe Python/Ruby interpreter availability using platform-appropriate candidates, select the first resolved executable for the real invocation, and cache the probe once per check invocation.
Strip a leading UTF-8 BOM before parsing Python or Ruby source; retain Python -I isolation.
ctx.ast() must throw on missing interpreters, parse failures, and base-revision absence; errors must distinguish environment failure, parse failure, missing base, and files absent at base. fileAtBase() alone returns null for missing base or missing files.
For ast(..., { rev: "base" }), apply the same path-safety, language-plausibility, interpreter-probe, and guarded-invocation flow as working-tree parsing; parse TypeScript/JavaScript in process and use isolated temporary files for Python/Ruby base content.
When { comments: true } is requested, attach structured root comments tokens with type, delimiter-stripped value, and loc; omit the array otherwise.
Extract TypeScript/JavaScript comments from the original source, using a string/template-literal-aware scanner; do not treat regex-literal awareness as guaranteed.
Extract Python comments with tokenize and Ruby comments with Ripper.lex; tokenizer/lexer errors on otherwise parseable input degrade to an empt...

Files:

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

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

Base-revision reads must use the merge base of --base and HEAD, matching the existing three-dot changedFiles comparison; keep git reads confined to git-files.ts.

Files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • src/helpers/rules-shim.ts
🪛 LanguageTool
docs/src/content/docs/pt-br/reference/rule-api.mdx

[uncategorized] ~218-~218: Pontuação duplicada
Context: ...bjeto de resultado cobre as duas formas -- um mapeamento frontmatter anulável e ...

(DOUBLE_PUNCTUATION_XML)


[uncategorized] ~220-~220: Pontuação duplicada
Context: ...re null; content é o valor parseado -- faça o cast para o tipo esperado na sua...

(DOUBLE_PUNCTUATION_XML)


[uncategorized] ~220-~220: Encontrada possível ausência de vírgula.
Context: ...res --- de um stream multi-documento (manifestos Kubernetes, configurações de CI) nunca ...

(AI_PT_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~221-~221: Esta conjunção deve ser separada por vírgulas e só deve ser utilizada no início duma frase para efeitos de estilo.
Context: ..., espelhando fileAtBase), {} quando existe mas está vazio. content é o restante do c...

(VERB_COMMA_CONJUNCTION)


[uncategorized] ~221-~221: Pontuação duplicada
Context: ... do texto, sem espaços nas extremidades -- ele não é parseado como YAML. `rea...

(DOUBLE_PUNCTUATION_XML)


[uncategorized] ~404-~404: Pontuação duplicada
Context: ...tring segue um esquema de capitalização -- a substituição integrada para os regexe...

(DOUBLE_PUNCTUATION_XML)


[uncategorized] ~404-~404: Pontuação duplicada
Context: ...cisavam escrever à mão. Síncrono e puro -- não precisa de await. A correspondênc...

(DOUBLE_PUNCTUATION_XML)

🔇 Additional comments (20)
.archgate/adrs/ARCH-022-ast-aware-rule-context.rules.ts (2)

25-45: LGTM!


226-269: LGTM!

.archgate/adrs/ARCH-022-ast-aware-rule-context.md (2)

97-97: LGTM!


152-158: LGTM!

src/formats/rules.ts (1)

65-112: LGTM!

Also applies to: 261-279, 335-342

src/helpers/rules-shim.ts (1)

244-261: LGTM!

Also applies to: 312-319

docs/public/llms-full.txt (1)

5544-5580: LGTM!

docs/src/content/docs/reference/rule-api.mdx (1)

69-72: LGTM!

Also applies to: 199-251, 391-427

src/engine/check-case.ts (1)

1-41: LGTM!

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

1-10: LGTM!

Also applies to: 16-82

tests/engine/check-case.test.ts (1)

1-93: LGTM!

docs/src/content/docs/nb/reference/rule-api.mdx (1)

69-72: LGTM!

Also applies to: 199-251, 391-427

docs/src/content/docs/pt-br/reference/rule-api.mdx (1)

69-72: LGTM!

Also applies to: 199-251, 391-427

src/engine/safe-path.ts (1)

11-18: LGTM!

Also applies to: 25-31

src/engine/runner.ts (2)

3-3: LGTM!

Also applies to: 12-18, 39-39, 51-53


383-400: 🎯 Functional Correctness

No shim parity issue for readYAML and checkCase.

src/formats/rules.ts and src/helpers/rules-shim.ts both declare matching RuleContext members, including ReadYamlResult and CaseScheme.

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

15-15: LGTM!

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

1-101: LGTM!

tests/engine/safe-path.test.ts (1)

25-65: LGTM!

tests/engine/runner-yaml-case.test.ts (1)

1-170: LGTM!

Comment thread docs/public/llms-full.txt Outdated
Comment thread src/engine/safe-path.ts
Comment thread src/engine/yaml-utils.ts Outdated
Comment thread src/helpers/rules-shim.ts Outdated
@rhuanbarreto

Copy link
Copy Markdown
Contributor Author

Thanks — all four findings were real. Verified each against the code rather than applying them blind; three are fixed here in d370795, one is split out.

1. llms-full.txt publishing a bare Promise — fixed, and it was bigger than reported.

Worth flagging that the framing ("restore the return type") wasn't quite right: I never edited that file, and the damage was not specific to readYAML. The root cause is in the generator — stripJsx() in docs/scripts/generate-llms-full.ts strips <[A-Z]\w+…> as a JSX component tag, and it ran over code as well as prose. So it ate any generic whose type argument is capitalized, while lowercase ones survived:

Signature Published to llms-full.txt on main today
Promise<string> Promise<string>
Promise<unknown> Promise<unknown>
Promise<GrepMatch[]> Promise
Promise<AstNode> Promise

grep, grepFiles and ast have been advertising the wrong contract to LLM consumers on main — this PR just added a third victim. Hand-patching the generated artifact would have been wrong twice over (CI regenerates it, and it would leave the other three broken), so the generator is fixed instead: stripping now skips fenced blocks and inline code spans, the latter carrying the same damage inside reference tables. That was a real gap in my first attempt at the fix — I only protected fences, regenerated, and found | \Promise` |` still sitting in a table. All four signatures are now correct and prose stripping is unchanged.

2. FRONTMATTER_REGEX accepting ---- / ---note — fixed. Confirmed: the lazy \n--- matched the first three characters of a ---- line, parsing a truncated block and leaving the stray - at the head of the body. Both fences now anchor to their own line, with trailing spaces/tabs tolerated and end-of-input accepted. Tests added for all three malformed fences plus the tolerated forms. Noted in the code that this is marginally stricter than parseAdr's regex in src/formats/adr.ts — they agree on every well-formed file and differ only on malformed fences.

3. CaseScheme JSDoc parity in the shim — fixed. Correct, and a fair hit: I dropped the degenerate-value sentence when mirroring, in the same PR that adds rulecontext-shim-parity and documents full-JSDoc parity as a manual review item. First thing that manual item caught was me.

4. Ancestor-symlink escape — real, pre-existing, and split out to #499.

Confirmed by construction: resolve() is lexical, so isWithinRoot passes on the lexical path; the leaf of <root>/linkdir/secret.txt is an ordinary file so the leaf lstat passes; then the OS resolves linkdir and reads outside the project.

But git diff against main shows safe-path.ts is a byte-for-byte move of these helpers out of runner.ts (only line-wrapping differs) — the gap is on main today and this PR did not introduce it. Per the maintainer's call on #491, a pre-existing security finding gets its own fast-trackable PR rather than riding along inside a feature review: #499.

That PR also has the parts that don't fit in a footnote — the fix deliberately does not inspect components at or above the root (macOS /var/private/var would reject every temp-dir root), uses per-component boolean lstat rather than a realpath string comparison (which case-canonicalizes on Windows/macOS and would reject legitimate paths), memoizes verified directories because safePath runs per glob result, and its regression tests use symlink type "junction" so the ancestor case actually runs on Windows instead of skipping.

@rhuanbarreto
rhuanbarreto force-pushed the feat/ctx-read-yaml-check-case branch from 7f74a84 to b48dcb2 Compare July 25, 2026 00:33
@rhuanbarreto
rhuanbarreto changed the base branch from main to fix/safepath-ancestor-symlink July 25, 2026 00:33
rhuanbarreto added a commit that referenced this pull request Jul 25, 2026
…ctory (#499)

Split out of #497 at your request, following the same call you made on
#491 (pre-existing security finding → standalone PR).

Surfaced by CodeRabbit while reviewing #497. The finding is real and
**predates that PR** — #497 only moved this code verbatim out of
`runner.ts`, so the gap exists on `main` today.

## The escape

`safePath()` `lstat`'d only the **final** path component. Given a
symlinked ancestor:

```
<root>/linkdir  ->  /somewhere/outside
```

then `ctx.readFile("linkdir/secret.txt")` gets through every gate:

1. `resolveUserPath()` uses `resolve()`, which is **purely lexical** —
it does not resolve symlinks, so the path stays
`<root>/linkdir/secret.txt`.
2. `isWithinRoot()` is a string-prefix test on that lexical path —
**passes**.
3. `lstatSync(absPath)` inspects only the leaf, which is an ordinary
file, not a link — **passes**.
4. `Bun.file(absPath).text()` hands the path to the OS, which *does*
resolve `linkdir` — and reads outside the project.

So a `.rules.ts` file could read arbitrary files outside the project
through a symlinked directory, which is exactly the boundary ARCH-024
exists to hold.

## The fix

The leaf check and the ancestor check become one walk over every path
component **below** the project root.

Two deliberate constraints:

- **Nothing at or above the root is inspected.** The project root's own
location is the user's business, and on macOS the temp prefix is itself
a symlink (`/var` → `/private/var`) — walking above the root would
reject every path under a temp-dir root, breaking a large share of the
test suite for no security gain.
- **Each component is a boolean `lstat`, not a string comparison against
`realpath`.** A `realpath`-and-compare implementation is one syscall
instead of N and was my first instinct, but `realpath`
case-canonicalizes on Windows and macOS, so it would reject
case-mismatched-but-legitimate paths. The boolean question has no such
failure mode.

Verified directories are memoized per process. `safePath` runs once per
glob result and sibling files share every ancestor, so without the memo
the walk would re-`lstat` the same handful of directories thousands of
times per check run (`grepFiles` × 40+ rules).

## Tests

- `tests/engine/safe-path.test.ts` — ancestor symlink rejected, leaf
symlink rejected, deep real-directory chain accepted, non-existent
in-root path tolerated, root itself accepted, traversal rejected
- `tests/commands/check-security.test.ts` — end-to-end through
`runChecks`: a rule reading through a symlinked parent is blocked, and a
rule reading a deeply nested real path still succeeds (guards against
over-rejection)

**The regression tests actually run on Windows.** They create the
directory link with symlink type `"junction"` — ignored on POSIX (plain
symlink), but on Windows a junction needs no admin privileges and
`lstat` reports it as a symbolic link. The pre-existing file-symlink
test still skips on Windows, since file symlinks require elevation or
Developer Mode; that limitation is now isolated to that one case rather
than covering the whole symlink surface.

I confirmed the fix is load-bearing rather than trusting a green suite:
with the guard call stubbed out, the ancestor test fails; with it in
place, it passes.

## Note on overlap with #497

The helpers move to `src/engine/safe-path.ts` here because `runner.ts`
sits **exactly** at the 500-line `max-lines` cap on `main` — the fix
cannot land in place. #497 performs the same extraction independently
for the same reason. The two versions of the file are identical apart
from this fix, so whichever merges second needs a trivial rebase.

`bun run validate` passes (lint, typecheck, format:check, full suite,
`archgate check` 44/44, knip, build:check).

Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
Base automatically changed from fix/safepath-ancestor-symlink to main July 25, 2026 00:36
@archgatebot archgatebot Bot mentioned this pull request Jul 24, 2026

@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

🤖 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 `@docs/scripts/generate-llms-full.ts`:
- Around line 77-82: Update the comment near stripJsx() to use forward-behavior
wording: explain that applying the component-tag patterns to code can remove
capitalized generic type arguments from fenced blocks and inline table spans,
while lowercase generic arguments remain intact. Replace the informal “ate” and
historical “affected” phrasing without changing the documented examples or
scope.

In `@src/engine/yaml-utils.ts`:
- Line 19: Update FRONTMATTER_REGEX to allow the closing fence immediately after
the opening fence’s newline, while continuing to capture multiline frontmatter
and preserve the existing delimiter and content handling. Ensure an empty block
such as “---\n---\nbody” matches and produces the documented empty frontmatter
object with “body” as content.

In `@src/formats/rules.ts`:
- Around line 261-279: The readYAML() JSDoc contract must match ReadYamlResult
and remain synchronized across both declarations. In src/formats/rules.ts lines
261-279, describe content as YamlValue instead of unknown and retain the
complete source semantics; mirror the corrected JSDoc, including extension
dispatch, frontmatter behavior, content handling, and failure conditions, in
src/helpers/rules-shim.ts lines 246-263.
🪄 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: 2fa43f2e-c92a-4f22-9313-1f7cd20525a6

📥 Commits

Reviewing files that changed from the base of the PR and between 1858dd6 and 8c47511.

📒 Files selected for processing (18)
  • .archgate/adrs/ARCH-022-ast-aware-rule-context.md
  • .archgate/adrs/ARCH-022-ast-aware-rule-context.rules.ts
  • .claude/agent-memory/archgate-developer/project_oxlint_gotchas.md
  • .claude/agent-memory/archgate-developer/project_rules_engine_internals.md
  • .claude/agent-memory/archgate-developer/project_test_isolation_gotchas.md
  • docs/public/llms-full.txt
  • docs/scripts/generate-llms-full.ts
  • docs/src/content/docs/nb/reference/rule-api.mdx
  • docs/src/content/docs/pt-br/reference/rule-api.mdx
  • docs/src/content/docs/reference/rule-api.mdx
  • src/engine/check-case.ts
  • src/engine/runner.ts
  • src/engine/yaml-utils.ts
  • src/formats/rules.ts
  • src/helpers/rules-shim.ts
  • tests/engine/check-case.test.ts
  • tests/engine/runner-yaml-case.test.ts
  • tests/engine/yaml-utils.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Smoke Test (Windows) / Windows
  • GitHub Check: Cloudflare Pages
🧰 Additional context used
📓 Path-based instructions (23)
**/*.{ts,tsx,js,jsx,mjs,cjs}

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

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

Files:

  • tests/engine/check-case.test.ts
  • src/engine/check-case.ts
  • src/engine/yaml-utils.ts
  • docs/scripts/generate-llms-full.ts
  • tests/engine/yaml-utils.test.ts
  • src/formats/rules.ts
  • src/helpers/rules-shim.ts
  • tests/engine/runner-yaml-case.test.ts
  • src/engine/runner.ts
tests/**/*.ts

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

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

tests/**/*.ts: Use Bun's built-in bun:test runner for all tests; do not import test utilities from node:test.
Shared test helpers must also use restoreEnv when restoring environment variables.

Files:

  • tests/engine/check-case.test.ts
  • tests/engine/yaml-utils.test.ts
  • tests/engine/runner-yaml-case.test.ts
{src,tests}/**/*.ts

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

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

Files:

  • tests/engine/check-case.test.ts
  • src/engine/check-case.ts
  • src/engine/yaml-utils.ts
  • tests/engine/yaml-utils.test.ts
  • src/formats/rules.ts
  • src/helpers/rules-shim.ts
  • tests/engine/runner-yaml-case.test.ts
  • src/engine/runner.ts
tests/**/*.test.ts

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

tests/**/*.test.ts: Place tests in tests/ mirroring the src/ directory structure, and name test files with the .test.ts suffix.
Test public module interfaces rather than private implementation details, use descriptive test names, and do not depend on network access.
Use isolated temporary directories created with mkdtemp for filesystem tests, and clean them up in afterEach or afterAll. Do not use hardcoded user or system paths.
Close external SDK instances such as servers, clients, and transports in afterEach or afterAll; manage their lifecycle in test hooks rather than individual test bodies.
When a temporary git repository performs commits, configure local user.email and user.name immediately after git init; do not rely on global git identity.
Every runnable test() or it() must contain at least one expect() assertion. Make implicit no-throw contracts explicit with not.toThrow() or resolves.toBeUndefined(). Use test.skip or test.todo for intentional placeholders.
Use test.skipIf(condition), test.skip, or test.todo for conditional or intentionally disabled tests; do not use bare early returns or empty callbacks to skip tests, and do not skip without a tracking issue.
When adding assertions to an older test file, import expect from bun:test.
Mock HTTP requests by assigning directly to globalThis.fetch, and restore the original fetch implementation or mock in afterEach; do not mock node:fetch.
Mock first-party modules with import * as mod and spyOn(mod, "fn"), restoring spies after each test; do not use process-global mock.module() for first-party modules.
Wrap inline spyOn or mockImplementation usage in try/finally so mockRestore() always runs, or manage spies in beforeEach and afterEach.
When redirecting user-scope paths, mock node:os's homedir() rather than relying on runtime HOME overrides; use environment overrides only for code that reads environment variables directly at call time.
Re...

Files:

  • tests/engine/check-case.test.ts
  • tests/engine/yaml-utils.test.ts
  • tests/engine/runner-yaml-case.test.ts
**

⚙️ CodeRabbit configuration file

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

When reviewing, you must:

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

Files:

  • tests/engine/check-case.test.ts
  • src/engine/check-case.ts
  • src/engine/yaml-utils.ts
  • docs/scripts/generate-llms-full.ts
  • tests/engine/yaml-utils.test.ts
  • src/formats/rules.ts
  • docs/src/content/docs/nb/reference/rule-api.mdx
  • src/helpers/rules-shim.ts
  • tests/engine/runner-yaml-case.test.ts
  • docs/src/content/docs/pt-br/reference/rule-api.mdx
  • docs/src/content/docs/reference/rule-api.mdx
  • src/engine/runner.ts
  • docs/public/llms-full.txt
src/**/*.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-004-no-barrel-files.md)

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

src/**/*.ts: Use await Bun.file(path).json() when reading JSON files in Bun TypeScript source code; do not use JSON.parse(await Bun.file(path).text()) or JSON.parse(fs.readFileSync(path, "utf-8")) for file reads.
Use Bun.JSONC.parse() when reading files that may contain comments, such as tsconfig.json, instead of plain JSON.parse() on file contents.
Reserve JSON.parse() for parsing JSON strings from non-file sources such as API responses or string variables; do not use it as the default for reading JSON files.

src/**/*.ts: In TypeScript source files under src/, use Bun.env instead of process.env for all environment variable reads and writes; process.env must not be used.
In TypeScript source files under src/, use nullish coalescing for environment-variable defaults, e.g. Bun.env.NODE_ENV ?? "production".
In TypeScript source files under src/, use Boolean(Bun.env.CI) for truthy checks on environment flags.
In TypeScript source files under src/, do not destructure Bun.env (for example, const { HOME } = Bun.env); access properties individually instead.
In TypeScript source files under src/, do not reference process.env even in comments that suggest using it.

src/**/*.ts: Heavy dependencies such as inquirer, posthog-node, @sentry/*, and any similarly expensive runtime-only dependency MUST be loaded with dynamic import() at the point of use, never as a static value import at module top level.
Type-only references to heavy modules must use import type (for example, import type { PostHog } from "posthog-node" or import type * as SentryNs from "@sentry/node-core/light"); type-only...

Files:

  • src/engine/check-case.ts
  • src/engine/yaml-utils.ts
  • src/formats/rules.ts
  • src/helpers/rules-shim.ts
  • src/engine/runner.ts
src/**/!(*platform).ts

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

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

Files:

  • src/engine/check-case.ts
  • src/engine/yaml-utils.ts
  • src/formats/rules.ts
  • src/helpers/rules-shim.ts
  • src/engine/runner.ts
src/{helpers,engine}/**/*.ts

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

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

Files:

  • src/engine/check-case.ts
  • src/engine/yaml-utils.ts
  • src/helpers/rules-shim.ts
  • src/engine/runner.ts
src/engine/**/*.ts

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

src/engine/**/*.ts: In the rules engine, list project files by matching glob patterns in memory against the git-tracked file set rather than walking the filesystem.
Route new engine file listings through listMatchingFiles for rule-facing inputs or matchTrackedFiles for trusted ADR frontmatter patterns.
When the target is a Git repository and respectGitignore is not false, pass the tracked set from getGitTrackedFiles to matching operations.
Per-run RunCaches must share glob results keyed by pattern and tracked mode and file text keyed by absolute path; cached promises should share in-flight work, and returned glob arrays must be copied before exposing them to rules.
Do not cache readJSON results because rules receive mutable objects whose shared mutation could leak between rules.
Do not filter filesystem scan results against the tracked set as a substitute for in-memory matching, and do not hardcode ignore directories; use the Git-derived tracked set instead.

Files:

  • src/engine/check-case.ts
  • src/engine/yaml-utils.ts
  • src/engine/runner.ts
src/engine/**

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

src/engine/**: Do not add unsanctioned Bun.spawn/Bun.spawnSync calls or child_process imports; AST subprocesses belong only in the sanctioned AST helper and git subprocesses in git-files.ts.
Keep Python/Ruby AST results in their native language-specific shapes; do not normalize them into ESTree or another cross-language vocabulary.
Use isWindows() for platform detection when ordering interpreter candidates.

Files:

  • src/engine/check-case.ts
  • src/engine/yaml-utils.ts
  • src/engine/runner.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/rules.ts
src/formats/rules.ts

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

Expose exactly one RuleContext.ast(path, language, opts?) method supporting TypeScript, JavaScript, Python, and Ruby, with optional { rev?: "base"; comments?: boolean } and language-native AST shapes.

Files:

  • src/formats/rules.ts
docs/src/content/docs/**/*.mdx

📄 CodeRabbit inference engine (.archgate/adrs/GEN-001-documentation-site.md)

docs/src/content/docs/**/*.mdx: Use MDX (.mdx) for all content pages under docs/src/content/docs/.
Escape literal curly braces in MDX when showing template syntax; do not use bare {} in prose or code-fence labels.

Files:

  • docs/src/content/docs/nb/reference/rule-api.mdx
  • docs/src/content/docs/pt-br/reference/rule-api.mdx
  • docs/src/content/docs/reference/rule-api.mdx
{docs/src/content/docs/**/*.mdx,docs/astro.config.mjs}

📄 CodeRabbit inference engine (.archgate/adrs/GEN-001-documentation-site.md)

When adding a new documentation page, create the MDX file in docs/src/content/docs/<category>/<slug>.mdx and add the page to docs/astro.config.mjs sidebar configuration.

Files:

  • docs/src/content/docs/nb/reference/rule-api.mdx
  • docs/src/content/docs/pt-br/reference/rule-api.mdx
  • docs/src/content/docs/reference/rule-api.mdx
docs/src/content/docs/**

📄 CodeRabbit inference engine (.archgate/adrs/GEN-001-documentation-site.md)

Do not create documentation content files outside docs/src/content/docs/; Starlight content must live in that exact directory structure.

Files:

  • docs/src/content/docs/nb/reference/rule-api.mdx
  • docs/src/content/docs/pt-br/reference/rule-api.mdx
  • docs/src/content/docs/reference/rule-api.mdx
docs/src/content/docs/**/*.{mdx,md}

📄 CodeRabbit inference engine (.archgate/adrs/GEN-002-docs-i18n.md)

docs/src/content/docs/**/*.{mdx,md}: For every English docs page under docs/src/content/docs/, create a translated file in each locale directory with the exact same relative path and filename, and ensure every locale file corresponds to an existing root file (no orphan translations).
When adding or modifying English documentation content, update the corresponding locale files in the same pull request.
Translate all user-facing prose in docs pages, including titles, descriptions, headings, paragraphs, list items, table text, and admonition content.
Translate user-visible text props in Starlight components such as <Card title="..."> and <LinkCard description="...">.
Keep code blocks, CLI commands, file paths, TypeScript identifiers, technical terms, import statements, component names, and link/href/slug attribute values in English.
Keep internal links unchanged and do not add locale prefixes (for example, use /guides/... rather than /pt-br/guides/...).
Preserve MDX curly-brace escaping (\{\}) in translated content.
Preserve Starlight component import statements identically in translated files.

Files:

  • docs/src/content/docs/nb/reference/rule-api.mdx
  • docs/src/content/docs/pt-br/reference/rule-api.mdx
  • docs/src/content/docs/reference/rule-api.mdx
docs/src/content/docs/nb/**/*.{mdx,md}

📄 CodeRabbit inference engine (.archgate/adrs/GEN-002-docs-i18n.md)

Use Norwegian Bokmål (not Nynorsk) for Norwegian translations, with the informal du form and correct Norwegian characters (æ, ø, å).

Files:

  • docs/src/content/docs/nb/reference/rule-api.mdx
src/helpers/rules-shim.ts

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

Mirror every RuleContext surface change, including ast, fileAtBase, ambient types, signatures, and JSDoc, in the generated rules shim.

Files:

  • src/helpers/rules-shim.ts
docs/src/content/docs/pt-br/**/*.{mdx,md}

📄 CodeRabbit inference engine (.archgate/adrs/GEN-002-docs-i18n.md)

Use correct Portuguese diacritical marks in Brazilian Portuguese translations; never write unaccented Portuguese.

Files:

  • docs/src/content/docs/pt-br/reference/rule-api.mdx
docs/src/content/docs/reference/**/*.mdx

📄 CodeRabbit inference engine (.archgate/adrs/GEN-001-documentation-site.md)

Keep reference pages accurate to the CLI source code; when CLI APIs change, update the corresponding reference docs in the same PR.

Files:

  • docs/src/content/docs/reference/rule-api.mdx
src/engine/runner.ts

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

src/engine/runner.ts: Implement AST dispatch internally in createRuleContext(); rule authors must not receive direct subprocess or filesystem primitives.
For Python and Ruby AST operations, execute guardrails in this exact order before any subprocess: path safety, language plausibility, interpreter availability probe, then guarded invocation.

Files:

  • src/engine/runner.ts
src/engine/{runner,ast-support}.ts

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

src/engine/{runner,ast-support}.ts: Use Bun.spawn with array arguments for Python/Ruby AST subprocesses; never use shell interpolation, Bun.$, or direct child-process access from rules.
Run Python AST subprocesses with isolated mode (python -I -c ...) and strip a leading UTF-8 BOM before parsing Python or Ruby source.
Reuse the in-process meriyah parser for TypeScript and JavaScript, factoring duplicated parseModule() calls in rule-scanner.ts into one shared exported helper used by both scanner and ctx.ast().
Probe Python/Ruby interpreter availability using platform-appropriate candidates, select the first available executable for both probing and invocation, and cache the result once per check.
Throw on missing interpreters and parse failures, with distinguishable error messages; never return null or another silent-failure sentinel from ast().
Base AST reads must apply the same path-safety, language-plausibility, interpreter-probe, and guarded-invocation ordering as working-tree reads; missing base or base-added files must throw distinguishable errors.
Implement fileAtBase(path) as the exception to AST failure semantics: return null when no base exists or the file did not exist at the base, rather than throwing.
When { comments: true } is requested, attach structured root-level comments with type, delimiter-stripped value, and loc; omit the comments array unless explicitly requested.

Files:

  • src/engine/runner.ts
src/engine/{runner,ast-support,git-files}.ts

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

For { rev: "base" }, resolve the merge base of --base and HEAD, use the same base as changedFiles, and keep git reads confined to git-files.ts.

Files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • .archgate/adrs/ARCH-022-ast-aware-rule-context.md
🪛 LanguageTool
docs/src/content/docs/pt-br/reference/rule-api.mdx

[uncategorized] ~218-~218: Pontuação duplicada
Context: ...bjeto de resultado cobre as duas formas -- um mapeamento frontmatter anulável e ...

(DOUBLE_PUNCTUATION_XML)


[uncategorized] ~220-~220: Pontuação duplicada
Context: ...re null; content é o valor parseado -- faça o cast para o tipo esperado na sua...

(DOUBLE_PUNCTUATION_XML)


[uncategorized] ~220-~220: Encontrada possível ausência de vírgula.
Context: ...res --- de um stream multi-documento (manifestos Kubernetes, configurações de CI) nunca ...

(AI_PT_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~221-~221: Esta conjunção deve ser separada por vírgulas e só deve ser utilizada no início duma frase para efeitos de estilo.
Context: ..., espelhando fileAtBase), {} quando existe mas está vazio. content é o restante do c...

(VERB_COMMA_CONJUNCTION)


[uncategorized] ~221-~221: Pontuação duplicada
Context: ... do texto, sem espaços nas extremidades -- ele não é parseado como YAML. `rea...

(DOUBLE_PUNCTUATION_XML)


[uncategorized] ~404-~404: Pontuação duplicada
Context: ...tring segue um esquema de capitalização -- a substituição integrada para os regexe...

(DOUBLE_PUNCTUATION_XML)


[uncategorized] ~404-~404: Pontuação duplicada
Context: ...cisavam escrever à mão. Síncrono e puro -- não precisa de await. A correspondênc...

(DOUBLE_PUNCTUATION_XML)

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

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

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

🔇 Additional comments (18)
tests/engine/check-case.test.ts (1)

1-93: LGTM!

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

1-127: LGTM!

tests/engine/runner-yaml-case.test.ts (1)

1-169: LGTM!

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

15-15: LGTM!

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

8-8: LGTM!

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

15-15: LGTM!

src/engine/check-case.ts (1)

18-23: 🎯 Functional Correctness

No change needed. The /u flag restricts these patterns so a final newline does not satisfy any scheme; values such as "Foo\n" fail camelCase.

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

65-112: LGTM!

Also applies to: 335-342

src/helpers/rules-shim.ts (1)

72-115: LGTM!

Also applies to: 314-321

docs/src/content/docs/nb/reference/rule-api.mdx (1)

69-72: LGTM!

Also applies to: 199-251, 391-427

docs/src/content/docs/pt-br/reference/rule-api.mdx (1)

69-72: LGTM!

Also applies to: 199-251, 391-427

docs/src/content/docs/reference/rule-api.mdx (1)

69-72: LGTM!

Also applies to: 199-251, 391-427

docs/public/llms-full.txt (1)

5218-5226: LGTM!

Also applies to: 5353-5404, 5544-5580

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

25-45: LGTM!

Also applies to: 226-269

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

97-97: LGTM!

Also applies to: 152-158

src/engine/runner.ts (3)

3-3: LGTM!

Also applies to: 12-12, 39-39, 51-53


383-392: LGTM!


394-400: LGTM!

Comment thread docs/scripts/generate-llms-full.ts Outdated
Comment thread src/engine/yaml-utils.ts Outdated
Comment thread src/formats/rules.ts
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 25, 2026
rhuanbarreto and others added 7 commits July 25, 2026 12:21
readYAML reads a YAML file or a Markdown file with YAML frontmatter,
returning one { frontmatter, content } object: extension-based dispatch
parses .yml/.yaml as a whole document (multi-document --- separators are
never misread as frontmatter) and every other file as an optional leading
frontmatter block plus body text. Values are typed as YamlValue, the
JSON-like data YAML's core schema produces.

checkCase validates a string against a casing scheme (kebab-case,
camelCase, PascalCase, snake_case, SCREAMING_SNAKE_CASE) — pure and
synchronous, with camelCase/PascalCase following the typescript-eslint
naming-convention definition.

Both helpers close the rule-file duplication gap discussed in #490: rule
files are sandboxed and cannot import shared modules, so common helpers
belong on ctx (ARCH-024).

The path-sandbox helpers move unchanged from runner.ts to
src/engine/safe-path.ts to keep runner.ts under the max-lines limit.

Closes #490

Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
Adding readYAML and checkCase showed the RuleContext mirror between
src/formats/rules.ts and the generated ambient shim in
src/helpers/rules-shim.ts is maintained entirely by hand — only the ast()
signature was enforced (single-ast-method). A drifted shim silently hands
rule authors wrong types with no signal.

The new rulecontext-shim-parity rule extracts the interface RuleContext
member names from both surfaces and fails on any member present in one but
missing from the other, in both directions. Extraction is a regex over raw
text rather than ctx.ast(), because Bun.Transpiler erases type-only
interface declarations before the tree is built.

Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…ents

stripJsx's component-tag patterns cannot tell a JSX tag from a TypeScript
generic whose argument is capitalized, and they ran over code as well as
prose. Promise<GrepMatch[]>, Promise<AstNode> and Promise<ReadYamlResult>
were all published to llms-full.txt as a bare Promise, while lowercase
arguments (Promise<string>) survived — so the AI-facing docs advertised the
wrong contract for grep, grepFiles, ast and readYAML.

Stripping now skips both fenced code blocks and inline code spans (the
latter carried the same damage inside reference tables). Prose stripping is
unchanged: JSX components, imports and admonition markers are still removed.

Also address review feedback on the readYAML surface:
- FRONTMATTER_REGEX now anchors both fences to their own line, so '----' or
  '---note' no longer terminate a block and leave stray characters at the
  head of the body. Trailing spaces/tabs are tolerated; end-of-input closing
  is accepted.
- Mirror the CaseScheme degenerate-value note into the generated shim JSDoc,
  the manual parity item rulecontext-shim-parity deliberately does not cover.

Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
Two non-obvious lessons from the readYAML/checkCase work:

- Directory symlinks in tests should use symlinkSync(target, path, "junction").
  The type arg is ignored on POSIX but on Windows creates a junction, which
  needs no admin rights and which lstat reports as a symlink — so the test
  runs everywhere instead of silently skipping, as the pre-existing
  leaf-symlink test had been doing.
- Commit before fire-testing a rule or guard: the mutate/verify/restore loop
  restores with git checkout, which discards all uncommitted work in that
  file, including the fix under test.

Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
FRONTMATTER_REGEX required a newline before the closing fence, so the
degenerate empty block `---\n---` did not match: readYAML returned
frontmatter: null and left both delimiters at the head of content,
contradicting the documented {}-for-empty-frontmatter contract. The body
group is now optional, and the undefined capture is coalesced.

The existing empty-block test passed because it used `---\n\n---`, whose
blank line the body group can capture — a near-empty variant, not the empty
case. Tests now cover the degenerate form directly, plus its end-of-input
and CRLF spellings.

Also address review feedback:
- readYAML's JSDoc described content as unknown in both the source interface
  and the generated shim, left stale when content narrowed to YamlValue.
  Both now state YamlValue and drop the obsolete cast advice.
- Reword the llms-full generator comment to describe current behavior
  instead of narrating what previous output did.

Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
GEN-004 landed in #496 while this branch was open, capping contiguous
comment runs at 5 lines of prose. Ten blocks added here exceeded it.

Each is trimmed to current-behavior essentials, with the rationale moved
behind @see pointers to the ADR, the tests, or the type that carries the
contract — the remedy GEN-004's own fix advice recommends. The rules.ts and
rules-shim.ts JSDoc stay mirrored so ARCH-022's manual parity item holds.

No behavior change; llms-full.txt regenerated for the trimmed prose.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

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

Inline comments:
In `@docs/src/content/docs/reference/rule-api.mdx`:
- Around line 218-234: Update the readYAML documentation to consistently state
that YamlValue content needs no cast: in
docs/src/content/docs/reference/rule-api.mdx:218-234, remove the readJSON-style
cast guidance; re-translate the corrected bullet in
docs/src/content/docs/nb/reference/rule-api.mdx:218-234 and
docs/src/content/docs/pt-br/reference/rule-api.mdx:218-234; then regenerate
docs/public/llms-full.txt:5353-5388 with docs/scripts/generate-llms-full.ts.

In `@src/engine/check-case.ts`:
- Around line 28-34: Update the scheme validation in the case-checking flow to
require scheme ownership via Object.hasOwn(CASE_PATTERNS, scheme) before
indexing CASE_PATTERNS. Treat inherited names such as constructor and toString
as unknown schemes and preserve the existing documented error; only call
pattern.test(value) after the own-property check succeeds.
🪄 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: ac1e26fb-3aed-4558-9a81-29f606ab3e5f

📥 Commits

Reviewing files that changed from the base of the PR and between f4ff986 and ff25b30.

📒 Files selected for processing (18)
  • .archgate/adrs/ARCH-022-ast-aware-rule-context.md
  • .archgate/adrs/ARCH-022-ast-aware-rule-context.rules.ts
  • .claude/agent-memory/archgate-developer/project_oxlint_gotchas.md
  • .claude/agent-memory/archgate-developer/project_rules_engine_internals.md
  • .claude/agent-memory/archgate-developer/project_test_isolation_gotchas.md
  • docs/public/llms-full.txt
  • docs/scripts/generate-llms-full.ts
  • docs/src/content/docs/nb/reference/rule-api.mdx
  • docs/src/content/docs/pt-br/reference/rule-api.mdx
  • docs/src/content/docs/reference/rule-api.mdx
  • src/engine/check-case.ts
  • src/engine/runner.ts
  • src/engine/yaml-utils.ts
  • src/formats/rules.ts
  • src/helpers/rules-shim.ts
  • tests/engine/check-case.test.ts
  • tests/engine/runner-yaml-case.test.ts
  • tests/engine/yaml-utils.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: Smoke Test (Windows) / Windows
  • GitHub Check: Analyze (java-kotlin)
  • GitHub Check: Cloudflare Pages
🧰 Additional context used
📓 Path-based instructions (24)
**/*.{ts,tsx,js,jsx,mjs,cjs}

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

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

Files:

  • tests/engine/check-case.test.ts
  • src/engine/check-case.ts
  • src/engine/yaml-utils.ts
  • docs/scripts/generate-llms-full.ts
  • src/helpers/rules-shim.ts
  • tests/engine/runner-yaml-case.test.ts
  • src/formats/rules.ts
  • src/engine/runner.ts
  • tests/engine/yaml-utils.test.ts
tests/**/*.ts

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

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

tests/**/*.ts: Use Bun's built-in bun:test runner for all tests; do not import test utilities from node:test.
Shared test helpers must also use restoreEnv when restoring environment variables.

Files:

  • tests/engine/check-case.test.ts
  • tests/engine/runner-yaml-case.test.ts
  • tests/engine/yaml-utils.test.ts
{src,tests}/**/*.ts

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

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

Files:

  • tests/engine/check-case.test.ts
  • src/engine/check-case.ts
  • src/engine/yaml-utils.ts
  • src/helpers/rules-shim.ts
  • tests/engine/runner-yaml-case.test.ts
  • src/formats/rules.ts
  • src/engine/runner.ts
  • tests/engine/yaml-utils.test.ts
tests/**/*.test.ts

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

tests/**/*.test.ts: Place tests in tests/ mirroring the src/ directory structure, and name test files with the .test.ts suffix.
Test public module interfaces rather than private implementation details, use descriptive test names, and do not depend on network access.
Use isolated temporary directories created with mkdtemp for filesystem tests, and clean them up in afterEach or afterAll. Do not use hardcoded user or system paths.
Close external SDK instances such as servers, clients, and transports in afterEach or afterAll; manage their lifecycle in test hooks rather than individual test bodies.
When a temporary git repository performs commits, configure local user.email and user.name immediately after git init; do not rely on global git identity.
Every runnable test() or it() must contain at least one expect() assertion. Make implicit no-throw contracts explicit with not.toThrow() or resolves.toBeUndefined(). Use test.skip or test.todo for intentional placeholders.
Use test.skipIf(condition), test.skip, or test.todo for conditional or intentionally disabled tests; do not use bare early returns or empty callbacks to skip tests, and do not skip without a tracking issue.
When adding assertions to an older test file, import expect from bun:test.
Mock HTTP requests by assigning directly to globalThis.fetch, and restore the original fetch implementation or mock in afterEach; do not mock node:fetch.
Mock first-party modules with import * as mod and spyOn(mod, "fn"), restoring spies after each test; do not use process-global mock.module() for first-party modules.
Wrap inline spyOn or mockImplementation usage in try/finally so mockRestore() always runs, or manage spies in beforeEach and afterEach.
When redirecting user-scope paths, mock node:os's homedir() rather than relying on runtime HOME overrides; use environment overrides only for code that reads environment variables directly at call time.
Re...

Files:

  • tests/engine/check-case.test.ts
  • tests/engine/runner-yaml-case.test.ts
  • tests/engine/yaml-utils.test.ts
**

⚙️ CodeRabbit configuration file

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

When reviewing, you must:

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

Files:

  • tests/engine/check-case.test.ts
  • src/engine/check-case.ts
  • src/engine/yaml-utils.ts
  • docs/scripts/generate-llms-full.ts
  • src/helpers/rules-shim.ts
  • tests/engine/runner-yaml-case.test.ts
  • docs/src/content/docs/nb/reference/rule-api.mdx
  • src/formats/rules.ts
  • src/engine/runner.ts
  • tests/engine/yaml-utils.test.ts
  • docs/src/content/docs/reference/rule-api.mdx
  • docs/src/content/docs/pt-br/reference/rule-api.mdx
  • docs/public/llms-full.txt
src/**/*.ts

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-004-no-barrel-files.md)

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

src/**/*.ts: Use await Bun.file(path).json() when reading JSON files in Bun TypeScript source code; do not use JSON.parse(await Bun.file(path).text()) or JSON.parse(fs.readFileSync(path, "utf-8")) for file reads.
Use Bun.JSONC.parse() when reading files that may contain comments, such as tsconfig.json, instead of plain JSON.parse() on file contents.
Reserve JSON.parse() for parsing JSON strings from non-file sources such as API responses or string variables; do not use it as the default for reading JSON files.

src/**/*.ts: In TypeScript source files under src/, use Bun.env instead of process.env for all environment variable reads and writes; process.env must not be used.
In TypeScript source files under src/, use nullish coalescing for environment-variable defaults, e.g. Bun.env.NODE_ENV ?? "production".
In TypeScript source files under src/, use Boolean(Bun.env.CI) for truthy checks on environment flags.
In TypeScript source files under src/, do not destructure Bun.env (for example, const { HOME } = Bun.env); access properties individually instead.
In TypeScript source files under src/, do not reference process.env even in comments that suggest using it.

src/**/*.ts: Heavy dependencies such as inquirer, posthog-node, @sentry/*, and any similarly expensive runtime-only dependency MUST be loaded with dynamic import() at the point of use, never as a static value import at module top level.
Type-only references to heavy modules must use import type (for example, import type { PostHog } from "posthog-node" or import type * as SentryNs from "@sentry/node-core/light"); type-only...

Files:

  • src/engine/check-case.ts
  • src/engine/yaml-utils.ts
  • src/helpers/rules-shim.ts
  • src/formats/rules.ts
  • src/engine/runner.ts
src/**/!(*platform).ts

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

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

Files:

  • src/engine/check-case.ts
  • src/engine/yaml-utils.ts
  • src/helpers/rules-shim.ts
  • src/formats/rules.ts
  • src/engine/runner.ts
src/{helpers,engine}/**/*.ts

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

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

Files:

  • src/engine/check-case.ts
  • src/engine/yaml-utils.ts
  • src/helpers/rules-shim.ts
  • src/engine/runner.ts
src/engine/**/*.ts

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

src/engine/**/*.ts: In the rules engine, list project files by matching glob patterns in memory against the git-tracked file set rather than walking the filesystem.
Route new engine file listings through listMatchingFiles for rule-facing inputs or matchTrackedFiles for trusted ADR frontmatter patterns.
When the target is a Git repository and respectGitignore is not false, pass the tracked set from getGitTrackedFiles to matching operations.
Per-run RunCaches must share glob results keyed by pattern and tracked mode and file text keyed by absolute path; cached promises should share in-flight work, and returned glob arrays must be copied before exposing them to rules.
Do not cache readJSON results because rules receive mutable objects whose shared mutation could leak between rules.
Do not filter filesystem scan results against the tracked set as a substitute for in-memory matching, and do not hardcode ignore directories; use the Git-derived tracked set instead.

Do not add tree-sitter, web-tree-sitter, native parser bindings, WASM grammars, or other new production dependencies for this AST feature.

Files:

  • src/engine/check-case.ts
  • src/engine/yaml-utils.ts
  • src/engine/runner.ts
src/helpers/rules-shim.ts

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

Mirror every RuleContext surface change, including ast, fileAtBase, options, ambient types, signatures, and JSDoc, in the generated rules shim.

Files:

  • src/helpers/rules-shim.ts
docs/src/content/docs/**/*.mdx

📄 CodeRabbit inference engine (.archgate/adrs/GEN-001-documentation-site.md)

docs/src/content/docs/**/*.mdx: Use MDX (.mdx) for all content pages under docs/src/content/docs/.
Escape literal curly braces in MDX when showing template syntax; do not use bare {} in prose or code-fence labels.

Files:

  • docs/src/content/docs/nb/reference/rule-api.mdx
  • docs/src/content/docs/reference/rule-api.mdx
  • docs/src/content/docs/pt-br/reference/rule-api.mdx
{docs/src/content/docs/**/*.mdx,docs/astro.config.mjs}

📄 CodeRabbit inference engine (.archgate/adrs/GEN-001-documentation-site.md)

When adding a new documentation page, create the MDX file in docs/src/content/docs/<category>/<slug>.mdx and add the page to docs/astro.config.mjs sidebar configuration.

Files:

  • docs/src/content/docs/nb/reference/rule-api.mdx
  • docs/src/content/docs/reference/rule-api.mdx
  • docs/src/content/docs/pt-br/reference/rule-api.mdx
docs/src/content/docs/**

📄 CodeRabbit inference engine (.archgate/adrs/GEN-001-documentation-site.md)

Do not create documentation content files outside docs/src/content/docs/; Starlight content must live in that exact directory structure.

Files:

  • docs/src/content/docs/nb/reference/rule-api.mdx
  • docs/src/content/docs/reference/rule-api.mdx
  • docs/src/content/docs/pt-br/reference/rule-api.mdx
docs/src/content/docs/**/*.{mdx,md}

📄 CodeRabbit inference engine (.archgate/adrs/GEN-002-docs-i18n.md)

docs/src/content/docs/**/*.{mdx,md}: For every English docs page under docs/src/content/docs/, create a translated file in each locale directory with the exact same relative path and filename, and ensure every locale file corresponds to an existing root file (no orphan translations).
When adding or modifying English documentation content, update the corresponding locale files in the same pull request.
Translate all user-facing prose in docs pages, including titles, descriptions, headings, paragraphs, list items, table text, and admonition content.
Translate user-visible text props in Starlight components such as <Card title="..."> and <LinkCard description="...">.
Keep code blocks, CLI commands, file paths, TypeScript identifiers, technical terms, import statements, component names, and link/href/slug attribute values in English.
Keep internal links unchanged and do not add locale prefixes (for example, use /guides/... rather than /pt-br/guides/...).
Preserve MDX curly-brace escaping (\{\}) in translated content.
Preserve Starlight component import statements identically in translated files.

Files:

  • docs/src/content/docs/nb/reference/rule-api.mdx
  • docs/src/content/docs/reference/rule-api.mdx
  • docs/src/content/docs/pt-br/reference/rule-api.mdx
docs/src/content/docs/nb/**/*.{mdx,md}

📄 CodeRabbit inference engine (.archgate/adrs/GEN-002-docs-i18n.md)

Use Norwegian Bokmål (not Nynorsk) for Norwegian translations, with the informal du form and correct Norwegian characters (æ, ø, å).

Files:

  • docs/src/content/docs/nb/reference/rule-api.mdx
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/rules.ts
src/formats/rules.ts

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

Expose exactly one RuleContext.ast(path, language, opts?) method, with language dispatch hidden from rule authors; document that returned AST shapes differ by language.

Files:

  • src/formats/rules.ts
src/engine/{runner.ts,ast-support.ts,js-parser.ts}

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

Implement ctx.ast() internally in createRuleContext() with exactly this guardrail order before subprocess execution: path safety, language plausibility, interpreter availability, and guarded invocation.

Files:

  • src/engine/runner.ts
src/engine/{runner.ts,ast-support.ts}

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

src/engine/{runner.ts,ast-support.ts}: Use the existing meriyah parser for TypeScript and JavaScript without spawning a subprocess, and factor parsing into a shared helper reused by rule-scanner.ts and ctx.ast().
Use only Bun.spawn with array-based arguments for Python/Ruby AST subprocesses; do not use shell interpolation, Bun.$, or raw subprocess primitives exposed to rules.
Run Python AST parsing with python -I -c ... isolation so the target project's working directory cannot shadow standard-library modules.
Probe Python/Ruby interpreter availability using platform-appropriate candidates, cache the result once per check invocation, and use the resolved executable for the real invocation.
Validate the requested file's extension and/or leading content against the requested AST language before invoking Python or Ruby.
ctx.ast() must throw on missing interpreters, parse failures, missing base revisions, and files absent at the base revision; errors must distinguish these causes in their messages and must never return null.
Preserve language-native AST shapes: ESTree for TypeScript/JavaScript, Python's standard ast schema, and Ruby's native Ripper s-expressions; do not normalize them into a common shape.
Strip a leading UTF-8 BOM before Python and Ruby parsing using UTF-8-signature-aware file reads.
Do not trust TypeScript AST node.loc positions because parsing uses transpiled source; relocate reported constructs against the original source. JavaScript locations are source-accurate.

Files:

  • src/engine/runner.ts
src/engine/{runner.ts,safe-path.ts}

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

Apply safePath() and reject symlinks in every path component below the project root before reading or parsing an AST source file.

Files:

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

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

Implement { rev: "base" } AST reads and fileAtBase() using the merge base of --base and HEAD, matching the three-dot changedFiles comparison.

Files:

  • src/engine/runner.ts
src/engine/{ast-support.ts,runner.ts}

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

For base Python/Ruby content, use a securely created private temporary directory and exclusive file creation, then pass the source through the same isolated AST subprocess programs.

Files:

  • src/engine/runner.ts
docs/src/content/docs/reference/**/*.mdx

📄 CodeRabbit inference engine (.archgate/adrs/GEN-001-documentation-site.md)

Keep reference pages accurate to the CLI source code; when CLI APIs change, update the corresponding reference docs in the same PR.

Files:

  • docs/src/content/docs/reference/rule-api.mdx
docs/src/content/docs/pt-br/**/*.{mdx,md}

📄 CodeRabbit inference engine (.archgate/adrs/GEN-002-docs-i18n.md)

Use correct Portuguese diacritical marks in Brazilian Portuguese translations; never write unaccented Portuguese.

Files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • .claude/agent-memory/archgate-developer/project_rules_engine_internals.md
  • .claude/agent-memory/archgate-developer/project_oxlint_gotchas.md
  • .claude/agent-memory/archgate-developer/project_test_isolation_gotchas.md
📚 Learning: 2026-07-25T10:10:28.469Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 497
File: docs/scripts/generate-llms-full.ts:0-0
Timestamp: 2026-07-25T10:10:28.469Z
Learning: In the Archgate CLI repo, ensure comments and documentation in docs TypeScript sources use concise, forward-only, version-independent wording: describe the current behavior and its consequences (what happens now and why) rather than historical context such as prior bugs, previous/old outputs, or state-transfer narratives. Even when documenting a newly fixed bug, write the entry as a present-tense description of the behavior and impact rather than a “before/after” or history-based explanation (e.g., avoid historical-style explanations alongside comment tags like `stripJsxOutsideCode`).

Applied to files:

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

Applied to files:

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

Applied to files:

  • .archgate/adrs/ARCH-022-ast-aware-rule-context.md
🪛 LanguageTool
docs/src/content/docs/pt-br/reference/rule-api.mdx

[uncategorized] ~218-~218: Pontuação duplicada
Context: ...bjeto de resultado cobre as duas formas -- um mapeamento frontmatter anulável e ...

(DOUBLE_PUNCTUATION_XML)


[uncategorized] ~220-~220: Pontuação duplicada
Context: ...re null; content é o valor parseado -- faça o cast para o tipo esperado na sua...

(DOUBLE_PUNCTUATION_XML)


[uncategorized] ~220-~220: Encontrada possível ausência de vírgula.
Context: ...res --- de um stream multi-documento (manifestos Kubernetes, configurações de CI) nunca ...

(AI_PT_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~221-~221: Esta conjunção deve ser separada por vírgulas e só deve ser utilizada no início duma frase para efeitos de estilo.
Context: ..., espelhando fileAtBase), {} quando existe mas está vazio. content é o restante do c...

(VERB_COMMA_CONJUNCTION)


[uncategorized] ~221-~221: Pontuação duplicada
Context: ... do texto, sem espaços nas extremidades -- ele não é parseado como YAML. `rea...

(DOUBLE_PUNCTUATION_XML)


[uncategorized] ~404-~404: Pontuação duplicada
Context: ...tring segue um esquema de capitalização -- a substituição integrada para os regexe...

(DOUBLE_PUNCTUATION_XML)


[uncategorized] ~404-~404: Pontuação duplicada
Context: ...cisavam escrever à mão. Síncrono e puro -- não precisa de await. A correspondênc...

(DOUBLE_PUNCTUATION_XML)

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

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

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

🔇 Additional comments (18)
tests/engine/check-case.test.ts (1)

1-93: LGTM!

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

1-148: LGTM!

tests/engine/runner-yaml-case.test.ts (1)

1-169: LGTM!

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

15-15: LGTM!

Also applies to: 17-18

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

8-9: LGTM!

Also applies to: 21-24

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

15-15: LGTM!

src/engine/check-case.ts (1)

1-3: LGTM!

Also applies to: 5-18, 20-26

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

1-23: LGTM!

Also applies to: 25-49, 51-88

src/engine/runner.ts (1)

12-12: LGTM!

Also applies to: 39-39, 53-53, 143-144, 191-193, 242-245, 280-284, 350-353, 366-375, 377-383, 401-405

src/formats/rules.ts (1)

58-99: LGTM!

Also applies to: 262-273, 325-332

src/helpers/rules-shim.ts (1)

72-110: LGTM!

Also applies to: 267-278, 330-337

docs/src/content/docs/reference/rule-api.mdx (1)

69-72: LGTM!

Also applies to: 391-427

docs/src/content/docs/nb/reference/rule-api.mdx (1)

69-72: LGTM!

Also applies to: 391-427

docs/src/content/docs/pt-br/reference/rule-api.mdx (1)

69-72: LGTM!

Also applies to: 391-427

docs/public/llms-full.txt (1)

870-871: LGTM!

Also applies to: 3333-3333, 3347-3347, 3392-3392, 5218-5229, 5282-5282, 5294-5294, 5544-5580, 5743-5743, 6117-6117, 6325-6325, 6713-6713, 6819-6829, 6926-6927, 7022-7022, 7255-7255, 7268-7268, 7368-7388, 7493-7493, 7568-7568, 7582-7582, 7595-7595, 7695-7695, 7906-7906

docs/scripts/generate-llms-full.ts (1)

65-87: LGTM!

Also applies to: 120-120, 153-153

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

23-41: LGTM!

Also applies to: 221-264

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

97-97: LGTM!

Also applies to: 152-158

Comment thread docs/src/content/docs/reference/rule-api.mdx
Comment thread src/engine/check-case.ts Outdated
checkCase guarded with a truthiness test on the looked-up pattern, so a
scheme name inherited from Object.prototype — "constructor", "toString",
"__proto__" — resolved to a function, passed the guard, and threw
TypeError: pattern.test is not a function instead of the documented
unknown-scheme error. Rule files can reach this by building a scheme name
dynamically, which is exactly the case the throw exists to report clearly.
Object.hasOwn now gates the lookup.

Also fix the readYAML docs contradicting themselves on casting: the
.yml/.yaml bullet still said to cast "as with readJSON" while the example
below it narrowed the same value with a typeof check and a "no cast needed"
comment. readJSON returns unknown and does need a cast; readYAML returns the
narrower YamlValue and does not. Reworded in all three locales, with
llms-full.txt regenerated.

Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
@rhuanbarreto
rhuanbarreto merged commit c5d82c5 into main Jul 25, 2026
23 checks passed
@rhuanbarreto
rhuanbarreto deleted the feat/ctx-read-yaml-check-case branch July 25, 2026 19:50
rhuanbarreto pushed a commit that referenced this pull request Jul 26, 2026
# archgate

## [0.51.0](v0.50.0...v0.51.0)
(2026-07-26)

### Features

* **adrs:** enforce concise, forward-only code comments (GEN-004)
([#496](#496))
([9a114b3](9a114b3)),
references [#2123](https://github.com/archgate/cli/issues/2123)
* **adrs:** flag stray files at the repository root (GEN-005)
([#535](#535))
([6a6e765](6a6e765)),
closes [#514](#514), references
[#500](#500)
* **engine:** add ctx.readYAML and ctx.checkCase rule helpers
([#497](#497))
([c5d82c5](c5d82c5)),
closes [#490](#490), references
[#490](#490)
[#491](#491)
[#499](#499)
[#499](#499)
[#499](#499)
[#499](#499)
* report truncated ADR briefings, trim the ADR corpus 15.6%, add GEN-005
briefing budget ([#501](#501))
([a9dab40](a9dab40))

### Bug Fixes

* **docs:** relocate ADR content to clear briefing-budget warnings
([#531](#531))
([c7419b3](c7419b3))
* **docs:** restore pt-br diacritics and enforce locale content
integrity ([#523](#523))
([db39104](db39104)),
closes [#516](#516), references
[#231](#231)
* **engine:** allow symlinks that resolve inside the project root
([#500](#500))
([387bf15](387bf15))
* **engine:** reject rule-file reads through a symlinked ancestor
directory ([#499](#499))
([a555f9d](a555f9d)),
references [#497](#497)
[#491](#491)
[#497](#497)
[#497](#497)
[#497](#497)
[#497](#497)
* **engine:** scan top-level export declarations with a null source
([#493](#493))
([d07db03](d07db03)),
closes [#491](#491)
* **engine:** stop dropping AST nodes with exotic literal values
([#494](#494))
([0015542](0015542)),
closes [#493](#493)
[#493](#493)
* **lint:** resolve no-bare-env-restore by captured key and lexical
scope ([#524](#524))
([7094a3a](7094a3a)),
closes [#498](#498)
* **rules:** make ARCH-020 and ARCH-023 match ctx.ast() instead of raw
text ([#533](#533))
([ad5529b](ad5529b)),
closes [#513](#513), references
[#486](#486)
* **tests:** replace bun:test anti-patterns with idiomatic patterns
([#512](#512))
([bcb086f](bcb086f))

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

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



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

### Command Format

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

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

### Useful Parameters

#### Bump

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

#### Publish

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

### Usage Examples

#### Force specific version

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

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

#### Force major bump

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

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

#### Create alpha pre-release

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

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

#### Publish with specific access and tag

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

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

### Custom Changelog Preamble

You can add custom markdown to the top of the changelog (right after the
version header) using the `!simple-release/set-preamble` command. The
markdown after the command line becomes the preamble.

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

## What's new?

- The website was completely redesigned
- The new API gives you awesome possibilities
```

In a monorepo, pass the full package name after the command to target a
single package's changelog. Wrap the name in backticks so GitHub keeps
it as text instead of a mention:

```md
!simple-release/set-preamble `@your-org/core`

## Core changes

- New plugin system
```

Use one comment per package, plus one without a name for the whole
release.

### Access Restrictions

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

### Notes

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


</details>

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

Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: opt-in shared helper modules for rule files, contained within .archgate/

1 participant