Skip to content

feat(skills/merge-gate): add PR Review Advisor as a hard gate in check-gates.ts#5597

Closed
prekshivyas wants to merge 1 commit into
NVIDIA:mainfrom
prekshivyas:fix/merge-gate-pra-check
Closed

feat(skills/merge-gate): add PR Review Advisor as a hard gate in check-gates.ts#5597
prekshivyas wants to merge 1 commit into
NVIDIA:mainfrom
prekshivyas:fix/merge-gate-pra-check

Conversation

@prekshivyas

@prekshivyas prekshivyas commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds a fifth gate (prAdvisor) to check-gates.ts that fetches the PR Review Advisor sticky comment, parses the recommendation: field from its embedded HTML metadata, and sets allPass: false when the value is blocked
  • Fails closed on API errors (empty response or invalid JSON) — consistent with how the CodeRabbit gate behaves
  • Updates triage.ts with a comment making it explicit that CodeRabbit and PRA are both skipped there for performance, and that a merge-now bucket assignment does not mean check-gates.ts can be skipped
  • Updates MERGE-GATE.md: removes the "manual review step" caveat, documents the gate as automated, and removes a wrong claim that the advisor link appears in gates.prAdvisor.details

Motivation

PR #5526 was approved despite the PR Review Advisor posting a Blocked status with two required fixes. The advisor check was documented as "manual" in MERGE-GATE.md, making it easy to skip. Making it a programmatic gate means allPass will be false on any blocked advisor comment, preventing the approval flow from proceeding.

Test evidence

$ node --experimental-strip-types --no-warnings .agents/skills/nemoclaw-maintainer-day/scripts/check-gates.ts 5526
{
  "prAdvisor": {
    "pass": false,
    "details": "PR Review Advisor: blocked (2 required item(s))",
    "recommendation": "blocked",
    "openRequired": 2
  },
  "allPass": false
}

A PR with no PRA comment returns "pass": true, "details": "No PR Review Advisor comment found".

Test plan

  • Run check-gates.ts against a PR where the advisor is blocked — prAdvisor.pass should be false and allPass should be false
  • Run check-gates.ts against a PR with no advisor comment — prAdvisor.pass should be true
  • Run check-gates.ts against a PR where the advisor is approvedprAdvisor.pass should be true

Signed-off-by: Preksha Vyas prekshiv@nvidia.com

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores
    • Updated the merge gate workflow to add an explicit “PR Review Advisor not blocked” check.
    • PRs are now blocked from approval when PR Review Advisor recommends “blocked,” even if other checks pass.
  • Documentation
    • Refined PR triage guidance to clarify that PR Review Advisor enforcement happens in the merge gate step (not during early PR classification).

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

A new "Gate 4: PR Review Advisor not blocked" is added to the merge gate system. check-gates.ts gains a PrAdvisorGateResult type, a checkPrAdvisor() function that fetches PR issue comments via gh CLI and regex-parses the latest PRA comment for a blocked recommendation, and wiring into main(). MERGE-GATE.md documents the new gate and its blocking behavior. triage.ts is clarified to state that PR Review Advisor evaluation is deferred to the hard gate in check-gates.ts.

Changes

PR Review Advisor Merge Gate

Layer / File(s) Summary
checkPrAdvisor gate: type, implementation, and main() wiring
.agents/skills/nemoclaw-maintainer-day/scripts/check-gates.ts
Module description updated to 5 gates; PrAdvisorGateResult extends GateResult with optional recommendation and openRequired fields; GateOutput extended with prAdvisor; checkPrAdvisor() fetches issue comments via gh, locates the latest nemoclaw-pr-review-advisor comment, regex-extracts recommendation metadata, fails closed on API/JSON errors, and returns pass/fail; wired into main() to include prAdvisor.pass in allPass and serialize result in gates output.
MERGE-GATE.md documentation and triage.ts role clarification
.agents/skills/nemoclaw-maintainer-day/MERGE-GATE.md, .agents/skills/nemoclaw-maintainer-day/scripts/triage.ts
Gate 4 entry added describing PR Review Advisor not-blocked auto-evaluation; Step 1 updated to reference all gates in JSON output; Step 3 enhanced with explicit handling bullet for blocked advisor findings tied to gates.prAdvisor.pass and allPass; triage.ts clarified that CodeRabbit/PR Review Advisor are not evaluated in classification and must be enforced by check-gates.ts before approval.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • NVIDIA/NemoClaw#5545: Updates the maintainer Merge Gate documentation to treat PR Review Advisor findings as an approval-blocking gate, aligning workflow behavior around the same gate concept.
  • NVIDIA/NemoClaw#5572: Changes how PR Review Advisor sticky comments are generated and enriched with recommendation/structured fields that Gate 4's regex parsing directly reads.
  • NVIDIA/NemoClaw#5584: Refactors PR Review Advisor comment formatting and metadata fields in tools/pr-review-advisor/comment.mts, directly affecting the comment structure that Gate 4's regex parsing parses.

Suggested labels

area: skills, documentation

Suggested reviewers

  • jyaunches
  • cv

Poem

🐇 A gate has been added, hooray hooray!
The advisor's verdict now holds the day.
If blocked it cries in comment's embrace,
allPass turns false, no merge shall take place.
Four gates now stand where three stood before—
The rabbit keeps watch at the PR door! 🚪

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly summarizes the main change: adding PR Review Advisor as a hard gate in check-gates.ts, which is the primary objective of the PR.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 markdownlint-cli2 (0.22.1)
.agents/skills/nemoclaw-maintainer-day/MERGE-GATE.md

markdownlint-cli2 v0.22.1 (markdownlint v0.40.0)
Error: Unable to use configuration file '/coderabbit-0.markdownlint-cli2.jsonc'; ENOENT: no such file or directory, open '/coderabbit-0.markdownlint-cli2.jsonc'
at throwForConfigurationFile (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:48:9)
at readOptionsOrConfig (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:169:5)
at async main (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:927:21)
at async file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2-bin.mjs:14:22 {
[cause]: Error: ENOENT: no such file or directory, open '/coderabbit-0.markdownlint-cli2.jsonc'
at async open (node:internal/fs/promises:640:25)
at async Object.readFile (node:internal/fs/promises:1287:14)
at async readOptionsOrConfig (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:141:17)
at async main (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:927:21)
at async file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2-bin.mjs:14:22 {
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: '/coderabbit-0.markdownlint-cli2.jsonc'
}
}


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

@prekshivyas
prekshivyas force-pushed the fix/merge-gate-pra-check branch 2 times, most recently from f2e3ae2 to 390f488 Compare June 22, 2026 18:50
@prekshivyas
prekshivyas requested a review from cv June 22, 2026 18:51
@prekshivyas prekshivyas self-assigned this Jun 22, 2026

@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: 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 @.agents/skills/nemoclaw-maintainer-day/MERGE-GATE.md:
- Line 32: The documentation in MERGE-GATE.md at line 32 references a link that
should be available in gates.prAdvisor.details, but the check-gates.ts script
does not actually emit any URL in that field. Either update the check-gates.ts
script to include the actual comment URL when populating gates.prAdvisor.details
output, or remove the "(linked in `gates.prAdvisor.details`)" phrase from the
MERGE-GATE.md documentation to match the current implementation. Ensure that
what the documentation promises matches what the code actually delivers.

In @.agents/skills/nemoclaw-maintainer-day/scripts/check-gates.ts:
- Around line 275-283: The praComments filter on line 275-283 only checks if the
comment body contains the text "nemoclaw-pr-review-advisor" without validating
that the comment was actually posted by the PR Review Advisor bot account
itself. This allows any user to spoof a bot recommendation by posting a comment
with that marker text. Modify the filter condition to also check that the
comment author is the actual PR Review Advisor bot account (by verifying the
user or author property of the comment object), ensuring that only authentic bot
comments are accepted for gate validation.
🪄 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: Enterprise

Run ID: dc3aad8e-0754-4671-9905-1bb84c922129

📥 Commits

Reviewing files that changed from the base of the PR and between c256f8f and 237ab6d.

📒 Files selected for processing (2)
  • .agents/skills/nemoclaw-maintainer-day/MERGE-GATE.md
  • .agents/skills/nemoclaw-maintainer-day/scripts/check-gates.ts

Comment thread .agents/skills/nemoclaw-maintainer-day/MERGE-GATE.md Outdated
Comment on lines +275 to +283
const praComments = allComments.filter((c) =>
(c.body ?? "").includes("nemoclaw-pr-review-advisor"),
);
if (praComments.length === 0) {
return { pass: true, details: "No PR Review Advisor comment found" };
}

// Use the last PRA comment (most recent re-run)
const body = praComments[praComments.length - 1].body ?? "";

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Gate is spoofable because PRA detection trusts marker text without author validation.

Line 275-283 accepts any latest comment containing nemoclaw-pr-review-advisor. A non-bot commenter can post that marker with recommendation: approved and override a prior blocked bot comment.

Suggested fix
-  let allComments: Array<{ body?: string }>;
+  let allComments: Array<{ body?: string; user?: { login?: string; type?: string } }>;
@@
-  const praComments = allComments.filter((c) =>
-    (c.body ?? "").includes("nemoclaw-pr-review-advisor"),
-  );
+  const PRA_BOT_LOGIN = "nemoclaw-pr-review-advisor[bot]";
+  const praComments = allComments.filter((c) =>
+    (c.body ?? "").includes("nemoclaw-pr-review-advisor") &&
+    c.user?.login === PRA_BOT_LOGIN,
+  );
🤖 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 @.agents/skills/nemoclaw-maintainer-day/scripts/check-gates.ts around lines
275 - 283, The praComments filter on line 275-283 only checks if the comment
body contains the text "nemoclaw-pr-review-advisor" without validating that the
comment was actually posted by the PR Review Advisor bot account itself. This
allows any user to spoof a bot recommendation by posting a comment with that
marker text. Modify the filter condition to also check that the comment author
is the actual PR Review Advisor bot account (by verifying the user or author
property of the comment object), ensuring that only authentic bot comments are
accepted for gate validation.

…k-gates.ts

Previously the gate checker only checked CI, conflicts, CodeRabbit, and
risky-code coverage. The PR Review Advisor status was documented as a
manual review step, which allowed advisor-blocked PRs to slip through.

Add a fifth gate that fetches the PRA sticky comment, parses the
`recommendation:` metadata from its embedded HTML comment, and fails
`allPass` when `recommendation: blocked`. Update MERGE-GATE.md to
reflect that the gate is now automated.

Signed-off-by: Preksha Vyas <prekshiv@nvidia.com>
@prekshivyas
prekshivyas force-pushed the fix/merge-gate-pra-check branch from 390f488 to 8e012dc Compare June 22, 2026 19:03

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

🤖 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 @.agents/skills/nemoclaw-maintainer-day/scripts/check-gates.ts:
- Around line 286-295: After parsing the JSON in the try block, add a type check
to verify that allComments is actually an array before calling the filter
method. If the parsed JSON is not an array (such as when GitHub returns an error
object), return the fail-closed response with an appropriate error message
instead of allowing the code to proceed to the filter call, which would throw an
error on a non-array value. Add this validation immediately after the JSON.parse
assignment and before the filter call on allComments.
🪄 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: Enterprise

Run ID: e95009b3-e6ca-4959-85b5-0999ab0afe74

📥 Commits

Reviewing files that changed from the base of the PR and between 390f488 and 8e012dc.

📒 Files selected for processing (3)
  • .agents/skills/nemoclaw-maintainer-day/MERGE-GATE.md
  • .agents/skills/nemoclaw-maintainer-day/scripts/check-gates.ts
  • .agents/skills/nemoclaw-maintainer-day/scripts/triage.ts
✅ Files skipped from review due to trivial changes (1)
  • .agents/skills/nemoclaw-maintainer-day/scripts/triage.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • .agents/skills/nemoclaw-maintainer-day/MERGE-GATE.md

Comment on lines +286 to +295
let allComments: Array<{ body?: string }>;
try {
allComments = JSON.parse(raw) as Array<{ body?: string }>;
} catch {
return { pass: false, details: "Could not parse PR comments (invalid JSON — fail-closed)" };
}

const praComments = allComments.filter((c) =>
(c.body ?? "").includes("nemoclaw-pr-review-advisor"),
);

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle non-array API payloads to avoid a hard crash.

Line 286-295 assumes parsed JSON is always an array. If GitHub returns a JSON error object (valid JSON), allComments.filter(...) throws and bypasses fail-closed behavior.

Proposed fix
-  let allComments: Array<{ body?: string }>;
+  let allComments: Array<{ body?: string; user?: { login?: string } }>;
   try {
-    allComments = JSON.parse(raw) as Array<{ body?: string }>;
+    const parsed = JSON.parse(raw) as unknown;
+    if (!Array.isArray(parsed)) {
+      return { pass: false, details: "Could not parse PR comments (unexpected JSON shape — fail-closed)" };
+    }
+    allComments = parsed as Array<{ body?: string; user?: { login?: string } }>;
   } catch {
     return { pass: false, details: "Could not parse PR comments (invalid JSON — fail-closed)" };
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let allComments: Array<{ body?: string }>;
try {
allComments = JSON.parse(raw) as Array<{ body?: string }>;
} catch {
return { pass: false, details: "Could not parse PR comments (invalid JSON — fail-closed)" };
}
const praComments = allComments.filter((c) =>
(c.body ?? "").includes("nemoclaw-pr-review-advisor"),
);
let allComments: Array<{ body?: string; user?: { login?: string } }>;
try {
const parsed = JSON.parse(raw) as unknown;
if (!Array.isArray(parsed)) {
return { pass: false, details: "Could not parse PR comments (unexpected JSON shape — fail-closed)" };
}
allComments = parsed as Array<{ body?: string; user?: { login?: string } }>;
} catch {
return { pass: false, details: "Could not parse PR comments (invalid JSON — fail-closed)" };
}
const praComments = allComments.filter((c) =>
(c.body ?? "").includes("nemoclaw-pr-review-advisor"),
);
🤖 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 @.agents/skills/nemoclaw-maintainer-day/scripts/check-gates.ts around lines
286 - 295, After parsing the JSON in the try block, add a type check to verify
that allComments is actually an array before calling the filter method. If the
parsed JSON is not an array (such as when GitHub returns an error object),
return the fail-closed response with an appropriate error message instead of
allowing the code to proceed to the filter call, which would throw an error on a
non-array value. Add this validation immediately after the JSON.parse assignment
and before the filter call on allComments.

@prekshivyas prekshivyas removed their assignment Jun 22, 2026
@prekshivyas
prekshivyas requested a review from jyaunches June 22, 2026 19:12
@prekshivyas

Copy link
Copy Markdown
Contributor Author

Closing in favour of a PR directly from the main repo branch.

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.

2 participants