Skip to content

Use API award capacity in queue health#472

Open
jtc268 wants to merge 1 commit into
ramimbo:mainfrom
jtc268:codex/b406-small-fix-191629
Open

Use API award capacity in queue health#472
jtc268 wants to merge 1 commit into
ramimbo:mainfrom
jtc268:codex/b406-small-fix-191629

Conversation

@jtc268
Copy link
Copy Markdown

@jtc268 jtc268 commented May 26, 2026

Refs #406

Summary

  • Make live scripts/pr_queue_health.py --repo ... read MergeWork bounty capacity from the public bounty API.
  • Preserve the existing GitHub-only fallback when the API is unavailable.
  • Add regression coverage for an open GitHub bounty issue whose API-backed awards_remaining is 0.

Why

The runbook says queue health reports closed or exhausted bounty references before maintainer batches. Before this change, live mode only looked at GitHub issue state and treated every open bounty issue as having one payable slot, so an open but exhausted bounty could be missed.

Evidence

  • Live bounty preflight: GET https://api.mrwk.ltclab.site/api/v1/bounties/66 returned issue #406, status open, awards_remaining: 15, available_mrwk: "750".
  • Live attempts preflight: GET /api/v1/bounties/66/attempts?include_expired=false returned no warnings and no active attempts.
  • Open PR search found no existing queue-health award-capacity PR; existing MRWK bounty: useful bug reports and small fixes, round 5 #406 queue-health work only covered /claim #N reference parsing.

Validation

  • uv run --extra dev python -m pytest tests/test_pr_queue_health.py -q -> 10 passed
  • uv run --extra dev ruff check scripts/pr_queue_health.py tests/test_pr_queue_health.py -> passed
  • uv run --extra dev ruff format --check scripts/pr_queue_health.py tests/test_pr_queue_health.py -> passed
  • uv run --extra dev python -m mypy app scripts/pr_queue_health.py -> success
  • git diff --check -> clean

Summary by CodeRabbit

  • New Features

    • Fetches and applies external bounty metadata to live queue health, synchronizing bounty state and remaining awards.
  • Bug Fixes

    • Falls back gracefully on malformed or non-UTF8 API responses.
    • Deduplicates API entries, preserving the first seen record for a given issue.
  • Tests

    • Added coverage for API-sourced award capacity, non-UTF8 fallback, and duplicate-entry handling.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 256dd1b0-e988-477c-bd48-8117759ff7cb

📥 Commits

Reviewing files that changed from the base of the PR and between a8319aa and 89b5f78.

📒 Files selected for processing (2)
  • scripts/pr_queue_health.py
  • tests/test_pr_queue_health.py

📝 Walkthrough

Walkthrough

Adds MergeWork API fetching to pr_queue_health: defines DEFAULT_API_HOST, implements _load_api_bounties(repo, api_host), integrates API data into load_live_queue (overriding bounty state and awards_remaining), and adds tests for API capacity, non-UTF8 fallback, and duplicate-row handling.

Changes

API bounty metadata integration

Layer / File(s) Summary
API bounty fetching and contract
scripts/pr_queue_health.py
Adds urllib imports and DEFAULT_API_HOST; implements _load_api_bounties() to HTTP-GET the MergeWork API, validate the JSON is a list, filter rows to the requested repo with integer issue_number, deduplicate by issue number (keeping first), and return a map keyed by issue number with title, state (from status, default "open"), and awards_remaining.
Live queue enrichment and tests
scripts/pr_queue_health.py, tests/test_pr_queue_health.py
load_live_queue() now attempts _load_api_bounties() (falls back to empty map on RuntimeError) and uses API entries to override each bounty issue's state and awards_remaining; tests added for zero-capacity (marks not payable/closed), non-UTF8 API response fallback, and preserving the first API row when duplicates exist.

Possibly related PRs

  • ramimbo/mergework#324: Earlier changes to load_live_queue() bounty collection and tests that this PR extends by adding API-provided award capacity.
🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Use API award capacity in queue health' clearly names the main change: integrating API-based award capacity into the queue health script.
Description check ✅ Passed The description follows the template with all required sections (Summary, Evidence, Validation), includes the bounty reference (Refs #406), documents the problem, validation steps, and test evidence checklist completion.
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.
Mergework Public Artifact Hygiene ✅ Passed No investment, price, cash-out, fabricated payout claims, or private security details found. Code only adds API bounty capacity integration with public endpoint.
Bounty Pr Focus ✅ Passed Diff matches stated files with focused API bounty capacity integration, three regression tests, and no unrelated scope detected.

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


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

Copy link
Copy Markdown

@er1c-cartman er1c-cartman left a comment

Choose a reason for hiding this comment

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

Reviewed current head a8319aad13366ff4fbffb596be60f329041463a6 for the #447 review bounty.

I inspected scripts/pr_queue_health.py, tests/test_pr_queue_health.py, and the existing queue analysis path. The change is scoped: live mode still gathers PRs/issues through gh, then overlays only the bounty state and award capacity from the public MergeWork API when that API is available. If the API is unavailable or malformed, the existing GitHub-only heuristic is preserved.

Checks I walked through:

  • API status values are lowercase (open) while GitHub issue state is uppercase (OPEN); _is_open_bounty() normalizes both.
  • API awards_remaining: 0 flows through analyze_queue() and marks a referenced bounty as not payable, which is the behavior this patch needs.
  • API failures intentionally fall back to the previous GitHub issue-state behavior.
  • Live _load_api_bounties("ramimbo/mergework") returned current rows for #406, #447, and #459, including current award capacity.

Validation:

  • PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 ~/.local/bin/uv run --extra dev python -m pytest tests/test_pr_queue_health.py -q -> 10 passed
  • PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 ~/.local/bin/uv run --extra dev ruff check scripts/pr_queue_health.py tests/test_pr_queue_health.py -> passed
  • PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 ~/.local/bin/uv run --extra dev ruff format --check scripts/pr_queue_health.py tests/test_pr_queue_health.py -> 2 files already formatted
  • git diff --check origin/main...HEAD -> clean
  • live API readback: #406 open/15 awards, #447 open/8 awards, #459 open/10 awards, 76 repo rows

Note: I disabled external pytest plugin autoload because this workstation has a ROS Humble pytest plugin on the global path; the project test itself passed after isolating third-party pytest plugins.

No blocker found in this scoped review.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f060d644-00ae-4bf7-9e31-256f5f86a337

📥 Commits

Reviewing files that changed from the base of the PR and between d8532d4 and a8319aa.

📒 Files selected for processing (2)
  • scripts/pr_queue_health.py
  • tests/test_pr_queue_health.py

Comment thread scripts/pr_queue_health.py
Comment thread tests/test_pr_queue_health.py
Copy link
Copy Markdown

@adliebe adliebe left a comment

Choose a reason for hiding this comment

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

Reviewed PR #472 at current head a8319aad13366ff4fbffb596be60f329041463a6.

I found one fallback-path blocker before this should merge. The PR intends to preserve the GitHub-only fallback when the MergeWork API is unavailable or malformed, but _load_api_bounties() currently does not wrap UnicodeDecodeError from response.read().decode("utf-8"). That means a bad/non-UTF-8 API response bypasses the RuntimeError contract and load_live_queue() does not reach its except RuntimeError: api_bounties = {} fallback.

Concrete repro:

class BadUtf8Response:
    def __enter__(self): return self
    def __exit__(self, *args): return None
    def read(self): return b"\xff\xfe"

pr_queue_health.urlopen = lambda url, timeout: BadUtf8Response()
pr_queue_health._load_api_bounties("ramimbo/mergework")

Observed result:

UnicodeDecodeError
'utf-8' codec can't decode byte 0xff in position 0: invalid start byte

The same bad response through load_live_queue("ramimbo/mergework") also raises UnicodeDecodeError, so the fallback is not preserved for that malformed-response class. A simulated URLError("temporary outage") does fall back correctly and produced closed_bounty_references == 0, open_bounties == 1 in my local harness.

Recommended fix: catch UnicodeDecodeError alongside OSError, URLError, and json.JSONDecodeError, then add a regression test for API fallback behavior. Keep the test fixture using Refs #406; a backticked Refs \#406`form does not match the currentBOUNTY_REF_RE`.

Validation I ran on the current head:

  • .venv\Scripts\python.exe -m pytest tests\test_pr_queue_health.py -q -> 10 passed
  • .venv\Scripts\ruff.exe check scripts\pr_queue_health.py tests\test_pr_queue_health.py -> All checks passed
  • .venv\Scripts\ruff.exe format --check scripts\pr_queue_health.py tests\test_pr_queue_health.py -> 2 files already formatted
  • .venv\Scripts\python.exe -m mypy app scripts\pr_queue_health.py -> Success, no issues found in 31 source files
  • .venv\Scripts\python.exe -m pytest -q -> 415 passed
  • git diff --check origin/main...HEAD -> clean

The successful API overlay path looks good; the failure/fallback boundary just needs the decode case covered.

Copy link
Copy Markdown
Contributor

@GHX5T-SOL GHX5T-SOL left a comment

Choose a reason for hiding this comment

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

Reviewed current head a8319aad13366ff4fbffb596be60f329041463a6.

I found one additional blocker separate from the existing UTF-8/fallback comments. _load_api_bounties() collapses API rows into a dict by issue_number, but it overwrites an existing entry every time it sees the same issue. The live endpoint currently returns newest rows first (limit=10 starts with ids 76, 75, 74...), so if the API ever contains two bounty rows for the same repo issue, an older/stale row later in the response wins over the newer/current row. That can make load_live_queue() report an open/payable bounty as closed or exhausted.

Local repro against the submitted code:

p.urlopen = lambda url, timeout: response_with([
  {"repo":"ramimbo/mergework","issue_number":406,"title":"new","status":"open","awards_remaining":5},
  {"repo":"ramimbo/mergework","issue_number":406,"title":"old","status":"paid","awards_remaining":0},
])
print(p._load_api_bounties("ramimbo/mergework"))

Observed output:

{406: {'number': 406, 'title': 'old', 'state': 'paid', 'awards_remaining': 0}}

That is the stale row, even though the current/newer row appeared first. I would either keep the first row for a repo/issue pair after filtering, or explicitly compare a stable API field such as id/created_at if this endpoint does not guarantee ordering. A regression test with duplicate API rows would catch the current overwrite behavior.

Other validation I ran on this head:

  • /home/kali/money/mergework/.venv/bin/python -m pytest tests/test_pr_queue_health.py -q -> 10 passed
  • /home/kali/money/mergework/.venv/bin/python -m ruff check scripts/pr_queue_health.py tests/test_pr_queue_health.py -> passed
  • /home/kali/money/mergework/.venv/bin/python -m ruff format --check scripts/pr_queue_health.py tests/test_pr_queue_health.py -> 2 files already formatted
  • git diff --check upstream/main...HEAD -> clean
  • diff gitleaks scan -> no leaks found
  • live scripts/pr_queue_health.py --repo ramimbo/mergework --format json completed; current API scan returned 76 rows and no duplicate (repo, issue_number) pairs at review time

Copy link
Copy Markdown
Contributor

@GHX5T-SOL GHX5T-SOL left a comment

Choose a reason for hiding this comment

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

Reviewed current head a8319aad13366ff4fbffb596be60f329041463a6.

I am requesting changes because the new API fallback is incomplete. The PR says live queue health preserves the GitHub-only fallback when the MergeWork API is unavailable, and load_live_queue() only falls back when _load_api_bounties() raises RuntimeError. However _load_api_bounties() decodes the response before json.loads, and UnicodeDecodeError is not caught or wrapped. A malformed/non-UTF-8 API response therefore aborts live queue collection instead of falling back to GitHub-derived bounty state.

Local reproduction against this head:

UnicodeDecodeError 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte

The repro monkeypatched urlopen() to return b'\xff' and monkeypatched the gh calls to return one open PR referencing open bounty #406. Expected behavior for an unavailable/unusable API response is the same GitHub-only fallback used for URLError; actual behavior is an uncaught decode exception.

Suggested fix: include UnicodeDecodeError in the _load_api_bounties() wrapper and add a regression test for the fallback path. A focused test should prove that when the API raises URLError or returns undecodable bytes, load_live_queue() still returns the GitHub-derived open bounty with no closed-bounty reference.

Validation I ran:

uv run --extra dev python -m pytest tests/test_pr_queue_health.py -q
# 10 passed

uv run --extra dev python -m pytest -q
# 415 passed

uv run --extra dev python scripts/docs_smoke.py
# docs smoke ok

uv run --extra dev ruff check scripts/pr_queue_health.py tests/test_pr_queue_health.py
# All checks passed!

uv run --extra dev ruff format --check scripts/pr_queue_health.py tests/test_pr_queue_health.py
# 2 files already formatted

uv run --extra dev python -m mypy app scripts/pr_queue_health.py
# Success: no issues found in 31 source files

git diff --check origin/main...HEAD
# clean

gitleaks detect --no-banner --redact --source . --log-opts origin/main..HEAD --exit-code 1
# no leaks found

Live readback using this branch also confirmed the success path is wired to the public API: #406 reads as open with 15 awards remaining, #447 as open with 8, and #459 as open with 10. The blocker is specifically the fallback/error path.

@jtc268 jtc268 force-pushed the codex/b406-small-fix-191629 branch from a8319aa to 89b5f78 Compare May 27, 2026 07:42
@jtc268
Copy link
Copy Markdown
Author

jtc268 commented May 27, 2026

Updated current head to address the review blockers:

  • API response decode failures now wrap UnicodeDecodeError and preserve the GitHub-only fallback path.
  • Duplicate API rows for the same repo issue now keep the first/current row instead of letting a later stale row overwrite it.
  • Added regressions for both cases.

Validation on current head:

  • PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 uv run --extra dev python -m pytest tests/test_pr_queue_health.py -q -> 12 passed
  • PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 uv run --extra dev python -m pytest -q -> 417 passed
  • PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 uv run --extra dev python scripts/docs_smoke.py -> docs smoke ok
  • uv run --extra dev ruff check scripts/pr_queue_health.py tests/test_pr_queue_health.py -> passed
  • uv run --extra dev ruff format --check scripts/pr_queue_health.py tests/test_pr_queue_health.py -> already formatted
  • uv run --extra dev python -m mypy app scripts/pr_queue_health.py -> success
  • git diff --check -> clean

Copy link
Copy Markdown
Contributor

@GHX5T-SOL GHX5T-SOL left a comment

Choose a reason for hiding this comment

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

Approved current head 89b5f78b344d8f110a5f9af766bf558b96a77ce7.

This is a current-head follow-up to my earlier changes-requested reviews on old head a8319aad13366ff4fbffb596be60f329041463a6, not a new bounty claim.

I verified both blockers are fixed:

  • _load_api_bounties() now wraps UnicodeDecodeError, so malformed/non-UTF-8 API responses preserve the GitHub-only fallback path instead of aborting load_live_queue().
  • Duplicate API rows for the same (repo, issue_number) now keep the first/current row instead of allowing a later stale row to overwrite open/payable state.

Current-head validation from /home/kali/money/mergework-pr472-review:

  • PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 /home/kali/money/mergework/.venv/bin/python -m pytest tests/test_pr_queue_health.py -q -> 12 passed
  • PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 /home/kali/money/mergework/.venv/bin/python -m pytest -q -> 417 passed
  • /home/kali/money/mergework/.venv/bin/python -m ruff check scripts/pr_queue_health.py tests/test_pr_queue_health.py -> passed
  • /home/kali/money/mergework/.venv/bin/python -m ruff format --check scripts/pr_queue_health.py tests/test_pr_queue_health.py -> 2 files already formatted
  • PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 /home/kali/money/mergework/.venv/bin/python -m mypy app scripts/pr_queue_health.py -> success
  • PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 /home/kali/money/mergework/.venv/bin/python scripts/docs_smoke.py -> docs smoke ok
  • git diff --check upstream/main...HEAD -> clean
  • git diff --no-ext-diff upstream/main...HEAD | gitleaks stdin --no-banner --redact --exit-code 1 -> no leaks found
  • gh pr checks 472 --repo ramimbo/mergework -> CodeRabbit pass/skipped and quality workflow pass

I also ran the live queue-health command against public GitHub/API reads and it completed with a JSON report. No wallet material, secrets, authenticated production mutation, or private data was used.

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.

4 participants