Skip to content

feat(ci): auto-approve low-risk fix and refactor PRs - #3544

Draft
baktun14 wants to merge 4 commits into
mainfrom
feat/ci-auto-approve-fix-refactor-prs
Draft

feat(ci): auto-approve low-risk fix and refactor PRs#3544
baktun14 wants to merge 4 commits into
mainfrom
feat/ci-auto-approve-fix-refactor-prs

Conversation

@baktun14

@baktun14 baktun14 commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Why

The auto-approval workflow only ever approved chore/test/docs PRs, and even those approvals were mostly decorative: the protected3 ruleset requires a code-owner review and CODEOWNERS blanket-owned /apps/*/src/ and /packages/*/src/, so any code-touching PR needed a human regardless. This implements the mechanically-checkable subset of the team's Slack rules for AI-driven tasks (size cap, task categorization, risk map, migrations/public API need humans) as a two-layer system:

  1. CODEOWNERS becomes the risk map — only genuinely critical paths stay owned, so GitHub natively enforces human review exactly where the rules demand it, and everything else becomes bot-approvable.
  2. The approval action loosensfix (with regression-test evidence) and refactor PRs up to size: M from experienced-contributor authors auto-approve.

What

Behavior changes

Loosening

  • fix PRs (must add/update at least one test file) and refactor PRs ≤ size: M now auto-approve and become mergeable without a human on un-owned paths.
  • Most src paths (components, services, indexer, stats-web, most packages) lose the hard code-owner gate for all PRs — feat/XL/untrusted PRs there now need any-collaborator approval instead of specifically a code owner (accepted trade-off; in practice reviewers are the same team).
  • The test: scope check additionally accepts .test./.e2e. suffixes and playwright.config.*.

Intentional tightening

  • apps/*/drizzle/, apps/*/env/, and all Dockerfile* become code-owned. Today they are un-owned and chore-approvable, i.e. zero-human mergeable — this closes that gap.
  • Draft PRs are never auto-approved (previously a draft could be).
  • Stale bot approvals are dismissed when a re-evaluation stops qualifying (the ruleset has dismiss_stale_reviews_on_push: false, so the action cleans up after itself), with a sticky comment explaining why.
  • Qualifying PRs that touch owned paths get an explanatory sticky comment instead of a decorative approval.

Mechanics

  • Engine extracted to .github/scripts/pr-auto-approval/ (CommonJS, zero-dependency, tested with node:test): a gitignore-style CODEOWNERS parser (the action parses .github/CODEOWNERS as the single source of truth for its owned-path blocker — no duplicated path list), a pure evaluate() decision engine with 4 outcomes (ineligible/blocked/skipped/approved), and a sticky-comment upserter.
  • Workflow gains workflow_dispatch with pr-number + dry-run (default true) as a replay harness; the decision JSON is always logged.
  • associated-pr action gains a draft output.
  • github-actions-lint.yml runs the script tests; knip.json registers the scripts as entries.
  • All PR-controlled values flow through env:, never ${{ }} in script bodies.

Deviations & notes for reviewers

  • *.test.js naming deviates from the repo's *.spec.ts convention — it's the node --test discovery default and these files are outside vitest/tsc scope (.github/** is also outside the codecov patch gate).
  • The legacy chore scope check (f.includes("/apps/")) never matched because listFiles paths have no leading slash; the port fixes this latent bug (/^(apps|packages)\//), so chores touching code are now correctly skipped.
  • This PR is deliberately atomic: CODEOWNERS + parser + workflow move together because codeowners.test.js pins the real file's policy semantics (billing owned, components un-owned, spec files un-owned, drizzle owned).
  • workflow_run executes the main-branch engine, so a PR can never approve itself with modified rules; this PR touches .github/** (owned) and requires console-team review.
  • Known accepted gap: title-only edits and draft-conversion don't re-run CI, so re-evaluation/dismissal waits for the next push.
  • Rollback: revert the workflow file (scripts become inert); reverting CODEOWNERS restores the old human gate independently.

Verification

  • 53 unit tests green (node --test ".github/scripts/**/*.test.js"), actionlint clean, zizmor findings identical to the pre-change baseline, npx knip --workspace . unchanged vs main.
  • Branch workflow_dispatch dry-runs replayed real historical PRs; every decision matched the file evidence:
PR Type/size Expected Decision log
#3411 fix, S, deploy-web components + spec approved approved: bug fix within approved scope that includes test changes
#3260 docs, XS approved approved: all changes are related to documentation
#3526 fix, M, touches apps/api/src/billing/ + apps/tx-signer/ blocked blocked: code-owned-paths
#3262 chore, S, removes .helm/ files blocked blocked: code-owned-paths
#2798 refactor, L ineligible ineligible: missing required size label (XS, S, or M)
#3253 refactor, S, drizzle dep upgrade (src + manifests only) approved approved: refactor within approved scope

Post-merge follow-up: run a scratch fix: PR without a test to empirically confirm comment/dismissal permissions (pull-requests: write should suffice; fallback is adding issues: write), then monitor the first organic approvals for a week and tune CODEOWNERS as needed.

Summary by CodeRabbit

  • New Features

    • Added automated pull request approval with eligibility checks, policy validation, and clear approval or blocking reasons.
    • Added optional dry-run and manual PR evaluation support.
    • Added synchronized status comments describing blockers, dismissals, or successful resolution.
    • Expanded ownership rules for critical business and public API areas.
  • Tests

    • Added comprehensive coverage for approval rules, ownership matching, and status comments.
  • Chores

    • Automated validation now runs script tests during workflow linting.

Comment thread .github/scripts/pr-auto-approval/codeowners.js Fixed
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e6c80079-3e23-442c-82cb-e21d2ada4b89

📥 Commits

Reviewing files that changed from the base of the PR and between 6e78b7f and cf3a1a2.

📒 Files selected for processing (8)
  • .github/CODEOWNERS
  • .github/scripts/pr-auto-approval/codeowners.test.js
  • .github/scripts/pr-auto-approval/rules.js
  • .github/scripts/pr-auto-approval/rules.test.js
  • .github/scripts/pr-auto-approval/sticky-comment.js
  • .github/scripts/pr-auto-approval/sticky-comment.test.js
  • .github/workflows/github-actions-lint.yml
  • .github/workflows/pr-auto-approval.yml
🚧 Files skipped from review as they are similar to previous changes (6)
  • .github/scripts/pr-auto-approval/rules.test.js
  • .github/workflows/github-actions-lint.yml
  • .github/scripts/pr-auto-approval/codeowners.test.js
  • .github/workflows/pr-auto-approval.yml
  • .github/CODEOWNERS
  • .github/scripts/pr-auto-approval/rules.js

📝 Walkthrough

Walkthrough

The PR adds centralized auto-approval rules, CODEOWNERS matching, manual workflow dispatch, draft detection, stale approval dismissal, sticky comments, and Node.js test execution.

Changes

PR auto-approval

Layer / File(s) Summary
CODEOWNERS matching
.github/CODEOWNERS, .github/scripts/pr-auto-approval/codeowners.js, .github/scripts/pr-auto-approval/codeowners.test.js
Adds CODEOWNERS rules, glob matching with last-match-wins behavior, ownership clearing, and policy tests.
Approval decision rules
.github/scripts/pr-auto-approval/rules.js, .github/scripts/pr-auto-approval/rules.test.js, knip.json
Adds eligibility, scope, regression-test, and ownership checks. The evaluator returns structured approval outcomes.
Sticky decision comments
.github/scripts/pr-auto-approval/sticky-comment.js, .github/scripts/pr-auto-approval/sticky-comment.test.js
Adds creation, update, dismissal, and resolution handling for bot-owned sticky comments.
Workflow execution and review state
.github/actions/associated-pr/action.yml, .github/workflows/pr-auto-approval.yml, .github/workflows/github-actions-lint.yml
Adds draft output handling, manual replay and dry-run inputs, evaluator integration, approval gating, stale approval dismissal, sticky-comment synchronization, and script test execution.

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

Suggested reviewers: ygrishajev

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ci-auto-approve-fix-refactor-prs

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

Comment thread .github/scripts/pr-auto-approval/sticky-comment.js
Comment thread .github/workflows/github-actions-lint.yml

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (1)
.github/scripts/pr-auto-approval/rules.js (1)

43-45: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

The bot check matches human logins that contain "bot".

/bot/i matches logins such as Abbott or botanist. Those authors never receive auto-approval. Anchor the check to the GitHub bot suffix, and keep a small allowlist of known bot accounts if needed.

♻️ Proposed refinement
-  if (/bot/i.test(authorLogin)) {
+  if (/\[bot\]$/i.test(authorLogin)) {
🤖 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 @.github/scripts/pr-auto-approval/rules.js around lines 43 - 45, Update the
bot detection condition in the author-validation rule to match GitHub bot logins
by their bot suffix rather than any occurrence of “bot”; preserve an explicit
allowlist for known bot accounts if the surrounding logic requires it, while
allowing human logins such as “Abbott” and “botanist” to proceed.
🤖 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/CODEOWNERS:
- Around line 16-19: Update .github/CODEOWNERS to add ownership patterns
covering application-root and repository-root .env* files, alongside the
existing environment-directory rule, so these files require the designated human
owner. Add policy tests verifying both application-root files such as
.env.production and repository-root files such as .env.example are matched.
- Around line 8-14: Update the CODEOWNERS rule ordering so test-file exemption
patterns appear after all protected-path ownership rules, ensuring billing test
files remain owner-less under last-match semantics. Preserve the existing
protected-path owners and align the ordering with the expectation in
codeowners.test.js.

In @.github/scripts/pr-auto-approval/rules.js:
- Around line 5-6: Update TEST_FILE_PATTERN to recognize JavaScript test files
alongside TypeScript files, and match repository-relative test directories
whether they are at the root or nested. Ensure paths such as test/foo.spec.ts
and .github/scripts/pr-auto-approval/rules.test.js are classified as test files
without requiring a leading slash.

In @.github/workflows/github-actions-lint.yml:
- Around line 18-19: Update the workflow before the “Run workflow script tests”
step to add the pinned actions/setup-node action configured with
node-version-file: package.json, ensuring the tests run with the
repository-declared Node.js version.

In @.github/workflows/pr-auto-approval.yml:
- Around line 17-27: Add a GitHub Actions concurrency group for the
check-whether-to-approve job keyed by the associated pull request, and disable
cancellation of in-progress runs so concurrent evaluations for the same PR are
serialized and the latest evaluation completes.
- Around line 29-33: Update the sparse-checkout pattern under the checkout
action to use .github instead of ./.github, matching the repository-root
cone-mode pattern used by the other workflows.
- Around line 151-153: Update the review_id assignment to use gh api with
--paginate --slurp, then pipe the combined page results to jq so filtering for
the last approved github-actions[bot] review occurs across one collected array
and produces a single ID value.

---

Nitpick comments:
In @.github/scripts/pr-auto-approval/rules.js:
- Around line 43-45: Update the bot detection condition in the author-validation
rule to match GitHub bot logins by their bot suffix rather than any occurrence
of “bot”; preserve an explicit allowlist for known bot accounts if the
surrounding logic requires it, while allowing human logins such as “Abbott” and
“botanist” to proceed.
🪄 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: CHILL

Plan: Pro

Run ID: 9c29558f-54fb-45c3-aeac-9ac1101df2f6

📥 Commits

Reviewing files that changed from the base of the PR and between 1f263ad and 6e78b7f.

📒 Files selected for processing (11)
  • .github/CODEOWNERS
  • .github/actions/associated-pr/action.yml
  • .github/scripts/pr-auto-approval/codeowners.js
  • .github/scripts/pr-auto-approval/codeowners.test.js
  • .github/scripts/pr-auto-approval/rules.js
  • .github/scripts/pr-auto-approval/rules.test.js
  • .github/scripts/pr-auto-approval/sticky-comment.js
  • .github/scripts/pr-auto-approval/sticky-comment.test.js
  • .github/workflows/github-actions-lint.yml
  • .github/workflows/pr-auto-approval.yml
  • knip.json

Comment thread .github/CODEOWNERS
Comment thread .github/CODEOWNERS
Comment thread .github/scripts/pr-auto-approval/rules.js Outdated
Comment thread .github/workflows/github-actions-lint.yml
Comment thread .github/workflows/pr-auto-approval.yml
Comment thread .github/workflows/pr-auto-approval.yml Outdated
Comment thread .github/workflows/pr-auto-approval.yml Outdated

@claude claude 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.

Beyond the inline finding, this run also examined three other candidate issues and ruled them out: the CODEOWNERS test/tests exclusion carving a hole in critical-path ownership (checked — critical paths like billing/auth/tx-signer don't have test-dir carve-outs that matter in practice), workflow_dispatch bypassing the CI-success gate for real approve/dismiss actions (checked — dispatch is a manual, permissioned replay harness, not attacker-reachable), and provider-proxy's src being left unowned by the new CODEOWNERS (checked — consistent with the PR's stated scope, not a regression it introduces).

Extended reasoning...

This PR rewrites the CI auto-approval bot itself (CODEOWNERS ownership map, decision engine, and review-dismissal/sticky-comment logic) — a security/process-sensitive system that grants merge approval without human review. It is XL-sized and, per the author's own description, requires console-team review regardless of bugs found. Given the inline nit already found this run, I'm only adding a short note on other candidates that were checked and ruled out, not re-litigating the verdict.

Comment thread .github/workflows/pr-auto-approval.yml Outdated
- escape filenames in the sticky comment so a crafted PR path cannot
  break out of the markdown code span and inject a link
- clear the stale sticky comment when a blocked PR later becomes
  skipped/ineligible without a dismissal
- read review pages with --paginate --slurp so the dismissal lookup
  works when a PR has more than one page of reviews
- serialize auto-approval runs per PR via a concurrency group
- run the workflow script tests on .github/scripts/** changes and pin
  the repo Node version for them
- own .env* files wherever they live, not only under env/
- count JavaScript test files and repo-root test dirs as test evidence
- normalize the sparse-checkout pattern to .github

@claude claude 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.

Beyond the two inline nits from this run, I also checked whether the CODEOWNERS test-file exemption (*.spec.ts, **/test/, etc.) could let a file with a test-like name but real production logic bypass both code-owner review and the rules.js code-owned-paths blocker (since both consume the same CODEOWNERS parse). That exemption predates this PR — it already excluded spec files from ownership before this change — and exploiting it still requires an already-trusted experienced-contributor author, so I did not flag it as a new issue introduced by this diff.

Extended reasoning...

This is the narrow ruled-out-note exception: no new review body issues beyond the inline nits already posted. I independently re-derived the CODEOWNERS test-exemption bypass concern flagged by finder agents (a file like apps/api/src/billing/x.spec.ts clears ownership via the pre-existing '*.spec.ts' unowned pattern, and the same CODEOWNERS parse is reused by rules.js's code-owned-paths blocker) and confirmed it is not new to this PR: the spec-file exclusion from CODEOWNERS existed prior to this change, and reaching it still requires the same trusted-author/size/type gates as every other finding on this PR. Recording this here so it is not re-explored from scratch in a later pass.

Comment thread .github/scripts/pr-auto-approval/rules.js Outdated
Comment thread .github/workflows/pr-auto-approval.yml
- concurrency group now keys on workflow_run.pull_requests[0].number
  (falling back to the dispatch input) so all pushes to a PR serialize,
  not just re-runs of one commit
- SOURCE_CODE_PATTERN matches .mjs/.cjs for parity with the widened
  TEST_FILE_PATTERN, so a chore PR editing eslint configs is skipped
  rather than auto-approved

@claude claude 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.

No new bugs found in this pass — the issues I previously flagged (stale-comment leak, sparse-checkout, pagination truncation, concurrency grouping, .mjs/.cjs chore-scope gap) are all fixed in 73945a1 and cf3a1a2. This PR is still worth a human look before merge: it rewrites the auto-approval gating logic itself (CODEOWNERS risk-map + evaluate()/rules engine), so a subtle bug here directly weakens repo-wide review coverage, and per the PR's own notes .github/** is code-owned and requires console-team review anyway.

Beyond the inline findings from earlier rounds, this pass also checked and ruled out: the workflow_dispatch PR-resolution path omitting a merged_by fallback (not applicable — dispatch always resolves a live, unmerged-context PR via pulls.get, unlike the commit-association lookup that needs the fallback), the concurrency group collapsing for fork-originated workflow_run events (moot — fork PRs are rejected by findIneligibilityReason before any mutating step runs, so there's nothing to race), and the fork check being bypassed when the source fork is deleted (the fork/same-repo detection happens off webhook payload metadata at dispatch time, not by querying the fork itself, so deletion doesn't change the outcome).

Extended reasoning...

Overview

PR overhauls the CI auto-approval workflow (pr-auto-approval.yml) and its supporting engine (.github/scripts/pr-auto-approval/{rules,codeowners,sticky-comment}.js), and rewrites .github/CODEOWNERS to serve as the risk map that the engine consults. It also adds a workflow_dispatch dry-run replay path and wires script tests into github-actions-lint.yml.

Security risks

This code is a review-bypass control: a logic bug here can let an unreviewed PR merge into main. The engine reads all PR-controlled values (title, labels, filenames) through env: rather than interpolating into script bodies, avoiding the classic github-script injection pattern. Earlier rounds (mine and CodeRabbit's) found and got fixed real issues in this area — a markdown/link-injection vector in the sticky-comment filenames, a concurrency-group key that didn't actually serialize per-PR, and pagination truncation on the reviews lookup — all resolved in 73945a1 and cf3a1a2 with regression tests.

Level of scrutiny

High — this is exactly the kind of change the guidelines call out for deferral: it's XL-sized, touches CI gating/security-sensitive logic, and the PR description itself acknowledges .github/** is code-owned and needs console-team sign-off. Automated review clearing zero new bugs doesn't lower the bar here given the blast radius of a mistake (silently widening what auto-merges).

Other factors

53 unit tests across the three new modules, actionlint/zizmor clean, and dry-run replays against six real historical PRs matching expected decisions per the PR description. Two full rounds of review (CodeRabbit + this system) already surfaced and got fixed several concrete bugs, which is a good signal the harness is working, but also underscores this is intricate enough to warrant a human's final pass rather than resting on a clean automated run.

@baktun14
baktun14 marked this pull request as draft July 31, 2026 19:07
Comment thread .github/CODEOWNERS

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

thought: it's still too broad. My idea was to identify critical flows - they will always require human, even if it's a fix or refactor. refactor of stripe related logic IMO must have human review.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion: would be nice to delegate this completely to github and enforce it by github, not by our script

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

what is the purpose of this script?

core.setOutput("head_repo", pr.head.repo?.full_name || "");
core.setOutput("labels", JSON.stringify((pr.labels || []).map(l => l.name)));
core.setOutput("closed", pr.closed_at ? "true" : "false");
core.setOutput("draft", pr.draft ? "true" : "false");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

question: could you please explain why associated-pr cannot provide this outputs?

@stalniy

stalniy commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

CI related things cannot be feat -> it's chore because it doesn't bring any new functionality to the product. it helps to maintain the product

@stalniy

stalniy commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

My idea was to categorize changes by tiers and process them in this way:

Tier Rule Who approves
low CI green only bot
medium CI green and CodeRabbit APPROVED on head SHA bot
high always human nobody auto

instead of code rabbit or additionally we could use claude because coderabbit can be rate limited

Comment thread .github/CODEOWNERS
/packages/*/src/ @akash-network/console
/config @akash-network/console

# Critical business flows

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion: managed wallet API should also be a part of business critical flow

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants