Skip to content

feat(ci): ClawReview πŸ¦€ β€” our own advisory PR bot (policy + duplicates + review)#248

Merged
KrasimirKralev merged 5 commits into
ID-Robots:mainfrom
KrasimirKralev:feat/clawreview-bot
Jul 5, 2026
Merged

feat(ci): ClawReview πŸ¦€ β€” our own advisory PR bot (policy + duplicates + review)#248
KrasimirKralev merged 5 commits into
ID-Robots:mainfrom
KrasimirKralev:feat/clawreview-bot

Conversation

@KrasimirKralev

@KrasimirKralev KrasimirKralev commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Clawsweeper-inspired PR bot, owned by us, complementing CodeRabbit. On every non-draft, non-bot PR it posts (and thereafter updates β€” one comment, no spam) a structured advisory review:

  • Summary + verdict badge (🟒 looks-good / 🟠 needs-changes / 🟑 needs-discussion / πŸ”΄ likely-duplicate) + confidence + source/test surface split, plus a hidden <!-- clawreview-verdict:... --> marker for tooling
  • Repo-policy checks (deterministic, no AI): beta-first base rule with release-promotion exemption, diff-aware package.json↔bun.lock consistency, conventional title (incl. release:), tests-expected (β‰₯10 src lines), size, and security-sensitive-path notices (ℹ️, not ⚠️)
  • Findings P1–P3 + duplicate detection vs open PRs + linked-issue validation
  • Auto area-labels (same taxonomy as the issue-triage bot)

Empirically tuned: replayed against the 40 most recently merged PRs β€” the naive checks would have warned on 55% of them (5 warnings on the routine release PR); the tuned set warns on ~35%, all genuine, release PRs clean.

Advisory by design: never closes PRs, never fails the pipeline (exit 0 on any error), humans decide β€” unlike clawsweeper's auto-close, which fits a firehose repo, not ours.

Security shape (pull_request_target discipline): the checkout is the base repo only β€” PR code is never checked out or executed; the diff is reviewed as data via the API (80 k char cap); PR title/body/diff are declared untrusted in the system prompt; SHA-pinned actions; job-scoped permissions; author-checked comment upsert.

Activation

  • Shares the ANTHROPIC_API_KEY repo secret with the issue-triage bot (@yalexx β€” same pending Settings item). Until set, it no-ops gracefully.
  • ⚠️ pull_request_target runs the workflow from the PR's base branch β€” a follow-up PR syncs this to beta so beta-targeting PRs (most of our volume) trigger it too.

Validated by dry-runs against real PRs: #217 (release promotion → 0 warnings), #238 (meta→main → all pass), #227 (device→beta → all pass), #243 (bot-skip fires).

Summary by CodeRabbit

  • New Features

    • Added automated pull request advisory reviews with structured feedback, duplicate detection, convention checks, and area-based labeling.
    • Improved PR handling with safeguards for draft and bot-authored requests, plus per-PR run cancellation to keep results current.
  • Documentation

    • Updated contributor guidance to explain that PRs now receive two automated review passes and should be addressed or discussed.

… review)

Clawsweeper-inspired PR bot, owned by us, complementing CodeRabbit:

- Structured advisory review on every PR (opened/reopened/synchronize):
  summary, source/test surface split, severity-ranked findings (P1-P3),
  duplicate detection vs open PRs + linked-issue validation, and a hidden
  <!-- clawreview-verdict:... --> marker for tooling
- Deterministic repo-policy checks (no AI needed): beta-first base-branch
  rule (docs/meta paths may target main), package.json<->bun.lock
  consistency, conventional title, tests-expected heuristic,
  security-sensitive-path flagging, size warning
- Auto area-labels from touched paths (same taxonomy as the issue-triage bot)
- Advisory by design: posts + updates ONE comment (upsert by marker, no spam
  on every push); never closes PRs; never fails the pipeline (exit 0 on any
  error); skips bot-authored PRs
- Security: pull_request_target with the base-repo checkout ONLY β€” PR code
  is never checked out or executed; the diff is reviewed as data via the API
  (capped at 80k chars); PR title/body/diff are declared untrusted in the
  system prompt
- Same hardening as the triage bot: SHA-pinned actions, job-scoped
  permissions, --prefix scripts SDK install, graceful no-op without
  ANTHROPIC_API_KEY (same pending secret)

Dry-run validated against real PRs: ID-Robots#243 (bot-skip fires), ID-Robots#238 (meta->main
passes base policy), ID-Robots#227 (device->beta passes; sensitive-path + tests
checks fire).
…d plumbing

Four-angle /simplify review applied. The big one: replaying the policy
checks against the 40 most recently merged PRs showed 55% would have warned
(the routine release PR collected 5 warnings at once) β€” warnings tuned to
be rare enough to mean something:

- Release promotions (head beta -> base main) are the sanctioned path to
  main: exempted from feature-PR conventions (was 3/4 of base-check warns)
- Lockfile check is diff-aware: only warns when package.json DEPENDENCY
  sections change β€” version-only bumps never break --frozen-lockfile
  (6/6 historical warns on the naive check were false)
- 'release' added to the conventional-title types (the repo's own release
  convention was failing its own check)
- Tests-expected check gated to >=10 changed src lines (string-swap PRs
  were the main noise)
- SENSITIVE_RE: config/ narrowed to root-privilege files (was flagging
  every openclaw-target.txt version bump); added real misses (login-api
  route, rate limiters, oauth utils, credentials route, root-update-step.sh,
  launch-browser.sh, recover.sh); rendered as an ℹ️ note, not ⚠️, so
  warnings keep meaning

Plumbing (config + simplification reviews):
- upsertComment author-checked (a user comment starting with our marker
  can't be PATCH-overwritten) and single-page (bot comments early)
- --paginate + per-page '-q [...]' arrays would crash JSON.parse on
  >100-item PRs (we've had a 1,028-file PR) β€” files now fetched as JSONL
- drafts skipped (reviewed at ready_for_review; type added)
- bot-skip moved before the expensive gathering; dead 'dependabot' arm
  dropped; dead fetched fields trimmed; labels applied in one batched
  pr edit; duplicate render gated on likely && of
- reciprocal keep-in-sync comments at the two cross-bot drift points
  (area taxonomy, SDK version pin)

Re-validated by dry-run: ID-Robots#217 release promotion 5 warns -> 0 (1 pass +
1 info), ID-Robots#238 meta->main all-pass, ID-Robots#227 device->beta all-pass.
@KrasimirKralev
KrasimirKralev requested a review from a team as a code owner July 4, 2026 20:10
@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

Next review available in: 29 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 306b01fb-c81c-4943-bb89-870df5243bd7

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 680b149 and 43e9b23.

πŸ“’ Files selected for processing (4)
  • .github/workflows/issue-triage.yml
  • .github/workflows/pr-review.yml
  • scripts/issue-triage.mjs
  • scripts/pr-review.mjs
πŸ“ Walkthrough

Walkthrough

This PR adds a new "ClawReview" GitHub Actions workflow and accompanying scripts/pr-review.mjs script that performs deterministic policy checks and an AI-generated review on pull requests, posting a comment and applying area labels. CONTRIBUTING.md and existing issue-triage files are updated to reflect and reference this addition.

Changes

ClawReview PR advisory bot

Layer / File(s) Summary
ClawReview workflow definition
.github/workflows/pr-review.yml
New workflow triggered on pull_request_target with per-PR concurrency, bot/draft guard, base-repo-only checkout, Node 22 setup, pinned Anthropic SDK install, and script execution wiring secrets.
Data gathering and configuration
scripts/pr-review.mjs
Defines config constants, PR number resolution, gh command helpers, path classification regexes, and gathers PR metadata, diff, linked issues, and open PRs.
Deterministic checks and AI review
scripts/pr-review.mjs
Implements policy checks (release conventions, lockfile consistency, title format, size/churn, security paths), a surface-count helper, and the Anthropic-based review schema/prompt/call.
Comment output, labeling, and main flow
scripts/pr-review.mjs
Composes and upserts the PR comment, applies up to three area: labels, and orchestrates main() with non-blocking error handling.
Docs and cross-script sync notes
CONTRIBUTING.md, .github/workflows/issue-triage.yml, scripts/issue-triage.mjs
Documents the two automated advisory reviews and adds comments noting SDK version/area-taxonomy sync requirements with pr-review.mjs.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Contributor
  participant GitHub
  participant ClawReviewWorkflow
  participant PRReviewScript
  participant AnthropicAPI

  Contributor->>GitHub: open/update pull request
  GitHub->>ClawReviewWorkflow: pull_request_target event
  ClawReviewWorkflow->>ClawReviewWorkflow: skip if bot-authored or draft
  ClawReviewWorkflow->>PRReviewScript: run node scripts/pr-review.mjs
  PRReviewScript->>GitHub: fetch PR metadata, diff, linked issues, open PRs
  PRReviewScript->>PRReviewScript: run deterministic policy checks
  PRReviewScript->>AnthropicAPI: request structured review JSON
  AnthropicAPI-->>PRReviewScript: summary, findings, duplicate verdict
  PRReviewScript->>GitHub: upsert marked comment
  PRReviewScript->>GitHub: apply up to 3 area labels
Loading

Possibly related PRs

  • ID-Robots/clawbox#221: Updates the same issue-triage workflow and script files to keep the Anthropic SDK version and area label taxonomy consistent with this PR's new pr-review.mjs logic.

Suggested reviewers: yalexx

πŸš₯ Pre-merge checks | βœ… 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the feature well but does not follow the repository's required template or include the requested sections. Reformat it to the template with Summary, Type of change, How was this tested?, Checklist, and Screenshots/logs sections.
βœ… Passed checks (4 passed)
Check name Status Explanation
Title check βœ… Passed The title clearly identifies the new ClawReview advisory PR bot and its policy/duplicate-review focus.
Docstring Coverage βœ… Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check βœ… Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check βœ… Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
πŸ§ͺ Generate unit tests (beta)
  • Create PR with unit tests

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.

@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 @.github/workflows/pr-review.yml:
- Around line 11-30: Add a workflow-level permissions block at the top of the
pr-review workflow so the default GITHUB_TOKEN starts with no access; keep the
existing job-scoped permissions on the review job unchanged. Update the
top-level workflow configuration near the pull_request_target and jobs
definitions so only the explicit review job scopes apply and the default grant
is empty.

In `@scripts/pr-review.mjs`:
- Around line 110-114: The `bun.lock` warning logic in `scripts/pr-review.mjs`
is too broad because the `depsTouched` scan inside the `package.json` hunk
treats any added/removed top-level string property as a dependency change.
Tighten the check in the `package.json` branch so it only flags actual
dependency sections, using the existing `depsTouched`/`warn` flow and `hunk`
scan logic; either track the current top-level key while iterating the hunk or
explicitly exclude non-dependency keys like `name`, `description`, `scripts`,
`author`, and `version` before calling `warn`.
πŸͺ„ 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d74e9b93-dad8-48a8-8269-ed645719b6e3

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 52a786e and 680b149.

πŸ“’ Files selected for processing (5)
  • .github/workflows/issue-triage.yml
  • .github/workflows/pr-review.yml
  • CONTRIBUTING.md
  • scripts/issue-triage.mjs
  • scripts/pr-review.mjs

Comment thread .github/workflows/pr-review.yml
Comment thread scripts/pr-review.mjs
Playful frame, rigorous content: greeting, verdict badge, clean-bill line,
and sign-off carry the persona (Shipshape β€” claws up / Needs a molt /
Walking sideways / This shell looks occupied); policy checks and findings
stay strictly factual so a P1 never drowns in puns. Lines are picked
deterministically by PR number, keeping the upserted comment's voice stable
across pushes. The model's summary gets at most one marine flourish by
prompt; finding titles/details are instructed pun-free.
…crab avatar)

Both bots (pr-review + issue-triage) mint an installation token via
actions/create-github-app-token when the CLAWREVIEW_APP_ID +
CLAWREVIEW_APP_PRIVATE_KEY secrets exist β€” comments and labels then post as
clawreview[bot] with the App's avatar (and get the App's own rate limits).
Without the secrets the step skips and everything falls back to
github-actions[bot]; the bots work either way. Comment-upsert author check
accepts both identities so the switch-over edits the same comment.
Both bots now prefer the Claude Code CLI (claude -p, authed by
CLAUDE_CODE_OAUTH_TOKEN) β€” the official Pro/Max subscription path, same
runtime as claude-code-action and the team's crons β€” falling back to the
Anthropic SDK when only ANTHROPIC_API_KEY is set, and no-op without either.

- review()/main() split into reviewViaClaudeCli + reviewViaSdk (pr-review)
  and classifyViaClaudeCli + classifyViaSdk (issue-triage); selected by
  CLAUDE_CODE_OAUTH_TOKEN presence at runtime
- parseModelJson tolerates fenced/wrapped CLI output (json wrapper ->
  .result -> object); the SDK path keeps schema-enforced output
- workflows install the backend the available secret needs (CLI when OAuth,
  SDK otherwise), both pinned; token passed to the run step
- REVIEW_ONLY env prints the composed comment without posting, for local
  end-to-end testing of either transport

CodeRabbit (ID-Robots#248):
- top-level permissions: {} so the default token grant is empty (only the
  job's explicit scopes apply)
- bun.lock check: dependency-entry match now requires a version-spec-shaped
  value (^/~/digit/npm:/workspace:/git/url), so edits to scripts/name/etc.
  no longer misfire the 'dependencies changed' warning

Validated end-to-end via the OAuth CLI transport against PR ID-Robots#227: correct
verdict, crab voice, and a genuine P3 finding in gateway-pre-start.sh.
@KrasimirKralev
KrasimirKralev merged commit e4bd878 into ID-Robots:main Jul 5, 2026
7 checks passed
KrasimirKralev added a commit that referenced this pull request Jul 5, 2026
…#252)

* fix(ci): lazy-load the Anthropic SDK so the OAuth path needs no SDK

The OAuth transport (shipped in #248) installs only the Claude Code CLI, not
@anthropic-ai/sdk, but both bot scripts had a static top-level 'import
Anthropic from @anthropic-ai/sdk' evaluated at module load regardless of
transport β€” so the live bots on main crash with ERR_MODULE_NOT_FOUND before
running. Moved the import into a dynamic import() inside
reviewViaSdk/classifyViaSdk, reached only on the API-key fallback. Verified
by running the OAuth path with node_modules removed. Same fix rides to beta
in #250.

* feat(ci): reshape ClawReview into an informative mascot (not a reviewer)

Per direction: ClawReview should be a knowledgeable, friendly crab mascot that
greets and orients on issues/PRs β€” NOT a code reviewer racing CodeRabbit.

PR bot (pr-review.mjs):
- Dropped severity findings (P1/P2/P3) and pass/fail verdicts β€” that was the
  CodeRabbit-racing part. Schema is now summary + kind + touches + neutral
  'highlights' (helpful heads-ups, never bug reports) + duplicate hint.
- System prompt recast: 'you are the mascot, not a reviewer; CodeRabbit does
  the line-by-line; produce orientation, never verdicts'. Empty highlights is
  normal and encouraged.
- Comment is now: greeting -> plain-language summary -> 'At a glance'
  (kind/touches/base/surface + deterministic policy heads-ups) -> optional
  'Good to know' -> sign-off that explicitly defers to CodeRabbit.

Issue bot (issue-triage.mjs):
- Rebranded the triage comment as the same crab mascot (one character across
  issues + PRs): friendly greeting + summary + at-a-glance labels + next step.
- Added DRY_RUN (prints the comment, skips label writes) for local testing.

Deterministic policy checks stay β€” they're genuine ClawBox-specific value
(beta-first, bun.lock, sensitive paths) CodeRabbit can't provide β€” but are
now framed as friendly context, not a verdict. Validated end-to-end via the
OAuth CLI transport on PR #227 and issue #232.

* chore(assets): add square crab avatar for the ClawReview GitHub App

512x512 crab-only crop of the ClawBox logo (wordmark removed, padded so a
circular avatar mask keeps the claws). This is the image to upload when
creating the ClawReview GitHub App β€” a stable raw URL beats the earlier
non-square 87x128 crab PNG.

* fix(ci): apply CodeRabbit review β€” app-token if-guard + shared backend

CodeRabbit's 4 actionable findings on the bots:

1+2. app-token step's `if: env.HAS_APP` (both workflows) referenced env the
     step sets on ITSELF β€” a step's if can't see its own step-level env, so
     the App token would NEVER mint even with the secrets set. Hoisted
     HAS_APP to job-level env (visible to the step if), and scoped the minted
     token to only the permissions each bot needs (pull-requests+issues for
     review, issues for triage) instead of the full installation grant.

3.   parseModelJson + the two transports (CLI/SDK) were duplicated across
     pr-review.mjs and issue-triage.mjs. Extracted to scripts/lib/
     ai-backend.mjs (callClaude); both bots import it, so the transport stays
     in sync. This is the 'first SDK-shape change' trigger the earlier reuse
     analysis set for extraction.

4.   Static @anthropic-ai/sdk import breaking the OAuth path β€” already fixed
     (lazy import); the shared backend now owns the sole lazy import, so
     pr-review.mjs no longer imports the SDK at all.

Validated via the OAuth CLI transport with node_modules removed: both bots
produce their mascot output through the shared backend.

* fix(ci): address CodeRabbit re-review β€” tolerant SDK parse + read-only dry-run

- ai-backend.mjs: viaSdk now uses parseModelJson (tolerant of fenced/wrapped
  JSON) instead of a bare JSON.parse, matching the CLI path.
- issue-triage.mjs: DRY_RUN now gates the whole label block (ensure() +
  gh issue edit), not just the edit β€” a dry run is fully read-only.
KrasimirKralev added a commit that referenced this pull request Jul 5, 2026
* chore(ci): sync ClawReview + triage bots to beta

pull_request_target and issues-triggered workflows run from the PR's/issue's
BASE branch, so the bots must exist on beta too β€” otherwise beta-targeting
PRs (most of our volume) never trigger ClawReview. Brings the exact CI files
from main (#248): the new pr-review bot, plus the OAuth-transport + sync
updates to the issue-triage bot. CI-only files, identical to main.

* fix(ci): lazy-load the Anthropic SDK so the OAuth path needs no SDK

The OAuth transport installs only the Claude Code CLI, not @anthropic-ai/sdk,
but both scripts had a static top-level 'import Anthropic from
@anthropic-ai/sdk' β€” evaluated at module load regardless of transport β€” so
the review job crashed with ERR_MODULE_NOT_FOUND before running (passed my
local test only because the SDK was left installed there). Moved the import
into a dynamic import() inside reviewViaSdk/classifyViaSdk, reached only on
the API-key fallback. Verified by running the OAuth path with node_modules
removed.

* feat(ci): reshape ClawReview into an informative mascot (not a reviewer)

Per direction: ClawReview should be a knowledgeable, friendly crab mascot that
greets and orients on issues/PRs β€” NOT a code reviewer racing CodeRabbit.

PR bot (pr-review.mjs):
- Dropped severity findings (P1/P2/P3) and pass/fail verdicts β€” that was the
  CodeRabbit-racing part. Schema is now summary + kind + touches + neutral
  'highlights' (helpful heads-ups, never bug reports) + duplicate hint.
- System prompt recast: 'you are the mascot, not a reviewer; CodeRabbit does
  the line-by-line; produce orientation, never verdicts'. Empty highlights is
  normal and encouraged.
- Comment is now: greeting -> plain-language summary -> 'At a glance'
  (kind/touches/base/surface + deterministic policy heads-ups) -> optional
  'Good to know' -> sign-off that explicitly defers to CodeRabbit.

Issue bot (issue-triage.mjs):
- Rebranded the triage comment as the same crab mascot (one character across
  issues + PRs): friendly greeting + summary + at-a-glance labels + next step.
- Added DRY_RUN (prints the comment, skips label writes) for local testing.

Deterministic policy checks stay β€” they're genuine ClawBox-specific value
(beta-first, bun.lock, sensitive paths) CodeRabbit can't provide β€” but are
now framed as friendly context, not a verdict. Validated end-to-end via the
OAuth CLI transport on PR #227 and issue #232.

* fix(ci): apply CodeRabbit review β€” app-token if-guard + shared backend

CodeRabbit's 4 actionable findings on the bots:

1+2. app-token step's `if: env.HAS_APP` (both workflows) referenced env the
     step sets on ITSELF β€” a step's if can't see its own step-level env, so
     the App token would NEVER mint even with the secrets set. Hoisted
     HAS_APP to job-level env (visible to the step if), and scoped the minted
     token to only the permissions each bot needs (pull-requests+issues for
     review, issues for triage) instead of the full installation grant.

3.   parseModelJson + the two transports (CLI/SDK) were duplicated across
     pr-review.mjs and issue-triage.mjs. Extracted to scripts/lib/
     ai-backend.mjs (callClaude); both bots import it, so the transport stays
     in sync. This is the 'first SDK-shape change' trigger the earlier reuse
     analysis set for extraction.

4.   Static @anthropic-ai/sdk import breaking the OAuth path β€” already fixed
     (lazy import); the shared backend now owns the sole lazy import, so
     pr-review.mjs no longer imports the SDK at all.

Validated via the OAuth CLI transport with node_modules removed: both bots
produce their mascot output through the shared backend.

* fix(ci): address CodeRabbit re-review β€” tolerant SDK parse + read-only dry-run

- ai-backend.mjs: viaSdk now uses parseModelJson (tolerant of fenced/wrapped
  JSON) instead of a bare JSON.parse, matching the CLI path.
- issue-triage.mjs: DRY_RUN now gates the whole label block (ensure() +
  gh issue edit), not just the edit β€” a dry run is fully read-only.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant