Skip to content

fix: deduplicate-by-title rate-limit guard always skips due to wrong bucket threshold - #48092

Merged
pelikhan merged 8 commits into
mainfrom
copilot/fix-deduplicate-by-title-issue
Jul 26, 2026
Merged

fix: deduplicate-by-title rate-limit guard always skips due to wrong bucket threshold#48092
pelikhan merged 8 commits into
mainfrom
copilot/fix-deduplicate-by-title-issue

Conversation

Copilot AI commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

deduplicate-by-title was silently inert on every invocation. The rate-limit guard compared resources.search.remaining against a hardcoded threshold of 500, but the search bucket's ceiling is 30 req/min — so the check always short-circuited and dedup was skipped, even with a fully unused quota.

Changes

  • create_issue.cjs: Replace the absolute constant TITLE_DEDUP_MIN_SEARCH_RATE_LIMIT_REMAINING = 500 with a fractional threshold TITLE_DEDUP_MIN_SEARCH_RATE_LIMIT_FRACTION = 0.2. The guard now reads both remaining and limit from resources.search and skips only when the remaining budget is below 20% of that bucket's own ceiling:

    // Before — 500 > 30 (max search quota), always fires
    const TITLE_DEDUP_MIN_SEARCH_RATE_LIMIT_REMAINING = 500;
    if (remaining <= TITLE_DEDUP_MIN_SEARCH_RATE_LIMIT_REMAINING) { /* skip */ }
    
    // After — proportional to the bucket's own limit
    const TITLE_DEDUP_MIN_SEARCH_RATE_LIMIT_FRACTION = 0.2;
    const { remaining, limit } = response?.data?.resources?.search ?? {};
    const threshold = limit * TITLE_DEDUP_MIN_SEARCH_RATE_LIMIT_FRACTION;
    if (remaining <= threshold) { /* skip */ }

    This is also correct on GHES where instance admins can tune the search bucket ceiling.

  • create_issue.test.cjs: Update the default rateLimit.get mock to include limit: 30, remaining: 30 (realistic search bucket values), and update the "skip when low" test case to supply both fields.


run: https://github.com/github/gh-aw/actions/runs/30187302401

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 19 AIC · ⌖ 10.5 AIC · ⊞ 7.1K ·
Comment /souschef to run again


Generated by 👨‍🍳 PR Sous Chef · gpt54 · 14.5 AIC · ⌖ 8.46 AIC · ⊞ 7.1K ·
Comment /souschef to run again


Generated by 👨‍🍳 PR Sous Chef · gpt54 · 17.5 AIC · ⌖ 8.92 AIC · ⊞ 7.1K ·
Comment /souschef to run again


Generated by 👨‍🍳 PR Sous Chef · gpt54 · 10.9 AIC · ⌖ 8.52 AIC · ⊞ 7.1K ·
Comment /souschef to run again

The TITLE_DEDUP_MIN_SEARCH_RATE_LIMIT_REMAINING = 500 threshold was
compared against resources.search.remaining, which has a max of 30/min.
This made the guard always trigger, silently skipping dedup on every run.

Replace the absolute constant with TITLE_DEDUP_MIN_SEARCH_RATE_LIMIT_FRACTION = 0.2
and compare remaining/limit proportionally. Also read both remaining and limit
from the search resource so the check stays correct on GHES where admins
can tune ceilings.

Fixes #48070

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix deduplicate-by-title rate-limit guard issue fix: deduplicate-by-title rate-limit guard always skips due to wrong bucket threshold Jul 26, 2026
Copilot AI requested a review from pelikhan July 26, 2026 03:15
@pelikhan
pelikhan marked this pull request as ready for review July 26, 2026 03:20
Copilot AI review requested due to automatic review settings July 26, 2026 03:21
@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR #48092 does not have the 'implementation' label and has 0 new lines of code in business logic directories (threshold: 100).

Copilot AI 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.

Pull request overview

Fixes title deduplication by using 20% of the search API bucket limit instead of an impossible fixed threshold.

Changes:

  • Calculates the guard from the reported search limit.
  • Updates rate-limit mocks to realistic values.
Show a summary per file
File Description
actions/setup/js/create_issue.cjs Implements proportional rate-limit guarding.
actions/setup/js/create_issue.test.cjs Updates rate-limit test fixtures.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 2/2 changed files
  • Comments generated: 3
  • Review effort level: Medium

Comment thread actions/setup/js/create_issue.test.cjs Outdated
Comment on lines 684 to 685
limit: 30,
remaining: 1,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated in c1ef37a: the low-rate test now uses a non-default bucket (limit: 35, remaining: 7) so it verifies proportional threshold behavior and catches hard-coded cutoffs.

Comment thread actions/setup/js/create_issue.cjs Outdated
Comment on lines 500 to 501
if (!Number.isFinite(remaining) || !Number.isFinite(limit)) {
core.warning(`Could not determine search rate limit remaining for ${owner}/${repo}; proceeding with repo-level title dedup search`);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated in c1ef37a: the warning now reports both parsed values by including remaining and limit in the message when either value is invalid/missing.

Comment thread actions/setup/js/create_issue.cjs Outdated

@github-actions github-actions 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.

The fix is correct and well-scoped. The old constant 500 exceeded the search bucket ceiling of 30 req/min, making the guard always fire and silently disabling dedup. The fractional threshold (0.2 x limit) is proportional, portable to GHES, and both fields are validated. Tests updated correctly.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 15.3 AIC · ⌖ 7.31 AIC · ⊞ 5K

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: N/A — No Behavioral Tests

No new or modified test functions detected. Changes are test fixture updates only (mock data adjustments to match production code refactoring).

📊 Analysis Summary
Aspect Result
Test files analyzed 1 (create_issue.test.cjs)
New test functions 0
Modified test functions 0
Test assertions modified 0
Changes detected ✅ Fixture updates only

What changed:

  • Mock response objects updated to include limit field (required by refactored shouldSkipRepoTitleDedupSearch())
  • Test fixture values adjusted from hardcoded remaining: 1000 to realistic values: limit: 30, remaining: 30 and remaining: 1
  • No test logic, assertions, or test scope changes

Verdict

passed. Fixture-only PR; no behavioral test quality scoring needed. Existing tests remain valid and now correctly match the refactored production code.

Quality Signal: Changes demonstrate deliberate fixture maintenance — test data was updated to align with the production code refactoring (threshold logic change from fixed 500 to fractional 0.2 * limit). This is sound testing hygiene.

🧪 Test quality analysis by Test Quality Sentinel · haiku45 · 12.9 AIC · ⌖ 8.71 AIC · ⊞ 8.1K ·
Comment /review to run again

@github-actions github-actions Bot mentioned this pull request Jul 26, 2026

@github-actions github-actions 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.

✅ Test Quality Sentinel: Fixture-only PR. No behavioral tests were added or modified—only mock data was updated to match the production code refactoring. Existing tests remain valid. No violations detected.

@github-actions github-actions 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.

Non-blocking: test coverage gaps for the new fractional threshold

The fix itself is correct — replacing the hardcoded 500 with a proportional 20% fraction properly reflects the actual search bucket ceiling. The code logic is sound.

Findings

Missing boundary tests (noted inline): neither the <= threshold edge nor the new !Number.isFinite(limit) code path has a dedicated test case. The only "skip" test uses remaining: 1, limit: 30 (~3%), which is far from the boundary, and the "proceed" test uses 100% — no test touches near 20%.

🔎 Code quality review by PR Code Quality Reviewer · sonnet46 · 27.1 AIC · ⌖ 4.61 AIC · ⊞ 5.7K
Comment /review to run again

@@ -46,7 +46,8 @@ describe("create_issue", () => {
data: {

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.

Missing boundary tests for the 20% threshold: the updated mock uses remaining: 30, limit: 30 (100% quota), which never exercises the guard logic introduced by this PR.

💡 Suggested additions

Add tests for:

  1. Exactly at threshold (remaining: 6, limit: 30 → 20% → should skip)
  2. Just above threshold (remaining: 7, limit: 30 → ~23% → should NOT skip)
  3. limit is undefined/missing → new !Number.isFinite(limit) branch returns false with warning — completely untested

Without these, the <= semantics and the limit-NaN guard are exercised by neither the old nor the new tests.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in c1ef37a: added coverage for boundary/guard behavior with a just-above-threshold case (35/8) and a missing-limit case that proceeds with warning and repo-level search.

@github-actions github-actions 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.

Skills-Based Review 🧠

Applied /diagnosing-bugs and /tdd — approving with one minor suggestion on boundary test coverage.

📋 Key Themes & Highlights

Root-cause fix

The bug is correctly diagnosed and fixed at the source: the hardcoded 500 constant was never a valid threshold for a 30 req/min bucket, so every invocation silently skipped dedup. The proportional approach (0.2 * limit) is semantically correct and GHES-portable.

Positive Highlights

  • ✅ Both remaining and limit are now validated with Number.isFinite, guarding against partial API responses
  • ✅ Warning message now shows remaining/limit ratio, which will make future diagnostics much easier
  • ✅ Test default mock updated to realistic values (limit: 30, remaining: 30) — this was the right move
  • ✅ PR description clearly explains the before/after with a code snippet

One improvement

The skip test validates remaining: 1 (deep below threshold) but there are no boundary tests for exactly-at-threshold and just-above-threshold values. See inline comment for a concrete suggestion.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 · 29.6 AIC · ⌖ 4.73 AIC · ⊞ 6.7K
Comment /matt to run again

Comment thread actions/setup/js/create_issue.test.cjs Outdated
resources: {
search: {
limit: 30,
remaining: 1,

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.

[/tdd] The skip test uses remaining: 1 (far below the threshold) but there is no boundary test for exactly-at-threshold or just-above-threshold values, which is where off-by-one errors in <= guards typically hide.

💡 Suggested boundary tests

Add two cases:

it('skips when remaining equals 20% of limit', async () => {
  // 6/30 = 0.2 exactly — guard uses <=, so this should skip
  mockRateLimit({ limit: 30, remaining: 6 });
  expect(await shouldSkipRepoTitleDedupSearch(...)).toBe(true);
});

it('proceeds when remaining is one above threshold', async () => {
  // 7/30 > 0.2 — should NOT skip
  mockRateLimit({ limit: 30, remaining: 7 });
  expect(await shouldSkipRepoTitleDedupSearch(...)).toBe(false);
});

This pins the <= semantics and catches any future threshold change that accidentally flips the boundary.

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in c1ef37a: added a boundary-focused case above the threshold (limit: 35, remaining: 8) and updated the low-rate case to non-default bucket values to pin threshold semantics.

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

Copilot AI and others added 2 commits July 26, 2026 03:47
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

@copilot run pr-finisher skill

Completed a pr-finisher pass. Addressed in-scope review feedback in commit c1ef37a, ran local validation (make agent-report-progress), and replied to all in-scope review comments. CI on this HEAD is stale in cloud-agent mode and needs a maintainer re-trigger before merge.

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>

Copilot AI commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill, address the unresolved review feedback below, and rerun checks once the branch is up to date.

Unresolved review feedback:...

Addressed in e4c29d0. I added explicit boundary coverage for the unresolved github-actions thread (exactly at 20% skips and just above 20% proceeds), reran local checks (make agent-report-progress, make test, make recompile), and confirmed the remaining make test failure is pre-existing/unrelated (TestFallbackAWFilesMatchesLocalAWDirectory mismatch in embedded fallback list).

Copilot AI requested a review from gh-aw-bot July 26, 2026 04:38
@gh-aw-bot

Copy link
Copy Markdown
Collaborator

Copilot AI commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill, address the unresolved review comments below, and rerun checks once the branch is up to date.

Unresolved review feedback:...

I reran a pr-finisher pass. The listed unresolved review threads are already addressed by earlier commits (c1ef37a120, e4c29d03e2) with boundary coverage and rate-limit guard/message fixes in /home/runner/work/gh-aw/gh-aw/actions/setup/js/create_issue.test.cjs and /home/runner/work/gh-aw/gh-aw/actions/setup/js/create_issue.cjs.

I also reran local checks in this pass: make fmt, make lint, make test-unit passed; make test still fails on the same pre-existing unrelated integration failure (TestFallbackAWFilesMatchesLocalAWDirectory expecting release-workflow.md in fallback list). No new code changes were needed.

CI on the current head remains unverified/stale from the cloud-agent context and needs a maintainer-triggered rerun.

Copilot AI and others added 2 commits July 26, 2026 05:53
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
@gh-aw-bot

Copy link
Copy Markdown
Collaborator

Copilot AI commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill, close out the remaining unresolved review feedback if anything is still outstanding, and rerun checks once the branch is up to date.

Unresolved review fee...

I reran pr-finisher and rechecked the listed threads. There is no remaining code-level action outstanding on those items: they are already addressed by c1ef37a120 and e4c29d03e2 in /home/runner/work/gh-aw/gh-aw/actions/setup/js/create_issue.cjs and /home/runner/work/gh-aw/gh-aw/actions/setup/js/create_issue.test.cjs.

I reran checks in this pass:

  • make fmt
  • make lint
  • make test-unit
  • make test ❌ (same pre-existing unrelated failure: TestFallbackAWFilesMatchesLocalAWDirectory fallback-list mismatch)
  • make recompile

I also reviewed the referenced run (30191227976) and branch workflow runs via MCP logs/listing; no new failed jobs were reported for that sous-chef run.

CI on the current HEAD remains stale/unverified from cloud-agent pushes and needs a maintainer-triggered rerun.

@github-actions

Copy link
Copy Markdown
Contributor

🤖 PR Triage — §30192186368

Field Value
Category bug
Risk 🟢 low
Score 56 / 100 (impact 28 + urgency 10 + quality 18)
Action ⚡ fast_track

Why: Fixes #48070deduplicate-by-title rate-limit guard always skipped dedup because the threshold (500) exceeded the search bucket ceiling (30). Copilot check passing, bot-approved, clean focused fix (+77/-8).

Next: Fast-track merge — CI green, low risk.

Generated by 🔧 PR Triage Agent · sonnet46 · 48.8 AIC · ⌖ 8.75 AIC · ⊞ 5.7K ·

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

Copilot AI commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill, address the unresolved review feedback below, and rerun checks once the branch is up to date.

Unresolved review feedback:...

I reran a pr-finisher pass and rechecked those threads. The Copilot and github-actions review items listed are already satisfied in the branch (c1ef37a120, e4c29d03e2) in /home/runner/work/gh-aw/gh-aw/actions/setup/js/create_issue.cjs and /home/runner/work/gh-aw/gh-aw/actions/setup/js/create_issue.test.cjs, including explicit 20% boundary coverage and the rate-limit warning/value handling updates.

I reran checks locally in this pass:

  • make fmt
  • make lint
  • make test-unit
  • make test ❌ (same pre-existing unrelated integration failure: TestFallbackAWFilesMatchesLocalAWDirectory)
  • make recompile

I also reviewed recent workflow runs/logs via MCP (list_workflow_runs + get_job_logs), and the referenced sous-chef run did not report failed jobs. CI for the current agent head remains unverified/stale after cloud-agent pushes and needs a maintainer re-trigger.

@pelikhan
pelikhan merged commit fd4c353 into main Jul 26, 2026
10 checks passed
@pelikhan
pelikhan deleted the copilot/fix-deduplicate-by-title-issue branch July 26, 2026 08:22
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.83.4

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.

deduplicate-by-title never runs: rate-limit guard compares the search bucket (max 30/min) against a threshold of 500

4 participants