Skip to content

fix: bound scan loop, narrow install-method types, and extend ARCH-021 to markdown code spans - #548

Merged
rhuanbarreto merged 7 commits into
mainfrom
rhuanbarreto/verify-issues-541-540-515-79ebca
Aug 5, 2026
Merged

fix: bound scan loop, narrow install-method types, and extend ARCH-021 to markdown code spans#548
rhuanbarreto merged 7 commits into
mainfrom
rhuanbarreto/verify-issues-541-540-515-79ebca

Conversation

@rhuanbarreto

@rhuanbarreto rhuanbarreto commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Closes #541, closes #540, closes #515.

Each issue was re-verified against current main before implementing. Two held up as written, one held up in substance but not in its stated mechanism — details below.


#541detectInstallMethod's empty-string cache check

Verdict: valid. Every assignment in the function body sets one of four literals and none is "", so the !== "" half of the cache-hit guard has no falsifying path. The three call sites (telemetry.ts, doctor.ts, sentry.ts) each widen the result into a Record<string, unknown>, so nothing depended on the string return type.

Both the cache and the return type are now the InstallMethod union, and the redundant comparison is gone.

The issue's third bullet asked whether typescript/no-unnecessary-condition already flags this. It does not — before or after narrowing. Restoring the guard on top of the narrowed type and running bun run lint passes clean, while a string !== undefined control placed in the same function is flagged at once. So type-aware lint would not have caught this, and removing the guard on the strength of a clean lint run would not have been sound reasoning. The type is the enforcement here.

InstallMethod is deliberately distinct from upgrade.ts's same-named local type: that one is a discriminated union carrying the command needed to perform an upgrade, and its fourth member is package-manager rather than global-pm. They are not interchangeable and are not unified.

#540 — unbounded for(;;) scan loop in findCodeOccurrences

Verdict: valid, and there is a concrete case the issue did not identify.

The issue's reasoning about the current shape is correct: idx = found + 1 against a fixed-length string bounds the loop. The rewrite moves the advance into the for statement's increment slot, so no path through the body can skip it and termination is structural rather than a separate argument about the body.

While verifying, one input was found that genuinely does not terminate: an empty needle. String.prototype.indexOf("", n) clamps to source.length rather than returning -1, so the cursor stops advancing and the loop spins forever. Demonstrated on the pre-change shape:

needle 'Bun.spawn': 2 iterations
needle ''         : did not terminate
"const x = 1;\nBun.spawn([]);".indexOf("", 99) === 27   // not -1

This is latent, not reachable today. Every searchText producer in rule-scanner.ts is non-empty by construction: fixed anchors (import(, import.meta, .constructor), from "<specifier>" (minimum seven characters), and identifier names. It is guarded rather than left to chance, since an anchorless violation has no position to map to and the caller already falls back to line 0.

An explicit iteration cap was considered and not used — it bounds the symptom, whereas the guard plus the increment-slot advance makes termination provable from the loop's own shape.

Two regression tests added: the empty needle, and a needle overlapping at every offset.

#515 — escaped backticks nested in markdown inline code spans

Verdict: the invariant is valid and there was live corruption in the repository — but the issue's stated mechanism is stale, and its proposed detection algorithm cannot work as written.

Three corrections to the issue:

  1. oxfmt no longer reproduces the corruption. On the pinned 0.60.0, oxfmt --write over several constructed repros (including the exact shape the issue quotes, reproduced in the fenced block below) is byte-identical in and out.

    `UserError("... Run \`archgate init\` first.")`
    

Pinning the rule's justification to formatter behaviour would leave it unenforced on every version where the bug is absent.

  1. The real justification is tool-independent and permanent. CommonMark gives backslash escapes no meaning inside a code span (§6.1), and a span ends at the next backtick run of equal length (§6.3). So the "escaped" backtick still closes the span, the backslash renders as itself, and every later delimiter on the line re-pairs one position out. This is wrong on every renderer, with or without a formatter in the loop.

  2. The detection algorithm proposed in the issue — "flag a \` sequence occurring between the delimiters" — can never match. The escaped backtick is the closing delimiter; that is precisely the bug. A rule looking strictly between an opener and a closer finds nothing. The rule implemented here inverts it: walk and skip whole code spans, and flag a backslash-before-backtick found in the text between them.

That inversion is also what makes it precise. A span whose content legitimately ends in a backslash — a Windows path such as the .config\opencode\agents\ examples in the opencode integration guide — has its backslash inside the span and is never reported.

Live corruption found and repaired:

File Lines Shape
ARCH-010 14 every code span escaped, so the whole ADR rendered literal backslashes on the docs site and in every agent briefing
ARCH-007 2 the nested-escape shape the issue describes

Both are repaired using the CommonMark idiom the rule recommends: a longer delimiter, with symmetric padding when the content itself ends in a backtick.

The rule lives in ARCH-021, which is generalized rather than duplicated. The escaped-backtick check and the existing ASCII-only PowerShell check enforce one invariant — authored text must not carry a sequence a downstream reader decodes differently from the way it was written — and both are lexical line scans for a corruption format:check, oxlint, and tsc are all structurally unable to see. ARCH-021 is retitled Authored Text Integrity, widened to files: ["**/*.ps1", "**/*.md", "**/*.mdx"], and now carries two rules:

Rule Scope
ARCH-021/ascii-only-ps1 .ps1 (unchanged)
ARCH-021/no-escaped-backtick-in-markdown .md, .mdx (new)

A standalone ADR bought nothing: rules are discovered through ADRs, so a second ADR was the price of a second rule, not of a second decision. Verified — with the ADR removed and only the .rules.ts on disk, the loader drops from 51 rules to 50, the rule never runs, and a real violation passes silently.

CHANGELOG.md is exempt — regenerated from commit messages on every release, matching the ignorePatterns entry it already carries in .oxfmtrc.json. That exemption is a stated limitation in the ADR, not a silent carve-out.


Verification

Fire-tested in both directions rather than relying on a green suite:

  • Blocks: injecting the broken shape into a .md (ARCH-010) and a .mdx (opencode guide) each produce exactly one violation, at the column of the backslash to delete.
  • Permits: the same .mdx holds four Windows paths ending in a backslash inside code spans and stays clean. A probe using that identical single-escape shape was correctly not reported.
  • Nested fences: a four-backtick fence containing a three-backtick run, and a tilde fence containing a backtick run, are both skipped. Fences pair by marker and run length as CommonMark defines them rather than toggling on any fence-like line — review caught that the toggle form invented a violation inside a fenced example, and the fix is in 526de1a.
  • Corpus: 244 markdown files, 16 genuine violations before repair, 0 after, no false positives.
  • No regression on the rule it now shares a file with: a non-ASCII character injected into a .ps1 still trips ascii-only-ps1, unchanged.
  • Sandbox (ARCH-024): adding a node:fs import to the rule file is rejected by the rule scanner, confirming the boundary is enforced on it.

bun run validate passes from a fresh clone of this branch (exit 0) — the companion .rules.ts reaches its types through the gitignored, generated .archgate/rules.d.ts, so a long-lived working directory would mask a generation-ordering fault that CI would hit immediately.

archgate check: 51/51, no briefing-budget warnings. ARCH-021's two briefed sections measure 1686 and 1554 characters against the 2000 cap, so widening the ADR did not push its agent briefing into truncation.

No repo ADR companion rule carries a bespoke unit test — archgate check over the real corpus during validate is the test — so this follows that convention rather than introducing a new one.

The scan loop advanced its cursor from inside the body, so termination
depended on every path through the body remembering to advance it. The
advance now lives in the for-statement's increment slot, where no
`continue` can skip it, and `indexOf` from `found + 1` returns either
-1 or a strictly greater index — bounding the scan to source.length
iterations by construction.

An empty needle is the one input that never terminated: `indexOf("", n)`
clamps to source.length instead of returning -1, so the cursor stopped
advancing. Every caller anchors on a non-empty token today, so this was
latent rather than reachable; it is now guarded, since no anchor means
no position and the caller already falls back to line 0.

Closes #540

Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
The cache and the return type were both plain `string`, which discarded
the four values the function can actually produce and left a `!== ""`
cache-hit guard that no code path could falsify. Typing both as the
`InstallMethod` union restores that information for every caller and
makes the redundant comparison unwritable rather than merely unreachable.

The type is the enforcement here, not the linter: with `typeAware` on,
`no-unnecessary-condition` flags a `string !== undefined` control in the
same function but does not flag the narrowed union against `""`, so
removing the guard on the strength of a clean lint run would not have
been sound.

`InstallMethod` is distinct from upgrade.ts's same-named type, which
carries the command needed to perform an upgrade; this one only names
the source.

Closes #541

Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
CommonMark gives backslash escapes no meaning inside an inline code
span, so a backslash written to keep a backtick inside a span does the
opposite: the backtick closes the span and every later delimiter on the
line re-pairs one position out. The result is valid markdown that
renders wrongly, which format:check cannot see because the file is
already in normal form.

GEN-006 adds a companion rule that walks each line over its code-span
delimiters and reports a backslash-escaped backtick found in text
content. Walking spans rather than grepping for the byte pair is what
lets it pass a span whose content legitimately ends in a backslash,
such as a Windows path — a shape this repository's docs use for Windows
paths on four lines.

ARCH-007 and ARCH-010 are brought in line with the rule.

Closes #515

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

coderabbitai Bot commented Aug 5, 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: 37 minutes

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

How can I continue?

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

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

How do review limits work?

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

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

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 97335415-7aa7-4f07-9837-c05c79d7b178

📥 Commits

Reviewing files that changed from the base of the PR and between d2dda22 and 4b46ab3.

📒 Files selected for processing (2)
  • .archgate/adrs/ARCH-021-authored-text-integrity.md
  • .archgate/adrs/ARCH-021-authored-text-integrity.rules.ts
📝 Walkthrough

Walkthrough

This change adds ARCH-021 authored-text integrity guidance and a new Archgate rule set that checks ASCII-only PowerShell files and escaped backticks in Markdown and MDX text. It removes escaped inline-code formatting from ARCH-007 and ARCH-010 examples. It updates findCodeOccurrences to return early for empty search text and to advance occurrence searches in the loop structure, with regression tests for empty and overlapping matches. It narrows detectInstallMethod and its cache to an InstallMethod string-literal union. It also adds two agent-memory notes.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning Issues #541 and #540 are addressed, but #515 remains non-compliant because firstEscapeColumn() can miss a delimiter case. Fix firstEscapeColumn() to detect an escaped backtick that is also the next equal-length delimiter, and add a regression test.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the loop, install-method type, and ARCH-021 changes.
Description check ✅ Passed The description directly explains the implementation, issue coverage, corrections, and verification for the changeset.
Out of Scope Changes check ✅ Passed The implementation and documentation changes support the three linked objectives, with no unrelated code changes identified.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch

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.

Two facts no check can reach: a bun -e fixture loses one level of
escaping on the way into the file, which turns a fire-test into a
false negative that reads as a broken rule; and no-unnecessary-condition
does not flag a narrowed literal union against a literal outside it, so
a clean lint run is not evidence about a dead comparison either way.

Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
@cloudflare-workers-and-pages

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

Copy link
Copy Markdown

Deploying archgate-cli with  Cloudflare Pages  Cloudflare Pages

Latest commit: 4b46ab3
Status: ✅  Deploy successful!
Preview URL: https://19d59926.archgate-cli.pages.dev
Branch Preview URL: https://rhuanbarreto-verify-issues-5.archgate-cli.pages.dev

View logs

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Code Coverage

Metric Value
Lines 91.3% (8896 / 9747)
Threshold 90% minimum — met
Platforms Linux + Windows

Full HTML report available in workflow artifacts.

Per-directory breakdown
Directory Coverage Lines
src/commands/ 89.1% 2099 / 2357
src/engine/ 94.0% 2438 / 2593
src/formats/ 98.7% 149 / 151
src/helpers/ 90.6% 4210 / 4646

@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 @.archgate/adrs/GEN-006-markdown-code-span-integrity.rules.ts:
- Around line 77-79: GEN-006 requires the fence scanner around FENCE.test to
track the opening marker and length, then close only on the same marker with at
least that length and only spaces or tabs afterward; add regression cases for
shorter, opposite-marker, and text-suffixed runs. In
.archgate/adrs/GEN-006-markdown-code-span-integrity.rules.ts lines 77-79,
implement the stateful matching-fence logic. In
.archgate/adrs/GEN-006-markdown-code-span-integrity.md lines 77-78, retain the
“only suppress reports” guarantee only after matching closing-fence detection is
enforced.

In `@src/helpers/install-info.ts`:
- Around line 25-35: Restore detectInstallMethod() to return the installation
metadata object expected by the upgrade command, including type, protoCmd, cmd,
args, and manualHint; narrow only the type field to InstallMethod. Ensure its
cached value uses the same object shape and remains compatible with the
method.type-based dispatch in the upgrade flow.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 74adfc5c-3546-400c-9e9e-3ae3e0008975

📥 Commits

Reviewing files that changed from the base of the PR and between 2dd629e and 900273b.

📒 Files selected for processing (7)
  • .archgate/adrs/ARCH-007-cross-platform-subprocess-execution.md
  • .archgate/adrs/ARCH-010-prefer-bun-built-in-json-parsing.md
  • .archgate/adrs/GEN-006-markdown-code-span-integrity.md
  • .archgate/adrs/GEN-006-markdown-code-span-integrity.rules.ts
  • src/engine/source-positions.ts
  • src/helpers/install-info.ts
  • tests/engine/source-positions.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 (15)
.archgate/adrs/**/*.{md,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

Read relevant self-governance ADRs before architectural changes; ADRs use YAML frontmatter and companion .rules.ts files exporting a plain object satisfying RuleSet.

Files:

  • .archgate/adrs/ARCH-007-cross-platform-subprocess-execution.md
  • .archgate/adrs/GEN-006-markdown-code-span-integrity.rules.ts
  • .archgate/adrs/GEN-006-markdown-code-span-integrity.md
  • .archgate/adrs/ARCH-010-prefer-bun-built-in-json-parsing.md
.archgate/adrs/**/*.rules.ts

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

.archgate/adrs/**/*.rules.ts: Comments in .archgate/adrs/**/*.rules.ts must be concise, forward-only, and limited to current behavior; historical or relocation narration is prohibited.
Changes to narration or relocation detection patterns in companion .rules.ts files must be synchronized with .archgate/lint/oxlint.ts, and both enforcement layers must continue to report violations at error severity.

Files:

  • .archgate/adrs/GEN-006-markdown-code-span-integrity.rules.ts
.archgate/{lint,adrs}/**/*.ts

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

.archgate/{lint,adrs}/**/*.ts: A contiguous run of whole-line comments must contain at most five lines of narrative prose, including in lint and companion rule implementations.
Use the same synchronized structural-TSDoc exemption in Archgate TypeScript files; narrative must not be relabeled with prose-container tags to evade the limit.

Files:

  • .archgate/adrs/GEN-006-markdown-code-span-integrity.rules.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/helpers/install-info.ts
  • src/engine/source-positions.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/helpers/install-info.ts
  • src/engine/source-positions.ts
  • tests/engine/source-positions.test.ts
**/*.{ts,tsx}

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

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

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

Files:

  • src/helpers/install-info.ts
  • src/engine/source-positions.ts
  • tests/engine/source-positions.test.ts
src/**/*.ts

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

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

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

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

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

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

Files:

  • src/helpers/install-info.ts
  • src/engine/source-positions.ts
{src,tests,lint,scripts,shims}/**/*.ts

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

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

Files:

  • src/helpers/install-info.ts
  • src/engine/source-positions.ts
  • tests/engine/source-positions.test.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

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

Files:

  • src/helpers/install-info.ts
  • src/engine/source-positions.ts
**/*.{js,ts,tsx,mjs,cjs}

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

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

Files:

  • src/helpers/install-info.ts
  • src/engine/source-positions.ts
  • tests/engine/source-positions.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:

  • src/helpers/install-info.ts
  • src/engine/source-positions.ts
  • tests/engine/source-positions.test.ts
src/engine/**/*.ts

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

src/engine/**/*.ts: The rules engine must list files by matching globs in memory against the git-tracked file set, rather than walking the filesystem.
runChecks must share per-run caches across rule contexts: cache glob results by pattern and tracked mode, cache file text by absolute path using promises, copy cached glob arrays before returning them, and do not cache mutable readJSON results.
Do not filter filesystem scan results against the tracked set as a substitute for in-memory matching, and do not hardcode ignore directories; Git's ignore-aware file listing is authoritative.

src/engine/**/*.ts: TS/JS AST parsing MUST reuse the shared in-process meriyah parser primitive; scanner parsing and ctx.ast() parsing must not duplicate inline parseModule() implementations.
Python and Ruby AST parsing MUST use their standard-library AST facilities through guarded Bun.spawn subprocesses, without third-party parsers.
Within createRuleContext(), AST guardrails MUST run in order: path safety, language plausibility, interpreter availability probe, then guarded subprocess invocation. The probe must be cached per check invocation.
Rule execution MUST NOT directly access Bun.spawn, child_process, or other subprocess/filesystem primitives; ctx.ast() is the sole sanctioned door for AST subprocess work.
Python subprocesses MUST use the -I isolation flag; subprocess arguments MUST be array-based, with no shell interpolation of paths or source contents.
ctx.ast() MUST throw on unavailable interpreters, parse failures, missing base revisions, or files absent at base; it must never return null or another sentinel. fileAtBase() is the only exception.
ast(path, language, { rev: "base" }) and fileAtBase(path) MUST read the same merge-base commit used by changedFiles; git operations remain in src/engine/git-files.ts.
ast(..., { comments: true }) MUST opt in to a root comments array of structured CommentToken values; comments must not appear ...

Files:

  • src/engine/source-positions.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 use Jest, Vitest, or custom assertions.
Mirror the src/ directory structure in tests/, and name test files <module-name>.test.ts.
Use mkdtemp for filesystem-test isolation, keep writes inside the temporary directory, and clean up temporary resources in afterEach or afterAll.
Test each module's public interface with descriptive names; do not test private internals.
Every runnable test must contain an expect() assertion; use test.skip or test.todo for placeholders and do not leave assertion-less or silently skipped tests.
Restore every captured environment variable with restoreEnv(key, original) rather than assigning the captured value directly.
Mock os.homedir() via an imported module namespace and spyOn; do not override HOME to control home-directory resolution. Environment overrides are valid only for code that reads Bun.env at call time.
Mock first-party modules with import * as mod plus spyOn, restore them with mock.restore(), and never use mock.module() or an -impl production split for first-party modules.
For HTTP mocking, save globalThis.fetch before replacing it and restore the direct assignment in afterEach; do not use mock.module("node:fetch").
Tests must not hit the network or touch real user-scope paths or other real state.
Wrap inline spyOn or mockImplementation lifecycles in try/finally, or manage them in hooks, so mockRestore() always executes.
Close external SDK instances, servers, clients, and transports in afterEach or afterAll, not in test bodies.
Configure git user.email and user.name locally after git init and before committing in temporary repositories; never rely on global Git identity.
Inject small threshold values into threshold tests instead of generating thousands of file...

Files:

  • tests/engine/source-positions.test.ts
tests/**/*.test.ts

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

tests/**/*.test.ts: Use test.each() for the same assertion logic against multiple independent inputs, and describe.each() when each input requires a group of related tests. Do not register tests or run independent assertions inside for/.forEach loops.
Use array rows for positional test.each() arguments and object rows for named fields, with descriptive title placeholders such as %s, %p, %d, or $field.
Assert derived facts with specific matchers rather than collapsing booleans into .toBe(true) or .toBe(false): compare values directly with .toBe()/.toEqual(), use .toContain() or .toMatch() for membership and substrings, .toBeInstanceOf(Array) for array checks, .toHaveLength() for counts, and .find() with .toBeDefined()/.toBeUndefined() for predicate existence checks.
Do not precompute a boolean solely for assertion; assert directly on the underlying values so failures expose the expected and received values.
When converting a loop to test.each() or describe.each(), preserve every assertion that ran per iteration; do not drop or merge assertions across cases.

Files:

  • tests/engine/source-positions.test.ts
tests/engine/**/*.test.ts

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

Test AST base-revision parsing, comment extraction and opt-in behavior, cross-language handling, comment-only structural equivalence, character-offset conversion, and throw-versus-null semantics.

Files:

  • tests/engine/source-positions.test.ts
🧠 Learnings (15)
📓 Common learnings
Learnt from: CR
Repo: archgate/cli

Timestamp: 2026-08-05T10:57:30.662Z
Learning: Reviewers must interpret mixed prose and backticks according to CommonMark delimiter pairing rather than the author's apparent intent, and treat escaped-backtick violations as rendering defects.
📚 Learning: 2026-07-11T13:03:15.386Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 467
File: .archgate/adrs/ARCH-011-consistent-project-root-resolution.md:0-0
Timestamp: 2026-07-11T13:03:15.386Z
Learning: For Markdown files formatted by oxfmt (especially ADRs), avoid inline code spans that contain escaped backticks, e.g. `\`...\`` inside a single `` `...` `` span. oxfmt may mis-parse these and, on re-format, can collapse spaces after later inline code spans on the same line, effectively removing any manually re-added spacing. Instead, rephrase the text so the message stays plain quoted text, and put any embedded command/fragment that needs code formatting (e.g., `archgate init`) in its own separate inline code span; keep surrounding punctuation/spacing outside the code span.

Applied to files:

  • .archgate/adrs/ARCH-007-cross-platform-subprocess-execution.md
  • .archgate/adrs/GEN-006-markdown-code-span-integrity.md
  • .archgate/adrs/ARCH-010-prefer-bun-built-in-json-parsing.md
📚 Learning: 2026-07-25T16:24:51.133Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 501
File: .archgate/adrs/ARCH-003-output-formatting.md:0-0
Timestamp: 2026-07-25T16:24:51.133Z
Learning: In Archgate ADRs (.archgate/adrs/*.md), omit quantitative claims (e.g., token savings, benchmarks, performance deltas) unless they are backed by a reproducible measurement and supported by a single cited reference. If you cannot satisfy both (reproducible measurement + exactly one cited reference), describe the benefit qualitatively and tie it to the relevant policy/requirements instead of using numeric estimates.

Applied to files:

  • .archgate/adrs/ARCH-007-cross-platform-subprocess-execution.md
  • .archgate/adrs/GEN-006-markdown-code-span-integrity.md
  • .archgate/adrs/ARCH-010-prefer-bun-built-in-json-parsing.md
📚 Learning: 2026-07-25T22:03:17.073Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 501
File: .archgate/adrs/ARCH-015-cli-command-documentation-coverage.md:17-18
Timestamp: 2026-07-25T22:03:17.073Z
Learning: When updating an ADR that documents rule discovery/enforcement behavior, ensure the ADR’s stated discovery contract matches the implementation in code. If the rule only discovers commands by scanning `src/commands/*.ts` and `src/commands/*/index.ts`, the ADR must not claim it also inspects command registration calls elsewhere (e.g., `src/cli.ts`). Any ADR language that changes the documented contract should be treated as a normative change to behavior and aligned with the corresponding implementation/issue, not as prose-only documentation compression.

Applied to files:

  • .archgate/adrs/ARCH-007-cross-platform-subprocess-execution.md
  • .archgate/adrs/GEN-006-markdown-code-span-integrity.md
  • .archgate/adrs/ARCH-010-prefer-bun-built-in-json-parsing.md
📚 Learning: 2026-07-26T13:09:49.888Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 533
File: .archgate/adrs/ARCH-020-glob-scan-include-dotfiles.md:0-0
Timestamp: 2026-07-26T13:09:49.888Z
Learning: In archgate/cli rule ADRs, `ctx.scopedFiles` is computed from the ADR frontmatter `files` glob patterns before the rule context is constructed. For ARCH-020-style rules, ensure the ADR `files` frontmatter correctly scopes the allowed paths (e.g., `files: ["src/**/*.ts"]`); then rule-specific `.ts`/file filters should assume the incoming file list is already restricted and avoid re-applying the same path-prefix restriction inside individual rules.

Applied to files:

  • .archgate/adrs/ARCH-007-cross-platform-subprocess-execution.md
  • .archgate/adrs/GEN-006-markdown-code-span-integrity.md
  • .archgate/adrs/ARCH-010-prefer-bun-built-in-json-parsing.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/install-info.ts
📚 Learning: 2026-07-25T00:05:58.884Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 496
File: tests/helpers/auth.test.ts:38-46
Timestamp: 2026-07-25T00:05:58.884Z
Learning: When reviewing the Archgate CLI repository’s GEN-004 “concise forward-only narration” comments, don’t rely only on the automated phrase-based narration checks. Those checks can pass even when the comment wording describes historical/transfer semantics rather than current behavior (e.g., saying a prior restore “leaked” a value or a later subprocess “inherited it”). Manually verify that the comment describes the code’s current, forward behavior; flag or adjust comments that imply past/historical state transfer even if GEN-004 enforcement passes.

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • tests/engine/source-positions.test.ts
📚 Learning: 2026-07-25T23:21:11.504Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 512
File: tests/engine/runner-ast-cache.test.ts:82-82
Timestamp: 2026-07-25T23:21:11.504Z
Learning: For Bun/TS tests under tests/engine, it’s acceptable (per ARCH-025) to validate a runtime-sized collection produced by a single operation by looping over items and making direct assertions like `expect(item).toBe(...)` inside the loop. Treat this as an approved alternative to boolean-collapse assertions such as `expect(items.every(predicate)).toBe(true)`. Do NOT conflate this with prohibited “manual loops” that create independent test cases (e.g., calling `test(...)`/`it(...)` inside a loop); that pattern should still be flagged.

Applied to files:

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

Applied to files:

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

Applied to files:

  • tests/engine/source-positions.test.ts
🪛 LanguageTool
.archgate/adrs/GEN-006-markdown-code-span-integrity.md

[uncategorized] ~39-~39: Did you mean the formatting language “Markdown” (= proper noun)?
Context: ...L frontmatter, which is not rendered as markdown. A backslash that escapes another backs...

(MARKDOWN_NNP)


[style] ~49-~49: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...nd the space is stripped on render. - DO split a sentence instead, when a long...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~49-~49: To elevate your writing, try using a synonym here.
Context: ...stead, when a longer delimiter would be hard to read: give the inner command its own...

(HARD_TO)


[style] ~50-~50: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...e the surrounding message as prose. - DO treat a `GEN-006/no-escaped-backtick-...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~51-~51: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...it causes continues to end of line. - DO put illustrative examples of the brok...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~58-~58: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...e delimiters really are unbalanced. - DON'T extend the generated-file exemptio...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

🪛 markdownlint-cli2 (0.23.2)
.archgate/adrs/GEN-006-markdown-code-span-integrity.md

[warning] 19-19: Spaces inside code span elements

(MD038, no-space-in-code)

🔇 Additional comments (4)
src/engine/source-positions.ts (1)

134-148: LGTM!

tests/engine/source-positions.test.ts (1)

68-88: LGTM!

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

16-16: LGTM!

Also applies to: 63-63

.archgate/adrs/ARCH-010-prefer-bun-built-in-json-parsing.md (1)

11-36: LGTM!

Also applies to: 48-58

Comment thread .archgate/adrs/GEN-006-markdown-code-span-integrity.rules.ts Outdated
Comment thread src/helpers/install-info.ts
A fence closes only on its own marker, run at least as long as the
opener, with nothing but whitespace after it. Treating every fence-like
line as a toggle ended the block on a nested shorter run, exposing the
literal content after it to the code-span scan and inventing a
violation inside a fenced example.

The failure direction is now suppression only, which is what GEN-006's
Risks section claims.

Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 5, 2026
The escaped-backtick check and the ASCII-only PowerShell check enforce
one invariant: authored text must not carry a sequence that a
downstream reader decodes differently from the way it was written. Both
are lexical line scans for a corruption that format:check, oxlint, and
tsc are all structurally unable to see, so they belong to one decision
rather than two.

ARCH-021 widens to cover .ps1, .md, and .mdx and is retitled for the
invariant it now states. A standalone ADR bought nothing here: rules
are discovered through ADRs, so a second ADR was the price of a second
rule, not a second decision.

Closes #515

Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
@rhuanbarreto rhuanbarreto changed the title fix: bound scan loop, narrow install-method types, and add GEN-006 markdown code span rule fix: bound scan loop, narrow install-method types, and extend ARCH-021 to markdown code spans Aug 5, 2026
@rhuanbarreto

Copy link
Copy Markdown
Contributor Author

Restructured on review feedback: GEN-006 is gone. The markdown check now lives in ARCH-021, which is generalized rather than duplicated.

The reasoning: a standalone ADR bought nothing here. Rules are discovered through ADRs, so a second ADR was the price of a second rule, not of a second decision — and the decision was already made. The escaped-backtick check and the existing ASCII-only PowerShell check enforce one invariant: authored text must not carry a sequence that a downstream reader decodes differently from the way it was written. Both are lexical line scans for a corruption format:check, oxlint, and tsc are all structurally unable to see.

ARCH-021 is retitled Authored Text Integrity, widened to files: ["**/*.ps1", "**/*.md", "**/*.mdx"], and carries two rules:

Rule Scope
ARCH-021/ascii-only-ps1 .ps1 — unchanged
ARCH-021/no-escaped-backtick-in-markdown .md, .mdx — moved here

I confirmed the "rules without an ADR" shape is genuinely unavailable rather than merely unconventional: with the ADR removed and only the .rules.ts left on disk, the loader goes from 51 rules to 50, the rule never runs, and a real violation passes silently. loadRuleAdrs starts from parsed ADRs with rules: true and resolves the companion by filename, so an orphan companion has no discovery path.

Re-verified after the move, four ways:

Case Result
genuine escaped backtick in .md reported
four-backtick fence containing a three-backtick run silent
Windows path ending in a backslash inside a code span silent
non-ASCII character in a .ps1 still reported — no regression on the rule it now shares a file with

bun run validate exit 0, archgate check 51/51, no briefing-budget warnings. ARCH-021's two briefed sections measure 1686 and 1554 characters against the 2000 cap, so widening the ADR did not push its agent briefing into truncation.

The two earlier findings are unaffected and remain resolved — the fence-pairing fix moved across intact, and the install-info decline stands.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '=== Changed files ==='
git diff --name-status "$(git merge-base HEAD origin/main)" HEAD 2>/dev/null || git diff --name-status HEAD~1 HEAD

printf '%s\n' '=== ARCH-021 candidates ==='
fd -a 'ARCH-021*' .archgate 2>/dev/null || true

printf '%s\n' '=== Rule loader candidates ==='
rg -n -C 3 '\bloadRuleAdrs\b|rules:\s*true|\.rules\.ts' src .archgate -g '*.ts' 2>/dev/null || true

printf '%s\n' '=== Source position changes ==='
git diff "$(git merge-base HEAD origin/main)" HEAD -- src/engine/source-positions.ts tests/engine/source-positions.test.ts 2>/dev/null || true

Length of output: 20516


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '=== Rule file outline ==='
ast-grep outline .archgate/adrs/ARCH-021-authored-text-integrity.rules.ts --items all --view expanded || true

printf '%s\n' '=== ADR frontmatter and enforcement sections ==='
rg -n -C 8 '^(---|id:|title:|files:|rules:|## (Decision|Compliance|Enforcement|Scope))' \
  .archgate/adrs/ARCH-021-authored-text-integrity.md

printf '%s\n' '=== Full companion rule ==='
cat -n .archgate/adrs/ARCH-021-authored-text-integrity.rules.ts

printf '%s\n' '=== Loader selection implementation ==='
sed -n '260,370p' src/engine/loader.ts

printf '%s\n' '=== Tests and references for ARCH-021 rules ==='
rg -n -C 5 'ascii-only-ps1|no-escaped-backtick-in-markdown|ARCH-021' \
  tests .archgate -g '*.test.ts' -g '*.spec.ts' -g '*.ts' -g '*.md' 2>/dev/null || true

printf '%s\n' '=== Exact new-file diff ==='
git diff "$(git merge-base HEAD origin/main)" HEAD -- \
  .archgate/adrs/ARCH-021-authored-text-integrity.md \
  .archgate/adrs/ARCH-021-authored-text-integrity.rules.ts

Length of output: 43906


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
def find_closing_run(line, start, length):
    i = start
    while i < len(line):
        if line[i] != "`":
            i += 1
            continue
        run = 0
        while i + run < len(line) and line[i + run] == "`":
            run += 1
        if run == length:
            return i
        i += run
    return -1

def first_escape_column(line):
    i = 0
    while i < len(line):
        if line[i] == "\\":
            if i + 1 < len(line) and line[i + 1] == "`":
                return i + 1
            i += 2
            continue
        if line[i] != "`":
            i += 1
            continue
        run = 0
        while i + run < len(line) and line[i + run] == "`":
            run += 1
        close = find_closing_run(line, i + run, run)
        i = i + run if close == -1 else close + run
    return None

cases = {
    "ADR documented invalid form": r'`UserError("Run \`archgate init\` first")`',
    "Windows path ending in a backslash": r'`C:\work\`',
    "escape outside a span": r'prose \` text',
}
for name, line in cases.items():
    print(f"{name}: column={first_escape_column(line)!r}; line={line}")
PY

Length of output: 1528


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
def find_closing_run(line, start, length):
    i = start
    while i < len(line):
        if line[i] != "`":
            i += 1
            continue
        run = 0
        while i + run < len(line) and line[i + run] == "`":
            run += 1
        if run == length:
            return i
        i += run
    return -1

def first_escape_column(line):
    i = 0
    while i < len(line):
        if line[i] == "\\":
            if i + 1 < len(line) and line[i + 1] == "`":
                return i + 1
            i += 2
            continue
        if line[i] != "`":
            i += 1
            continue
        run = 0
        while i + run < len(line) and line[i + run] == "`":
            run += 1
        close = find_closing_run(line, i + run, run)
        i = close + run if close != -1 else i + run
    return None

line = r"`show \`literal tick`"
print(f"line={line}")
print(f"reported_backslash_column={first_escape_column(line)!r}")
PY

Length of output: 1198


@rhuanbarreto I found one issue in the moved Markdown rule.

  • .archgate/adrs/ARCH-021-authored-text-integrity.rules.ts, Line 74: firstEscapeColumn() skips an escaped backtick when that backtick is the next equal-length delimiter. For example, `show \`literal tick` returns no finding. The scanner pairs the first backtick with \``, then treats the final backtick as unclosed. CommonMark instead closes the first span at `, so this input has the corruption that ARCH-021/no-escaped-backtick-in-markdown` must report. Detect a backslash before a candidate closing delimiter unless it is confirmed as the valid trailing-backslash case. Add a regression test for this three-backtick form.

✏️ Learnings added
Learnt from: rhuanbarreto
URL: https://github.com/archgate/cli/pull/548

Timestamp: 2026-08-05T11:57:17.776Z
Learning: In this repository, `src/engine/loader.ts` function `loadRuleAdrs()` discovers executable `.rules.ts` files only from parsed ADR Markdown files with `rules: true`, then resolves the companion by the ADR basename. An orphan `.rules.ts` file has no rule-discovery path.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
.archgate/adrs/ARCH-021-authored-text-integrity.rules.ts (1)

49-77: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Check escaped backticks before changing code-span state.

firstEscapeColumn() scans backslash + backtick before opener handling, but it skips that pair at the start of a span because it then treats the backtick as the delimiter. A backslash cannot escape a backtick delimiter in CommonMark, so such lines should be reported before the next matching backtick closes the span.

  • .archgate/adrs/ARCH-021-authored-text-integrity.rules.ts#L49-L77: Report escaped backtick pairs before treating the following backtick as an opener, and add a regression such as `show \`literal tick`.
  • .archgate/adrs/ARCH-021-authored-text-integrity.md#L23: Remove the two backslash-backtick pairs from the explanatory prose outside the fenced example.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.archgate/adrs/ARCH-021-authored-text-integrity.rules.ts around lines 49 -
77, Update firstEscapeColumn in
.archgate/adrs/ARCH-021-authored-text-integrity.rules.ts (lines 49-77) to detect
backslash-backtick pairs before treating the backtick as a code-span opener,
while preserving delimiter scanning behavior; add a regression for `show
\`literal tick`. In .archgate/adrs/ARCH-021-authored-text-integrity.md (line
23), remove the two backslash-backtick pairs from the explanatory prose outside
the fenced example.

Source: Learnings

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

Inline comments:
In @.archgate/adrs/ARCH-021-authored-text-integrity.rules.ts:
- Around line 91-116: Update the line-scanning logic around firstEscapeColumn()
in ARCH-021-authored-text-integrity.rules.ts to preserve active code-span
delimiter state across physical lines, so escaped backticks inside multiline
spans are ignored until the matching close; add the requested double-backtick
multiline fixture with a later close. Update ARCH-021-authored-text-integrity.md
lines 89-90 to document that the rule must avoid both suppressing and inventing
reports for content inside multiline code spans.

---

Outside diff comments:
In @.archgate/adrs/ARCH-021-authored-text-integrity.rules.ts:
- Around line 49-77: Update firstEscapeColumn in
.archgate/adrs/ARCH-021-authored-text-integrity.rules.ts (lines 49-77) to detect
backslash-backtick pairs before treating the backtick as a code-span opener,
while preserving delimiter scanning behavior; add a regression for `show
\`literal tick`. In .archgate/adrs/ARCH-021-authored-text-integrity.md (line
23), remove the two backslash-backtick pairs from the explanatory prose outside
the fenced example.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 43d672ff-ca01-4636-8a76-4c99804a4641

📥 Commits

Reviewing files that changed from the base of the PR and between 900273b and d2dda22.

📒 Files selected for processing (5)
  • .archgate/adrs/ARCH-021-ascii-only-powershell-scripts.md
  • .archgate/adrs/ARCH-021-ascii-only-powershell-scripts.rules.ts
  • .archgate/adrs/ARCH-021-authored-text-integrity.md
  • .archgate/adrs/ARCH-021-authored-text-integrity.rules.ts
  • .claude/agent-memory/archgate-developer/MEMORY.md
💤 Files with no reviewable changes (2)
  • .archgate/adrs/ARCH-021-ascii-only-powershell-scripts.md
  • .archgate/adrs/ARCH-021-ascii-only-powershell-scripts.rules.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Cloudflare Pages
🧰 Additional context used
📓 Path-based instructions (3)
.archgate/adrs/**/*.{md,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

Read relevant self-governance ADRs before architectural changes; ADRs use YAML frontmatter and companion .rules.ts files exporting a plain object satisfying RuleSet.

Files:

  • .archgate/adrs/ARCH-021-authored-text-integrity.md
  • .archgate/adrs/ARCH-021-authored-text-integrity.rules.ts
.archgate/adrs/**/*.rules.ts

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

.archgate/adrs/**/*.rules.ts: Comments in .archgate/adrs/**/*.rules.ts must be concise, forward-only, and limited to current behavior; historical or relocation narration is prohibited.
Changes to narration or relocation detection patterns in companion .rules.ts files must be synchronized with .archgate/lint/oxlint.ts, and both enforcement layers must continue to report violations at error severity.

Files:

  • .archgate/adrs/ARCH-021-authored-text-integrity.rules.ts
.archgate/{lint,adrs}/**/*.ts

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

.archgate/{lint,adrs}/**/*.ts: A contiguous run of whole-line comments must contain at most five lines of narrative prose, including in lint and companion rule implementations.
Use the same synchronized structural-TSDoc exemption in Archgate TypeScript files; narrative must not be relabeled with prose-container tags to evade the limit.

Files:

  • .archgate/adrs/ARCH-021-authored-text-integrity.rules.ts
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: archgate/cli

Timestamp: 2026-08-05T11:57:29.098Z
Learning: Extract a helper such as `run(cmd, opts)` or `runGit(args, cwd)` when a module performs several subprocess calls with the same shape.
📚 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-021-authored-text-integrity.md
📚 Learning: 2026-07-25T16:24:51.133Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 501
File: .archgate/adrs/ARCH-003-output-formatting.md:0-0
Timestamp: 2026-07-25T16:24:51.133Z
Learning: In Archgate ADRs (.archgate/adrs/*.md), omit quantitative claims (e.g., token savings, benchmarks, performance deltas) unless they are backed by a reproducible measurement and supported by a single cited reference. If you cannot satisfy both (reproducible measurement + exactly one cited reference), describe the benefit qualitatively and tie it to the relevant policy/requirements instead of using numeric estimates.

Applied to files:

  • .archgate/adrs/ARCH-021-authored-text-integrity.md
📚 Learning: 2026-07-25T22:03:17.073Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 501
File: .archgate/adrs/ARCH-015-cli-command-documentation-coverage.md:17-18
Timestamp: 2026-07-25T22:03:17.073Z
Learning: When updating an ADR that documents rule discovery/enforcement behavior, ensure the ADR’s stated discovery contract matches the implementation in code. If the rule only discovers commands by scanning `src/commands/*.ts` and `src/commands/*/index.ts`, the ADR must not claim it also inspects command registration calls elsewhere (e.g., `src/cli.ts`). Any ADR language that changes the documented contract should be treated as a normative change to behavior and aligned with the corresponding implementation/issue, not as prose-only documentation compression.

Applied to files:

  • .archgate/adrs/ARCH-021-authored-text-integrity.md
📚 Learning: 2026-07-26T13:09:49.888Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 533
File: .archgate/adrs/ARCH-020-glob-scan-include-dotfiles.md:0-0
Timestamp: 2026-07-26T13:09:49.888Z
Learning: In archgate/cli rule ADRs, `ctx.scopedFiles` is computed from the ADR frontmatter `files` glob patterns before the rule context is constructed. For ARCH-020-style rules, ensure the ADR `files` frontmatter correctly scopes the allowed paths (e.g., `files: ["src/**/*.ts"]`); then rule-specific `.ts`/file filters should assume the incoming file list is already restricted and avoid re-applying the same path-prefix restriction inside individual rules.

Applied to files:

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

Applied to files:

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

Applied to files:

  • .claude/agent-memory/archgate-developer/MEMORY.md
🪛 LanguageTool
.archgate/adrs/ARCH-021-authored-text-integrity.md

[uncategorized] ~53-~53: Did you mean the formatting language “Markdown” (= proper noun)?
Context: ...s) $errs ``` No escaped backticks in markdown. Markdown and MDX files MUST NOT cont...

(MARKDOWN_NNP)


[uncategorized] ~66-~66: Did you mean the formatting language “Markdown” (= proper noun)?
Context: ... character to delete: the backslash for markdown, the offending character for .ps1 ##...

(MARKDOWN_NNP)

🪛 markdownlint-cli2 (0.23.2)
.archgate/adrs/ARCH-021-authored-text-integrity.md

[warning] 23-23: Spaces inside code span elements

(MD038, no-space-in-code)

🪛 OpenGrep (1.26.0)
.archgate/adrs/ARCH-021-authored-text-integrity.rules.ts

[ERROR] 27-27: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

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

29-29: LGTM!

Also applies to: 41-41

Comment thread .archgate/adrs/ARCH-021-authored-text-integrity.rules.ts Outdated
A CommonMark code span may cross a line break and close on a later line
of the same block. Restarting the walk at each line read that span's
continuation as text, so a backslash-backtick inside it was reported
against literal content.

The scan now threads the open backtick-run length from line to line and
clears it at a blank line, since a span cannot outlive its block. A run
left unmatched on its line is treated as still open rather than as
text, which keeps the failure direction on suppression.

ARCH-021's Negative bullet is restated to match: the check is
block-scoped, and an unmatched run costs reports to the next blank line.

Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
@rhuanbarreto
rhuanbarreto enabled auto-merge (squash) August 5, 2026 12:21
@rhuanbarreto
rhuanbarreto merged commit 3793d80 into main Aug 5, 2026
22 checks passed
@rhuanbarreto
rhuanbarreto deleted the rhuanbarreto/verify-issues-541-540-515-79ebca branch August 5, 2026 12:23
@archgatebot archgatebot Bot mentioned this pull request Aug 5, 2026
@rhuanbarreto

Copy link
Copy Markdown
Contributor Author

Re the outside-diff finding in review 4864151998"Check escaped backticks before changing code-span state": declining both parts, with the measurement below. (No inline thread was created for it, so replying here.)

Why the reordering can't work

The suggested regression case and a legitimate Windows path are the same bytes. In both, the span's last content character is a backslash and the delimiter that follows closes the span:

`show \`literal tick`          <- the review's example
`C:\Users\<username>\.config\` <- docs/src/content/docs/guides/opencode-integration.mdx:27

Tokenized, the structure is identical — the character immediately before the closing delimiter is a backslash in each. Nothing lexical distinguishes them; the only difference is what the author meant, which the scanner cannot see. So reporting the first necessarily reports the second.

Measured

I implemented the suggestion (report a backslash-backtick pair before treating the following backtick as a delimiter) and ran it over the repository's markdown:

Result Count
lines flagged 7
genuine defects among them 0

All seven are correct-rendering content: six are the Windows path rows in the opencode integration guide across three locales, and the seventh is ARCH-021's own line 23 — which is the second part of the finding. Those two spans there are deliberate: they quote the fragments that fall out of the mangled example, and they render correctly as code containing a trailing backslash. Removing them would make the explanation wrong.

This is the alternative ARCH-021 already records as considered and rejected: "Flag every backslash-backtick byte pair — rejected. It cannot distinguish a mistaken escape from a code span whose content legitimately ends in a backslash, which a Windows path routinely does. A rule that fires on correct prose is a rule contributors learn to suppress."

What I did change

The finding is right that this shape goes unreported, and ARCH-021 did not say so. That silence was a real gap — a limit a later reader would have had to rediscover. #549 adds it to Consequences as a stated boundary:

A single escape immediately before a span's closing delimiter is not reported. [...] and a Windows path ending in a backslash are the same bytes — a backslash as the span's last content character — so no lexical rule separates them, and reporting one reports the other. The check accepts this false negative rather than fire on correct prose; the multi-escape form, which is what a mangled snippet almost always looks like, is still caught because its later escapes land in text.

The last clause is the coverage that matters in practice: the shape from #515 carries two escapes, so its second one lands in text and is caught. Verified — that case still reports, and the seven correct lines above still do not.

🤖 Addressed by Claude Code

rhuanbarreto added a commit that referenced this pull request Aug 5, 2026
…549)

Two documentation-only follow-ups from the review cycle on #548. No
source, rule, or behaviour changes.

## 1. Host a new rule on the ADR that already states its invariant

The correction on #548 was that a rule does not justify its own ADR.
Rules are reachable only through ADRs — `loadRuleAdrs` walks ADRs with
`rules: true` and resolves `<baseName>.rules.ts` by filename, so an
orphan companion has no discovery path and silently never runs. That
coupling makes a new ADR the _price_ of a new rule, not evidence that a
new decision exists.

Extends the existing "pick the right enforcement layer" memory rather
than adding a file, since it is the same question one level deeper:
having chosen the ADR-rule layer, which ADR.

## 2. Name the escape-at-close case ARCH-021 does not report

A later review asked the markdown check to report an escaped backtick
before treating the following backtick as a span delimiter, with ``
`show \`literal tick` `` as the regression case. Declined on measurement
— implementing it flags **7 lines across 244 markdown files, of which 0
are defects**: six Windows-path rows in the opencode integration guide
across three locales, plus ARCH-021's own explanatory prose.

The two shapes are byte-identical. In each, the span's last content
character is a backslash and the delimiter that follows closes the span:

```text
`show \`literal tick`          <- the review's example
`C:\Users\<username>\.config\` <- opencode-integration.mdx:27
```

Nothing lexical separates them; only authorial intent does, which the
scanner cannot see. ARCH-021 already records this as a
considered-and-rejected alternative.

But the finding was right that the ADR never _said_ the shape goes
unreported — and that silence is why it reads as a bug rather than a
boundary. Consequences now states it, along with the mitigation: the
multi-escape form a mangled snippet almost always takes is still caught,
because its later escapes land in text rather than inside a span. That
is the shape the corruption in #515 actually had.

## Verification

`bun run validate` exit 0 (2017 tests, 0 fail). `archgate check` 51/51,
no briefing-budget warnings — the added text is in Consequences, which
is not a briefed section, so ARCH-021's Decision and Do's and Don'ts are
unchanged at 1686 and 1554 characters against the 2000 cap.

The new bullet quotes its example in a double-backtick span, so it
renders as the literal source and does not trip the rule it documents.

---------

Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
rhuanbarreto pushed a commit that referenced this pull request Aug 7, 2026
# archgate

## [0.52.0](v0.51.0...v0.52.0)
(2026-08-06)

### ⚠ BREAKING CHANGES

* add --strict and --output <format> (SARIF), remove
--json/--ci/--max-warnings from check (#536)

### Features

* add --strict and --output <format> (SARIF), remove
--json/--ci/--max-warnings from check
([#536](#536))
([70b1ede](70b1ede))
* **dist:** distribute archgate via winget
([#552](#552))
([93cb3a8](93cb3a8)),
references [#544](#544)
* **lint:** migrate to TypeScript 7 and adopt oxlint type-aware linting
([#534](#534))
([29daad8](29daad8)),
references [#529](#529)
* **plugin:** install Copilot plugin declaratively, covering the desktop
app ([#543](#543))
([ae2b988](ae2b988))

### Bug Fixes

* bound scan loop, narrow install-method types, and extend ARCH-021 to
markdown code spans ([#548](#548))
([3793d80](3793d80)),
closes [#541](#541)
[#540](#540)
[#515](#515), references
[#541](#541)
[#540](#540)
[#515](#515)
* **cli:** exit 0 quietly when the output pipe closes (EPIPE)
([#546](#546))
([e7bfa19](e7bfa19))
* **hooks:** invoke hooks through package scripts instead of a shell
([#557](#557))
([3b9e411](3b9e411)),
references [#442](#442)
[#441](#441)
[#442](#442)

---
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

1 participant