Gemini Review Integrator
Integrate Gemini Code Assist inline review comments into the canonical pr-review-toolkit PR review comment.
Contract
Use .pr-review-cache/pr-#.json as the only PR review state file. Do not create extra cache files or extra PR comments. Do not commit, push, merge, or directly call gh api to update the canonical review comment.
This skill integrates external Gemini feedback only. It is not a review producer, resolver, or fixer:
- It does not run
codex-review-pass. - It does not ask the user to resolve findings.
- It does not modify source files.
- It preserves Claude, Codex, and existing Gemini issues.
Find the toolkit root in this order:
Use
PR_REVIEW_TOOLKIT_ROOTwhen set. This is the supported path.If
PR_REVIEW_TOOLKIT_ROOTis unset, derive the packaged plugin root from the skill path. This SKILL.md lives at<root>/codex/skills/<skill-name>/SKILL.md, so<root>is exactly three levels up. EnsureSKILL_PATHis set in the environment to the absolute path of this SKILL.md before running the snippet:: "${SKILL_PATH:?SKILL_PATH must be set to the absolute path of this SKILL.md}" PR_REVIEW_TOOLKIT_ROOT="$(cd "$(dirname "$SKILL_PATH")/../../.." && pwd)"Verify both
<root>/.codex-plugin/plugin.jsonand<root>/scripts/cache-write-comment.shexist. If either is missing, treat derivation as failed and proceed to step 3.Stop and ask the dev agent for
PR_REVIEW_TOOLKIT_ROOT.
Canonicalize the root before using helper scripts:
PR_REVIEW_TOOLKIT_ROOT="$(cd "$PR_REVIEW_TOOLKIT_ROOT" && pwd)"
Use only these scripts for review state:
"${PR_REVIEW_TOOLKIT_ROOT}/scripts/get-pr-number.sh"
"${PR_REVIEW_TOOLKIT_ROOT}/scripts/fetch-gemini-comments.sh"
"${PR_REVIEW_TOOLKIT_ROOT}/scripts/cache-read-comment.sh"
"${PR_REVIEW_TOOLKIT_ROOT}/scripts/cache-write-comment.sh"
"${PR_REVIEW_TOOLKIT_ROOT}/scripts/cache-sync.sh"
"${PR_REVIEW_TOOLKIT_ROOT}/scripts/extract-content-hash.sh"
"${PR_REVIEW_TOOLKIT_ROOT}/scripts/disambiguate-stale-source.sh"
"${PR_REVIEW_TOOLKIT_ROOT}/scripts/review-metadata-upgrade.sh"
"${PR_REVIEW_TOOLKIT_ROOT}/scripts/review-metadata-replace.sh"
Before running the workflow, verify these helper scripts are executable and scripts/lib/common.sh is readable.
Workflow
Get the PR number with
get-pr-number.sh.Read the canonical review comment with
cache-read-comment.sh "$PR_NUMBER":set +e EXISTING_CONTENT=$("${PR_REVIEW_TOOLKIT_ROOT}/scripts/cache-read-comment.sh" "$PR_NUMBER") rc=$? set -e case $rc in 0) ;; 2) echo "No canonical review comment found. Run pr-review-and-document first." >&2; exit 2 ;; *) echo "cache-read-comment.sh failed with exit $rc" >&2; exit "$rc" ;; esaccache-read-comment.shexits2if no canonical comment exists — there is no need for a separatefind-review-comment.shprecheck (it would return empty stdout with rc=0 and silently look like success).Extract the cache
content_hashfor CAS via the shared helper. The helper does file-existence check, jq read with rc capture, regex validation, and triggerscache-sync.shon any failure (with rc propagation and post-recovery validation):set +e EXPECTED_CONTENT_HASH=$("${PR_REVIEW_TOOLKIT_ROOT}/scripts/extract-content-hash.sh" "$PR_NUMBER") rc=$? set -e case $rc in 0) ;; 2) echo "Cache was refreshed; re-read EXISTING_CONTENT and retry from Step 2." >&2; exit 2 ;; *) exit "$rc" ;; esacUnit-tested in
tests/extract-content-hash-test.sh.Fetch Gemini comments with explicit error handling. The script can fail for several reasons (gh auth expired, GitHub 5xx, network timeout, malformed PR) — collapsing those into "no Gemini comments" silently masks integration failures:
set +e GEMINI_RAW=$("${PR_REVIEW_TOOLKIT_ROOT}/scripts/fetch-gemini-comments.sh" "$PR_NUMBER") gemini_rc=$? set -e if [ "$gemini_rc" -ne 0 ]; then echo "Error: fetch-gemini-comments.sh failed (rc=$gemini_rc). Stopping integration; the canonical comment is unchanged." >&2 exit "$gemini_rc" fi if ! printf '%s' "$GEMINI_RAW" | jq -e 'type == "array"' >/dev/null 2>&1; then echo "Error: fetch-gemini-comments.sh returned non-array JSON. Stopping integration." >&2 exit 2 fi GEMINI_COMMENTS="$GEMINI_RAW"An empty array (
[]) is a valid "no Gemini comments yet" result and must be reported distinctly from a fetch failure.Filter comments:
- Exclude
is_outdated: true. - Prefer existing metadata
review_sources.gemini.consumed_comment_ids. - Fall back to legacy
gemini_integrated_ids. - Exclude IDs already consumed.
- Exclude
Categorize new Gemini comments:
Gemini priority Category high+is_securitycriticalhighimportantmedium+is_securityimportantmediumsuggestionlowsuggestionUnknown priority values must not be silently dropped. Categorize them as
suggestionand add a non-canonical follow-up note naming the unknown priority so it can be triaged later.Format each new Gemini issue. The outer markdown example uses four backticks so the inner
suggestionfence (three backticks) is not consumed as the closing delimiter:<details> <summary><b>N. ⚠️ [Gemini] Issue title</b></summary> **Source:** Gemini Code Assist **File:** `path/to/file.ts:42` **Gemini Comment ID:** `123456789` **Problem:** ... **Suggested Fix:** ```suggestion code here ``` </details>Preserve Gemini's suggestion block when present. Keep content concise enough that the final PR comment stays below GitHub limits.
Insert new Gemini issues into the canonical severity sections:
### 🔴 Critical Issues### 🟡 Important Issues### 💡 Suggestions
Renumber affected issue sections and update summary counts. Preserve existing issue statuses.
Upgrade metadata to schema
1.1:METADATA_JSON=$(printf '%s\n' "$EXISTING_CONTENT" \ | "${PR_REVIEW_TOOLKIT_ROOT}/scripts/review-metadata-upgrade.sh" \ --stdin --last-writer gemini-review-integrator)Dual-write Gemini metadata during the compatibility window:
- Add newly consumed integer IDs to
review_sources.gemini.consumed_comment_ids. - Add the same IDs to legacy
gemini_integrated_ids. - Set both
review_sources.gemini.last_integrated_atand legacygemini_integration_dateto the current UTC timestamp. - Set
last_writer/skillas appropriate forgemini-review-integrator. - Preserve
review_sources.codex,review_sources.claude,[Codex]issues, and untagged Claude issues. - Keep
review_roundunchanged. Gemini integration is not a review producer.
- Add newly consumed integer IDs to
Replace the hidden metadata block with
review-metadata-replace.sh. The script requires a metadata JSON file path; pipe the comment over stdin:METADATA_FILE=$(mktemp) trap 'rm -f "$METADATA_FILE"' EXIT printf '%s' "$METADATA_JSON" > "$METADATA_FILE" UPDATED_CONTENT=$(printf '%s\n' "$EXISTING_CONTENT" \ | "${PR_REVIEW_TOOLKIT_ROOT}/scripts/review-metadata-replace.sh" \ --stdin --metadata-file "$METADATA_FILE")Write through
cache-write-comment.sh --stdin "$PR_NUMBER" --expected-content-hash "$EXPECTED_CONTENT_HASH":printf '%s\n' "$UPDATED_CONTENT" \ | "${PR_REVIEW_TOOLKIT_ROOT}/scripts/cache-write-comment.sh" \ --stdin "$PR_NUMBER" --expected-content-hash "$EXPECTED_CONTENT_HASH"Handle
cache-write-comment.shexit codes (seecache-write-comment.sh:22-25):0: success.1: covers two distinct failure modes — disambiguate via the shared helper:set +e "${PR_REVIEW_TOOLKIT_ROOT}/scripts/disambiguate-stale-source.sh" "$PR_NUMBER" disambig_rc=$? set -e case $disambig_rc in 1) exit 1 ;; # Nominal: recovery advice on stderr; follow it. 10) echo "Cannot disambiguate cache-write-comment.sh exit 1 cause; manual intervention required." >&2; exit 10 ;; *) echo "disambiguate-stale-source.sh exited unexpectedly (rc=$disambig_rc)" >&2; exit "$disambig_rc" ;; esacUnit-tested in
tests/disambiguate-stale-source-test.sh.2: local error; abort.3: remote is newer; re-fetch with${PR_REVIEW_TOOLKIT_ROOT}/scripts/cache-sync.sh "$PR_NUMBER"(it does a force-refresh internally), then redo Steps 2-13 against the fresh content.4: CAS hash mismatch. Re-run Step 2 (cache-read-comment.sh) to refresh$EXISTING_CONTENT, re-run Step 3 to recapture and re-validateEXPECTED_CONTENT_HASH, re-merge only the new Gemini integrations into the newer content, and retry once. If the retry also exits4, reportCAS conflict: another writer holds the lockwith the current content hash and stop.
Gemini Comment Handling
fetch-gemini-comments.sh returns JSON entries with:
{
"id": 123456789,
"priority": "high",
"is_security": false,
"is_outdated": false,
"file": "path/to/file.ts",
"line": 42,
"body": "full comment body",
"suggestion": "code suggestion if any",
"created_at": "2026-01-26T10:00:00Z"
}
Outdated comments have is_outdated: true and must be skipped because the code has changed. ID-based deduplication is the source of truth for repeated integration runs.
Status Indicators
New Gemini issues start as ⚠️ pending review. Later resolver runs may mark them:
✅ Fixed⏭️ Deferred⏭️ N/A⏭️ Duplicate🔴escalated/blocking
Output Contract
End with:
Gemini Review Integration Complete:
- PR: #123
- Found Gemini comments: X
- Outdated skipped: Y
- Already integrated skipped: Z
- Newly integrated: W
- Critical: A
- Important: B
- Suggestions: C
- Comment URL: ...
Review state:
- Cache: .pr-review-cache/pr-123.json
- Metadata schema: 1.1
- review_round: unchanged
If there are no new Gemini comments, report that the canonical review comment is already up to date and do not rewrite it unless metadata migration is explicitly needed.
Source: marxbiotech/pr-review-toolkit — distributed by TomeVault.