fix(buds): stop unrelated PRs leaking onto PROD / UAT tabs - #193
Merged
Conversation
The release-stage tabs (PROD, UAT) were showing every open PR raised to
the configured branch on any impacted repo — including PRs linked to a
different BUD entirely. Root cause was the OR-of-equalities predicate
in ``list_open_for_bud_with_repo`` which let ``repo_id IN impacted``
bypass the BUD link.
Two coordinated fixes:
1. **Broaden the BUD-NNN resolver.** ``extract_bud_number`` now scans
PR titles in addition to head branches, with a negative-lookbehind
regex that rejects mid-word and run-on matches (``auth-bud-7``,
``abcbud-5``) while accepting the punctuation real titles use
(``Closes #BUD-4``, ``fix,BUD-3``, ``revert: BUD-21 cleanup``). The
webhook handler now also processes ``pull_request.edited`` events
gated on ``changes.title``, so retitling an orphan PR to
``[BUD-008] X`` relinks it on the next webhook. Old + new BUD both
get audit timeline events. The duplicate regex in ``mcp.handlers_hooks``
is removed in favour of the single seam.
2. **Tighten the stage-tab query.** ``list_open_for_bud_with_repo`` now
uses ``bud_id == X OR (bud_id IS NULL AND repo_id IN impacted)``.
The IS-NULL arm preserves aggregate release PRs (``develop → main``)
that legitimately carry no single owning BUD; PRs linked to a
different BUD on the same impacted repo no longer leak.
Adds ``scripts.backfill_pr_bud_links`` to re-link existing orphan PRs
using the broadened resolver. Defaults to open PRs only; an explicit
``--include-merged`` opts in to merged-PR backfill behind a CONFIRM
prompt because the next release-detection walk can fire new
``merged_to_{stage}`` timeline events and auto-close BUDs that were
previously orphaned in the release chain.
Tests: 5 parametrized-accept and 8 parametrized-reject cases pin the
matcher; predicate-shape tests pin the three-way SQL form.
Signed-off-by: Arun Rajkumar <mickyarunr@gmail.com>
7 tasks
The previous over-match fix preserved aggregate release PRs by keeping ``bud_id IS NULL`` rows on impacted repos visible — but that pulled in unrelated PRs to ``main`` / ``release/*`` that simply happened to share a repo with the BUD (e.g. an ATOA-9396 PR appearing on BUD-004's PROD tab even though it has no BUD reference at all). Real semantics: a PR belongs on a BUD's release-stage tab only when its head ref or title carries that BUD's number. Multi-BUD release branches like ``release/bud-001-bud-004-bud-007`` should surface on EACH referenced BUD's tab, not on every impacted-repo BUD. Adds two helpers in ``pr_auto_transition``: - ``extract_all_bud_numbers(text)`` returns every BUD number found. - ``pr_references_bud(num, head, title)`` is the call site. Loosens the matcher's left lookbehind so digit-then-hyphen prefixes are allowed (``-bud-`` after a digit is the multi-BUD chain pattern), while keeping letter-then-hyphen prefixes rejected (``auth-bud-7`` mid-word case is still rejected). The release-stage filter in ``bud_prs.py`` now applies the content guard to any PR that isn't directly linked to this BUD — directly linked PRs short-circuit so a user-edited link wins over text matching. Tests pin the multi-BUD branch parser and the user's exact reported case (``release/bud-001-bud-004`` matches BUD-1 and BUD-4 but not BUD-7; unrelated ``ATOA-9396`` PR no longer leaks onto BUD-4's tab). Signed-off-by: Arun Rajkumar <mickyarunr@gmail.com>
The edited webhook handler only synced ``title`` — base-branch and head-branch changes from GitHub were silently dropped. When a PR was retargeted from ``main`` to ``develop`` (the BUD-004 / PR-1997 case), the local row kept ``base_branch = main`` and the release-stage filter kept matching the PR onto the PROD tab because ``branch_matches`` runs against the stale value. Two coordinated changes: * Dispatcher widens the ``edited`` action gate. We now act when the payload's ``changes`` carries ``title`` OR ``base`` — the two fields that affect either the BUD link or the release-stage filter. Body / label edits stay silent. * Handler also assigns ``pr.base_branch`` and ``pr.head_branch`` from the latest pr_data on every invocation. Cheap, and prevents the same drift on future force-pushes or base-branch edits. Does NOT retroactively fix already-stale rows: an operator-triggered sync script or a webhook replay is needed for those. The unit test also pins that the loose space-form title ``Bud 004/processing loader`` does NOT satisfy ``pr_references_bud`` on its own; the head branch ``bud-004/...`` is the reliable link source. Signed-off-by: Arun Rajkumar <mickyarunr@gmail.com>
Code review of the post-merge fixes flagged two real regressions and one auto-transition risk: * The relaxed regex ``(?<![A-Za-z])(?<![A-Za-z]-)`` accepted ``1bud-3``, ``sha7bud-5``, ``v2bud-5``, and ``auth1-bud-7`` — digit-glued mid-word and prefix-with-trailing-digit shapes that are not real BUD references. Revert to the strict ``(?<![A-Za-z0-9-])`` prefix that uniformly rejects letters, digits, and hyphens on the left. Multi-BUD release branches are now handled by a SEPARATE chain matcher: ``extract_all_bud_numbers`` finds each strict ``bud-NNN`` match and then walks forward through any ``-bud-NNN-bud-NNN`` tail. Six new parametrize-reject cases pin the digit-glued and trailing-digit-prefix rejections. * ``_handle_pr_edited`` previously called ``resolve_bud_from_pr`` on every invocation. Now that the dispatcher also fires on base edits, unconditional re-resolution risked unlinking a PR whose title carried a once-accepted-now-rejected reference (the regex tightening is exactly that kind of contract change). Gate the relink to actual title changes; base / head edits still sync ``pr.base_branch`` and ``pr.head_branch`` but do not touch ``pr.bud_id``. Auto-transition audit confirmed clean: * ``check_all_repos_have_prs`` / ``check_all_prs_merged`` key off ``pr.bud_id``, not the text content — the matcher tightening only affects NEW PR-open / title-edit links, not existing rows. * The development → code_review threshold is unaffected by base edits because the gate above blocks the only path that could have unlinked an existing link on a base-only edit. Full backend suite green: 1887 passed. Signed-off-by: Arun Rajkumar <mickyarunr@gmail.com>
mickyarun
force-pushed
the
fix/bud-pr-linking
branch
from
June 3, 2026 06:25
2092b55 to
8a09762
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The release-stage tabs (PROD, UAT) on BUD detail were showing every open PR raised to the configured branch on any impacted repo — including PRs linked to a different BUD entirely. The DEVELOPMENT tab was unaffected because it filters strictly by
bud_id.Two coordinated fixes restore the intended behaviour:
1. Broaden the BUD-NNN resolver
extract_bud_numberregex:(?<![A-Za-z0-9-])bud-?0*(\d+)\b(case-insensitive). Negative lookbehind rejects mid-word (auth-bud-7) and run-on (abcbud-5) matches while accepting the punctuation real PR titles use:Closes #BUD-4,fix,BUD-3,revert: BUD-21 cleanup,[BUD-12],feat/BUD-7-rename,BUD-8 fix x.resolve_bud_from_prconsults head branch first, then title so manually-named PRs whose author tagged the BUD in the title get linked.pull_request.editedwebhook action is now handled (gated onchanges.title) so retitling an orphan PR to[BUD-008] Xrelinks it. Old and new BUD both record audit timeline events (pr_unlinked/pr_linked) for symmetry, and both get a re-check ofcheck_all_repos_have_prsso the development → code_review auto-advance stays consistent._BUD_BRANCH_REinapp/mcp/handlers_hooks.pyis removed; both paths now flow through the singleextract_bud_numberseam.2. Tighten the stage-tab query
list_open_for_bud_with_repochanges fromOR(bud_id == X, repo_id IN impacted)to:The
IS NULLarm preserves aggregate release PRs (develop → main) that legitimately carry no single owning BUD — those still flow through. PRs linked to a different BUD on the same impacted repo no longer leak.3. One-shot backfill
scripts.backfill_pr_bud_linksre-links existing orphan PRs via the broadened resolver. Defaults to--skip-merged(only OPEN PRs touched). Passing--include-mergedopens a runtime CONFIRM prompt because the next release-detection walk uses the SHA→BUD map and can fire newmerged_to_{stage}events / auto-close orphaned BUDs.--dry-runto preview.--yesfor non-interactive runs.Test plan
cd backend && pytest tests/services/test_pr_auto_transition.py tests/repositories/test_pull_request_open_for_bud_predicate.py— 29 cases pass (parametrized accept/reject + SQL predicate shape).python -m scripts.backfill_pr_bud_links --dry-runon prod, eyeball the mapping diff.pull_request.edited: open a PR without a BUD reference, retitle to[BUD-008] X, confirm the PR appears under BUD-008 on next webhook.Verification snippets