Skip to content

fix(ops): guard against duplicate same-fix pull requests - #80

Merged
nish3451 merged 17 commits into
mainfrom
fix/pr-duplicate-guard
Aug 19, 2026
Merged

fix(ops): guard against duplicate same-fix pull requests#80
nish3451 merged 17 commits into
mainfrom
fix/pr-duplicate-guard

Conversation

@nish3451

Copy link
Copy Markdown
Collaborator

Why

Duplicate same-fix PR pairs keep appearing because the same finding is dispatched to multiple lanes:

Today these pile up silently; a human (or Grok review) has to notice by hand.

What

  • scripts/check-pr-duplicates.mjs — compares a PR's diff against every other open PR: shared changed-file coverage >= 0.8 and patch similarity >= 0.5 on the shared files ⇒ same-fix duplicate. Calibrated against all 79 open PRs on 2026-08-11: every pair above the thresholds is a genuine duplicate-cluster member (18 pairs), zero false positives; unrelated pairs (e.g. fix(ops): stop tracked live metrics surface from silently reporting a zero pipeline #73 vs fix(ops): stop tracked 11/10 proof-run brief from silently reporting a zero pipeline #45) stay below.
  • .github/workflows/pr-duplicate-guard.yml — runs on every PR event, fails the check loudly and posts one marker comment naming the duplicate(s) and the canonical PR. Informational (not a required status), so it never blocks legitimate work — it makes duplication visible the moment it happens.
  • scripts/test-pr-duplicates.mjs — unit tests (parsing, similarity, detection, comment upsert) with an injected API; wired into npm ci and npm test.
  • README documents the guard and the local pre-PR check.

Verified

Outcome for the fleet

The next time a lane opens a PR for an already-in-flight finding, the guard fails red with the canonical PR number and a close/consolidate instruction, so the orchestrator can close the duplicate instead of letting it sit silently.

@greptile-apps greptile-apps 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.

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Warning

Review limit reached

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

Next review available in: 42 minutes

Limit details: You’ve used the included review currently available.

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 within each organization.

For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Free

Run ID: 47350786-e925-4972-8ccd-74a57767377c

📥 Commits

Reviewing files that changed from the base of the PR and between 607e99e and 8da2390.

📒 Files selected for processing (5)
  • .github/workflows/pr-duplicate-guard.yml
  • README.md
  • package.json
  • scripts/check-pr-duplicates.mjs
  • scripts/test-pr-duplicates.mjs

Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login.

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

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f11f9d22f3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +219 to +221
const canonical = canonicalNumber(report.matches)
lines.push(`Same-fix duplicate open PRs detected for #${report.prNumber} (${report.currentTitle}).`)
lines.push(`Canonical: #${canonical}. Close this PR and keep the canonical one, or consolidate the fix there.`)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Include the current PR when choosing the canonical PR

When the earlier PR in a duplicate pair is synchronized, reopened, or marked ready, matches contains only the later PR because the current PR is excluded during detection. Computing the canonical number from that list therefore identifies the later PR as canonical and tells maintainers to close the actual earliest PR; for example, rerunning identical PR #10 after #11 exists chooses #11. Compare the current PR's created_at alongside the matches before issuing the close instruction.

Useful? React with 👍 / 👎.

Comment on lines +285 to +287
if (report.matches.length > 0 && args.comment) {
await upsertComment(api, owner, repo, args.pr, formatReport(report, args.repo))
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Remove stale guard comments after duplicates disappear

If a PR was previously flagged and a subsequent synchronization changes its patch so that no duplicate remains, this condition skips upsertComment entirely. The check turns green, but the old marker comment remains and still instructs maintainers to close the PR, contradicting the current result. The no-match path should remove any existing marker comment or replace it with a resolved report.

Useful? React with 👍 / 👎.

Comment thread README.md
Comment on lines +86 to +88
```bash
node scripts/check-pr-duplicates.mjs --pr 0 --repo nish3451/tinystudio-in --no-fail
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Do not advertise an unsupported pre-PR invocation

This documented command always exits with --pr NUMBER is required because argument parsing explicitly rejects --pr 0; moreover, detection expects that number to identify an open PR returned by GitHub. Consequently the advertised local-before-opening check cannot inspect anything. Document a real existing PR number, or implement a mode that compares the local diff without requiring an open PR.

Useful? React with 👍 / 👎.

Comment on lines +22 to +25
pr-number:
description: "PR number to check (defaults to the PR that triggered the run)"
required: false
type: string

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Require a PR number for manual dispatches

A workflow_dispatch run has no pull_request payload, so when this optional input is omitted the PR_NUMBER expression resolves empty and the script immediately rejects it as missing. The description's claimed fallback only exists for pull-request-triggered runs, not manual dispatches; make this input required or provide a valid manual-run default.

Useful? React with 👍 / 👎.

The fleet keeps dispatching the same finding to multiple lanes, producing
duplicate same-fix PR pairs: #36/#44 and #55/#56 are byte-identical or
near-identical patches of the operator export --help fix, #39/#49 the
offername article fix, #40/#52 the recording exporter --help fix, plus
#38/#48/#51, #30/#61/#72, #45/#64, #42/#60, #43/#54 and #46/#74.

Add a PR Duplicate Guard:
- scripts/check-pr-duplicates.mjs compares a PR's diff against every other
  open PR (shared changed-file coverage >= 0.8 and patch similarity >= 0.5).
  Calibrated on all 79 open PRs on 2026-08-11: every pair above the
  thresholds was a genuine duplicate-cluster member, zero false positives.
- .github/workflows/pr-duplicate-guard.yml runs it on every PR event and
  posts one marker comment naming the duplicate(s) and the canonical PR; the
  check fails loudly when a duplicate is found. Informational, not required.
- scripts/test-pr-duplicates.mjs covers parsing, similarity, detection, and
  comment upsert with an injected API; wired into npm ci and npm test.
@nish3451
nish3451 force-pushed the fix/pr-duplicate-guard branch from f11f9d2 to 2091c7a Compare August 11, 2026 08:56

@greptile-apps greptile-apps 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.

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2091c7a6e1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +120 to +122
const linesA = shared.flatMap(path => a.get(path))
const linesB = shared.flatMap(path => b.get(path))
return {coverage, similarity: ratio(linesA, linesB), sharedFiles: shared}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Compare only changed content when scoring similarity

Because the ratio includes diff --git, ---, +++, and hunk headers, two unrelated short edits to the same file can exceed the 0.5 threshold. For example, distinct one-line replacements at the same hunk location score 0.625 even with different index and changed-content lines, so the workflow fails and recommends closing a legitimate PR. Strip diff scaffolding or weight actual additions/removals before applying the threshold.

Useful? React with 👍 / 👎.

Comment on lines +174 to +176
for (const pr of prs) {
if (pr.number === prNumber) continue
const other = patches.get(pr.number)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restrict duplicate comparisons to the current base branch

The open-pulls response includes PRs targeting every base branch, but this loop compares them without checking pr.base.ref. When an identical patch is intentionally proposed to both the main and a release branch, each is required independently, yet this guard labels them duplicates and instructs maintainers to close one. Filter candidates to the current PR's base branch before comparing patches.

Useful? React with 👍 / 👎.

Comment on lines +203 to +204
const list = await api(`${BASE_URL}/repos/${owner}/${repo}/issues/${prNumber}/comments`)
const comments = await list.json()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Paginate marker-comment lookup before posting

The issue-comments endpoint is paginated, but this request reads only its first page. On a PR with more than one page of comments, a marker outside that page is not deleted and every duplicate-check rerun posts another marker, violating the function's promise to keep exactly one authoritative report. Request all pages, or search with explicit pagination, before posting the replacement.

Useful? React with 👍 / 👎.

@greptile-apps greptile-apps 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.

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3f45ab27d5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

with:
node-version: 20
- name: Detect same-fix duplicate open PRs
run: node scripts/check-pr-duplicates.mjs --pr "$PR_NUMBER" --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.

P1 Badge Avoid executing PR-controlled code with a write token

When a same-repository PR modifies scripts/check-pr-duplicates.mjs, the checkout loads that PR's version and this step executes it with the write-capable GITHUB_TOKEN from the job environment. Unreviewed PR code can therefore perform arbitrary pull-request API mutations; execute an immutable copy of the script from the base revision, or separate detection from a trusted privileged commenting job.

Useful? React with 👍 / 👎.

Comment on lines +119 to +122
const coverage = shared.length / minFiles
const linesA = shared.flatMap(path => a.get(path))
const linesB = shared.flatMap(path => b.get(path))
return {coverage, similarity: ratio(linesA, linesB), sharedFiles: shared}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject one-sided changed-file coverage matches

When the current PR contains an earlier PR's one-file patch plus any number of unrelated files, dividing by the smaller file count still gives 100% coverage, while similarity ignores every non-shared file. The report then marks the broader PR as a duplicate and tells maintainers to close it, potentially discarding its additional work; require coverage in both directions or otherwise account for the unmatched files before issuing that instruction.

Useful? React with 👍 / 👎.

@greptile-apps greptile-apps 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.

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 57f654a963

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +159 to +161
for (const pr of prs) {
const res = await api(`${BASE_URL}/repos/${owner}/${repo}/pulls/${pr.number}`, {accept: "application/vnd.github.v3.diff"})
patches.set(pr.number, parseDiff(await res.text()))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Avoid fetching every open PR diff on each event

With the 79 open PRs stated in this commit, this loop makes roughly 80 REST requests per invocation. GitHub limits unauthenticated REST clients to 60 requests/hour and GITHUB_TOKEN to 1,000 requests/hour per repository, so the documented tokenless local check cannot complete even once, while around 13 PR events within an hour can exhaust the workflow's shared quota and make clean PR checks fail with an API error. Batch, cache, or prefilter candidates before downloading every diff. See GitHub's primary rate-limit documentation and GITHUB_TOKEN limit.

Useful? React with 👍 / 👎.

@greptile-apps greptile-apps 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.

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2db89fd44d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

with:
node-version: 20
- name: Detect same-fix duplicate open PRs
run: node scripts/check-pr-duplicates.mjs --pr "$PR_NUMBER" --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.

P2 Badge Preserve duplicate reports for fork pull requests

In this pull_request workflow, when a fork-authored or Dependabot PR is detected as a duplicate, the standard GITHUB_TOKEN is read-only despite the requested YAML permission, so upsertComment fails with 403 while deleting or posting the marker comment. The check still turns red, but the promised report never appears on the PR; GitHub documents the fork token restriction under workflows in forked repositories. Route commenting through a trusted privileged workflow while keeping PR-controlled detection unprivileged.

Useful? React with 👍 / 👎.

@greptile-apps greptile-apps 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.

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4fbdc345a9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +206 to +207
if (comment.body.includes(COMMENT_MARKER)) {
await api(`${BASE_URL}/repos/${owner}/${repo}/issues/comments/${comment.id}`, {method: "DELETE"})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restrict marker deletion to bot-authored comments

When a contributor quotes or manually includes <!-- pr-duplicate-guard --> in a discussion comment, the next duplicate-guard run deletes that comment because this check does not verify the comment author. Since the workflow runs with pull-requests: write, only comments previously created by the guard bot should be eligible for replacement.

Useful? React with 👍 / 👎.

@greptile-apps greptile-apps 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.

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8120c4d1d6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

bestLen = len
bestA = i - len + 1
bestB = j - len + 1
} else if (len === bestLen && (bestB === -1 || j < bestB)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Make patch similarity independent of PR direction

When repeated lines create equally long candidate matches, this tie-break chooses the smaller b position without considering the a position, making ratio asymmetric. For example, ratio(["A","B"], ["B","A","C","B"]) returns 0.33 while reversing the arguments returns 0.67; because detectDuplicates always places the currently checked PR first, the same pair can fall on opposite sides of the default 0.5 threshold depending on which PR receives an event. Use a symmetric score or deterministic tie-breaking that produces the same duplicate decision in both directions.

Useful? React with 👍 / 👎.

@greptile-apps greptile-apps 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.

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e3ea807c7b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Recheck surviving matches when a peer PR closes

In the inspected pr-duplicate-guard.yml workflow, closing or merging a matched PR triggers none of these event types, so the remaining PR is never reevaluated. If the recommended canonical PR is closed without merging, the survivor retains both a failed check and a marker comment instructing maintainers to close it in favor of a PR that is no longer open; cleaning up only during later synchronizations does not help when its head remains unchanged. Add handling that reevaluates affected open peers when a PR closes, or periodically reconcile outstanding reports.

Useful? React with 👍 / 👎.

@greptile-apps greptile-apps 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.

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1a2e190133

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +83 to +85
const hits = bIndex.get(a[i]) ?? []
const next = new Map()
for (const j of hits) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Bound repeated-line similarity work

When two patches modify the same file with thousands of identical lines, this loop visits every occurrence in b for every matching line in a, making ratio quadratic and allocating a new map on each iteration. An identical 5,000-line input took about 16 seconds locally, while 10,000 lines exhausted the process before completing; because the workflow has no timeout and runs on a shared self-hosted runner, a generated or adversarial duplicate patch can monopolize or crash that runner. Cap the compared patch size or use a similarity implementation with bounded resource usage.

Useful? React with 👍 / 👎.

@greptile-apps greptile-apps 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.

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eb77414d0b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +40 to +41
- uses: actions/setup-node@v7
with:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Pin privileged workflow actions to immutable commits

Unlike the existing repo-check workflow, these steps reference mutable major-version tags. If either tag is retargeted or its upstream release is compromised, replacement code runs on the self-hosted runner; checkout also receives the workflow's write-capable token and persists credentials for subsequent steps, enabling unauthorized PR mutations or runner compromise without any change to this repository. Pin both actions to reviewed full commit SHAs, as .github/workflows/codex-ci.yml already does.

Useful? React with 👍 / 👎.

@greptile-apps greptile-apps 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.

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@greptile-apps greptile-apps 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.

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 95228e19d8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

await api(`${BASE_URL}/repos/${owner}/${repo}/issues/comments/${comment.id}`, {method: "DELETE"})
}
}
await api(`${BASE_URL}/repos/${owner}/${repo}/issues/${prNumber}/comments`, {method: "POST", body})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Wrap the comment text in the API body object

When a duplicate is found, the --comment path invoked by pr-duplicate-guard.yml passes the report string directly here, and makeApi serializes it as a JSON string. GitHub's Create an issue comment endpoint requires an object whose body property contains the comment text, so this request is rejected and the workflow never posts its duplicate report (and deletes the previous report first on reruns). Pass {body} as the request payload and update the fake API test to validate its shape.

Useful? React with 👍 / 👎.

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d8c186deb3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

let current = null
for (const line of String(text ?? "").split("\n")) {
if (line.startsWith("diff --git ")) {
const path = line.slice("diff --git a/".length).split(" b/")[0]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Parse diff paths without splitting inside filenames

When a PR touches a legal path containing the substring b/, such as foo b/one.txt, Git emits diff --git a/foo b/one.txt b/foo b/one.txt, and this split records the path as only foo. Multiple files under that prefix then overwrite the same map entry, corrupting changed-file coverage and patch similarity and potentially producing either missed duplicates or false reports. Parse the two Git path fields with quoting and separator rules instead of splitting at the first b/.

Useful? React with 👍 / 👎.

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 99030e83ca

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

let current = null
for (const line of String(text ?? "").split("\n")) {
if (line.startsWith("diff --git ")) {
const path = line.slice("diff --git a/".length).split(" b/")[0]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Include rename destinations in changed-file identity

When two PRs rename the same source file to different destinations, this keys both patches only by the old a/ path. Pure renames such as old.txt -> one.txt and old.txt -> two.txt consequently receive 100% file coverage and 60% similarity from the shared rename metadata, exceeding both defaults even though they propose incompatible destinations; the workflow then labels one a same-fix duplicate and recommends closing it. Parse and compare both source and destination paths so differently targeted renames cannot match solely through their old name.

Useful? React with 👍 / 👎.

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

Resolve package.json conflict by taking main's ci/test chains (which
added test-operator-check-strictness, test-deploy-public-site-workflow and
test-pages-release since this branch was cut) and re-inserting this PR's
test-pr-duplicates.mjs before the node --check sweep.

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

package.json conflict only after the latest main merges: take main ci/test
chains and re-insert this PR own check. Guard re-run against current main.

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@nish3451
nish3451 merged commit 0a9909b into main Aug 19, 2026
4 checks passed
nish3451 added a commit that referenced this pull request Aug 21, 2026
…main by PR #80 (duplicate guard) (#234)

The duplicate-PR guard (PR #80, merged 2026-08-19) is now live on main,
preventing the same-fix duplication pattern at the CI level. The named
duplicate pairs (#36/#44, #39/#49, #40/#52) are functionally superseded:
their underlying fixes are on main via PRs #135, #145, and #178. The
prior 2026-08-15 lane run had concluded the guard existed but was stuck
unmerged; on 2026-08-19 it landed and the item is resolved at the
root-cause level.

Verification-only run (no product or test files touched):
- PR #80 source commit 2091c7a and merge commit 0a9909b are ancestors of origin/main
- scripts/test-pr-duplicates.mjs → 'test-pr-duplicates: ok' (exit 0)
- Superseding PRs #135 (d4f3ef4), #145 (fc44b42), #178 (77f6922) all on main
- Guard workflow runs on pull_request events; not a required status, so existing work never blocks

Co-authored-by: minimax-vps <minimax-vps@nish3451.dev>
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