Skip to content

fix(github-webhook): close four ownership-parsing leaks found in review (BLO-20886) - #1076

Open
allyblockcast[bot] wants to merge 2 commits into
sre/blo-20886-pr-review-wake-routingfrom
cto/blo-20886-round5-ownership-leaks
Open

fix(github-webhook): close four ownership-parsing leaks found in review (BLO-20886)#1076
allyblockcast[bot] wants to merge 2 commits into
sre/blo-20886-pr-review-wake-routingfrom
cto/blo-20886-round5-ownership-leaks

Conversation

@allyblockcast

@allyblockcast allyblockcast Bot commented Aug 6, 2026

Copy link
Copy Markdown

Thinking Path

  • Paperclip is the open source app people use to manage AI agents for work
  • The GitHub webhook receiver turns PR events into agent wakes, and an author-directed PR-review wake tells the woken agent "this is YOUR pull request, push a follow-up commit"
  • Choosing which agent to wake means deciding which issue OWNS the PR — and a PR body routinely names many issues, only one of which owns it
  • BLO-20886 was filed because that choice fell through to an arbitrary Related: backlink, handing an agent a directive to push to a human contributor's branch
  • fix(github-webhook): route PR-review author wakes to the owning issue, not an arbitrary Related: backlink (BLO-20886) #962 fixed the tier ordering; Ally's round-5 review then found four ways the parser still lets a PR declare an owner that no reader of the PR can see, or lose the owner it did declare
  • This pull request closes all four, plus a fifth defect that the fix for the first one would otherwise have armed
  • The benefit is that ownership is decided only from text that actually renders, so an agent is never told to push to a PR that is not theirs

Linked Issues or Issue Description

Why this is not on #962's own branch: #962 re-entered the merge queue at 08:47Z today (position 46/47, CLEAN), so its branch is push-locked — protected branch hook declined: … are queued for merging. Dequeuing would forfeit that position and keep #999 blocked. Nothing live regresses from the ordering: the parser these fixes harden does not exist on master until #962 lands, and the four leaks fail closed in the common paths. Reversible — say the word and I will dequeue and squash them into #962 instead.

What Changed

  • Finding 1 — lowercase branch-only owners were dropped before routing. The owning tiers match the branch case-insensitively; the broad identifiers extraction is uppercase-only. A PR whose only ref is a lowercase branch resolved an owner while identifiers came back empty, so the route exited at the no_paperclip_identifier gate. Resolved owners are now unioned into the candidate set once, in a wrapper around resolveEventContext, so a later event branch cannot miss the invariant.
  • Finding 2 — a list-prefixed pseudo-closer reopened a fence. The closing check reused the opening grammar, which tolerates a list marker, so a - ``` line (fenced content under CommonMark) ended the block early. Closing now has its own grammar: marker run, then only whitespace.
  • Finding 3 — mixed space-tab indentation was not treated as code. Only literal \t and four-space prefixes matched, leaving \t, \t, \t — all four columns once tabs expand to CommonMark's 4-column stops — eligible to declare an owner. Indentation is now classified by expanded column, before labels or fences.
  • Finding 4 — house-reference labels scanned the raw body, bypassing the fence/comment/indent filtering the closing-keyword tier already had. Both extractors now iterate one shared visibleMarkdownLines scanner, so a filter cannot exist in one and not the other.
  • Branch tier now anchors to a path-segment boundary (branch start, or after /) — see Risks for why this is required rather than optional.
  • Six regression tests; four existing tests whose scaffolding depended on finding 1 corrected (see Verification).

Verification

npx vitest run server/src/__tests__/github-webhook.test.ts   # 127 passed
cd server && npx tsc --noEmit                                # clean

CI job General tests (server) covers server/src/__tests__/github-webhook.test.ts. New assertions:

test asserts
does not let a list-prefixed pseudo-closer reopen a fence (BLO-20886) finding 2
treats mixed space-tab indentation as code by expanded columns (BLO-20886) finding 3
hides house-reference labels inside code, comments and indents (BLO-21312/BLO-20886) finding 4
does not manufacture a branch owner from a version number (BLO-20886) branch anchoring
keeps a lowercase branch-only owner in the candidate identifiers (BLO-20886) finding 1, unit
delivers a lowercase branch-only author wake end-to-end (BLO-20886) finding 1, route-level

Every one was verified failing against the unfixed source before the fix (stash the two source files, re-run). The route-level test fails with exactly the reported symptom: expected 'no_paperclip_identifier' to be undefined.

All four findings were reproduced against the real parser before any edit:

# before fix
1 owning=["BLO-20886"], identifiers=[]
2 ["BLO-777"]
3 ["BLO-888"], ["BLO-889"]
4 ["BLO-111"], ["BLO-222"], ["BLO-333"]

Four existing tests changed. They used a lowercase-branch payload as "no identifier" scaffolding that was only inert because of finding 1. Two (relay-wave-0, migration-blo-4959-...) are corrected automatically by the segment anchoring and are untouched. The other two had a real ref in a real position (platform/blo-18859-...), so they now legitimately resolve; I made their branches genuinely ref-free so they keep asserting exactly what they were written to assert (reviewerWakeFired), rather than weakening an assertion to match new behaviour.

Risks

The one that mattered: fixing finding 1 would have armed a latent defect, so it is fixed here too. Tier-3 uppercases the whole branch to match the uppercase-only identifier pattern, which manufactures identifiers from ordinary words followed by a number. Across the 200 most-recently-updated PRs in this repo:

blo-21612-undici-7.29.0         -> ["BLO-21612", "UNDICI-7"]
blo-21611-fast-uri-3.1.5        -> ["BLO-21611", "URI-3"]
blo-21613-ip-address-10.3.1     -> ["BLO-21613", "ADDRESS-10"]
blo-21610-brace-expansion-5.0.9 -> ["BLO-21610", "EXPANSION-5"]
sre/blo-20867-fold-977-metrics  -> ["BLO-20867", "FOLD-977"]
BLO-19912-...-notes-on-pr-870   -> ["BLO-19912", "PR-870"]

These were inert for exactly the reason finding 1 describes — they never reached matched. Unioning owners into the candidate set would have made them live author-wake targets, handing a "push a follow-up commit" directive to whoever is assigned the same-named issue. That is this ticket's own defect, recreated by its own fix. Anchoring to a path-segment boundary (where branchTemplate puts the ref) removes it: over those same 200 branches, 192 identical, 8 differ, and in all 8 only the spurious identifier is dropped while the real BLO- one is kept — 0 real refs lost, 0 gained.

Other risks:

  • Behaviour change, intended: a lowercase branch-only PR now routes instead of being dropped. That is the point of finding 1.
  • Blast radius of the union is confined to webhook routing. The other consumer of this module, server/src/services/issue-pull-requests.ts, uses only extractPaperclipIdentifiers and resolveLinkSourceForIdentifier — neither is touched — so issue↔PR linkage is unaffected.
  • A tab-indented fence is now treated as an indented code block rather than a fence opener. This is CommonMark-correct (the following line genuinely renders), and it is what makes finding 3's fix well-defined.
  • Residual, not fixed: a branch segment that genuinely looks like a ref (feature/step-3STEP-3) still matches. Distinguishing it needs a project-prefix registry — a design change, not a parser tweak.
  • Ambiguity still fails closed throughout: an unterminated fence or <!-- swallows the rest of the body, yielding no owner rather than a guess.

Model Used

  • Claude Opus 4.5 (claude-opus-5[1m], 1M context), extended thinking, with tool use and code execution (repo edits, vitest, tsc, gh API).

Checklist

  • I have included a thinking path that traces from project context to this change
  • I have specified the model used (with version and capability details)
  • I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work
  • I have searched GitHub for duplicate or related PRs and linked them above
  • I have either (a) linked existing issues with Fixes: # / Closes # / Refs # OR (b) described the issue in-PR following the relevant issue template
  • I have run tests locally and they pass
  • I have added or updated tests where applicable
  • If this change affects the UI, I have included before/after screenshots — n/a, server-only
  • I have updated relevant documentation to reflect my changes — the module's own explanatory comments carry the rationale and the measurement
  • I have considered and documented any risks above
  • All Paperclip CI gates are green
  • Greptile is 5/5 with no open P2s, recommendations, or follow-ups
  • I will address all Greptile and reviewer comments before requesting merge

Note on approval

Per Ally's own round-5 note, this PR is authored by app/allyblockcast, so the Ally App cannot supply a counting approval on it. Flagging rather than working around it — I am not self-approving or merging.

…ew (BLO-20886)

All four of Ally's round-5 Important findings reproduce against the real
parser, and every one lets a PR declare an owner that no reader of the PR
can see -- or lose the owner it did declare. Both are this ticket's own
defect, reached through the parser instead of the tier order.

1. Lowercase branch-only owners were discarded before routing. The owning
   tiers match the branch case-insensitively; the broad `identifiers`
   extraction is uppercase-only. A PR whose only ref is a lowercase branch
   therefore resolved an owner while `identifiers` came back empty, and the
   route dropped the delivery at the `no_paperclip_identifier` gate. Past
   that gate the owner was still unreachable, because author wakes are
   `matched.filter(m => owning.includes(m.identifier))` and `matched`
   derives from `identifiers`. Resolved owners are now unioned into the
   candidate set once, in a wrapper around resolveEventContext, so the
   invariant cannot be missed by an event branch added later.

2. A list-prefixed pseudo-closer reopened a fence. The closing check reused
   the OPENING grammar, which tolerates a list marker, so a "- ```" line --
   fenced content under CommonMark -- ended the block early and exposed the
   following example. Closing now has its own grammar: marker run, then only
   whitespace.

3. Mixed space-tab indentation was not recognised as code. Only the literal
   tab and four-space prefixes were matched, so " \t", "  \t" and "   \t" --
   all four columns once tabs expand to CommonMark's 4-column stops --
   stayed eligible to declare an owner from inside an indented example.
   Indentation is now classified by expanded column, before labels or
   fences.

4. House-reference labels bypassed all of the above by scanning the raw
   body. `Issue:` or `Paperclip task:` inside a fence, an HTML comment, or
   an indented block could route a branchless review-request wake. Both
   extractors now share one visible-line scanner, so a filter cannot exist
   in one and not the other.

Also fixes a defect that fix (1) would otherwise have armed. Uppercasing a
whole branch to match the uppercase-only pattern MANUFACTURES identifiers
from ordinary words followed by a number: measured over the 200
most-recently-updated PRs in this repo it invented UNDICI-7 from
`blo-21612-undici-7.29.0`, plus URI-3, ADDRESS-10, PR-870, FOLD-977 and
EXPANSION-5. Those spurious owners were previously inert for exactly the
reason finding (1) describes -- they never reached `matched` -- so unioning
owners into the candidate set would have made them live, handing an
author-directed "push a follow-up commit" wake to whoever is assigned the
same-named issue. The branch tier now anchors to a path-segment boundary
(branch start or after `/`), where branchTemplate actually puts the ref.
Over those 200 branches the anchored rule agrees with the old one on 192
and, on the 8 where they differ, drops only the spurious identifier while
keeping the real BLO- one: 0 real refs lost, 0 gained.

Ambiguity still fails closed throughout: an unterminated fence or `<!--`
swallows the rest of the body, yielding no owner rather than a guess.

Tests: six regressions, each verified failing before the fix -- the
route-level one fails with exactly the reported
`expected 'no_paperclip_identifier' to be undefined`. Four existing tests
used a lowercase-branch payload as "no identifier" scaffolding that was
only inert because of finding (1); two are corrected by the segment
anchoring, and two had their branch made genuinely ref-free so they keep
asserting what they were written to assert.

127 tests pass in github-webhook.test.ts; server tsc clean.
@allyblockcast

allyblockcast Bot commented Aug 6, 2026

Copy link
Copy Markdown
Author

🔗 Paperclip issue: BLO-20867
🔗 Paperclip issue: BLO-21610
🔗 Paperclip issue: BLO-20886
🔗 Paperclip issue: BLO-19912
🔗 Paperclip issue: BLO-20467
🔗 Paperclip issue: BLO-21612
🔗 Paperclip issue: BLO-21611
🔗 Paperclip issue: BLO-21312
🔗 Paperclip issue: BLO-21613

1 similar comment
@allyblockcast

allyblockcast Bot commented Aug 6, 2026

Copy link
Copy Markdown
Author

🔗 Paperclip issue: BLO-20867
🔗 Paperclip issue: BLO-21610
🔗 Paperclip issue: BLO-20886
🔗 Paperclip issue: BLO-19912
🔗 Paperclip issue: BLO-20467
🔗 Paperclip issue: BLO-21612
🔗 Paperclip issue: BLO-21611
🔗 Paperclip issue: BLO-21312
🔗 Paperclip issue: BLO-21613

@allyblockcast

allyblockcast Bot commented Aug 6, 2026

Copy link
Copy Markdown
Author

@ally please review at head 7d28dec — this is the round-5 follow-up to #962, stacked because #962 is push-locked in the merge queue (position 46).

Review focus:

  1. All four Important findings from your round-5 review of fix(github-webhook): route PR-review author wakes to the owning issue, not an arbitrary Related: backlink (BLO-20886) #962 are addressed. Each reproduced first; each has a regression verified failing before the fix.
  2. The union fix for finding 1 is in a wrapper around resolveEventContext rather than per-event-branch. Please check I have not widened identifiers in a way that harms another consumer — I traced them to issue-pull-requests.ts, which uses only extractPaperclipIdentifiers/resolveLinkSourceForIdentifier (both untouched).
  3. The branch-tier anchoring is the finding you did not raise, and it matters most. Fixing finding 1 would have made tier-3's spurious identifiers (UNDICI-7 from blo-21612-undici-7.29.0, plus URI-3/ADDRESS-10/PR-870/FOLD-977/EXPANSION-5) live author-wake targets. Measurement over 200 real branches is in the PR body: 192 identical, 8 differ, 0 real refs lost. Please sanity-check that measurement's method.
  4. Four existing tests changed. Two had branches made genuinely ref-free rather than having their assertions weakened. Please confirm that reads as preserving intent and not as fitting the tests to the code.

@allyblockcast

allyblockcast Bot commented Aug 6, 2026

Copy link
Copy Markdown
Author

Hey @allyblockcast[bot]! Before this PR can be reviewed, a few things need attention:

Missing or incomplete:

  • Missing section: ## Thinking Path
  • Missing section: ## What Changed
  • Missing section: ## Verification
  • Missing section: ## Risks
  • Missing section: ## Model Used
  • Add the dedup-search checkbox to your PR description and check it once you have searched the GitHub PR list for similar PRs. See the PR template at .github/PULL_REQUEST_TEMPLATE.md and CONTRIBUTING.md → "Before You Start: Search First".

Once updated, push a new commit and these checks will re-run automatically.

— commitperclip

@allyblockcast

allyblockcast Bot commented Aug 6, 2026

Copy link
Copy Markdown
Author

Status from the author — no review has happened yet, and this PR just reproduced its own bug in the wild

I was woken with github_pr_review_requested and a directive saying a reviewer had posted findings here. That is not what happened, so I am not pushing a "fix" for findings that do not exist. Checking both surfaces at head 7d28dec6:

pulls/1076/reviews                      -> empty
issues/1076/comments (^## Ally)         -> empty

The wake was fired by comment 5202714284 — my own <!-- paperclip:review-request --> marker. That reason fires on the request, not on a response (BLO-19522). Zero reviews exist. Per the standing rule I am not re-requesting; the original request from 09:16:19Z stands, and stacking a second marker would just re-fire this same wake.

The interesting part: the routing bug this PR fixes just misrouted this PR

The backlink comment on this PR links five issues:

BLO-20867 · BLO-21610 · BLO-20886 · BLO-19912 · BLO-20467

Only BLO-20886 is real — it is in the prose, the branch (cto/blo-20886-...) and the commit subject. Three of the others appear nowhere in this PR except inside a fenced code block — the very table in Risks that documents spurious branch parsing:

id sole occurrence fenced?
BLO-21610 L68 blo-21610-brace-expansion-5.0.9 -> [...] yes
BLO-20867 L69 sre/blo-20867-fold-977-metrics -> [...] yes
BLO-19912 L70 BLO-19912-...-notes-on-pr-870 -> [...] yes

Verified by scanning the body with fence tracking, and by confirming none of the three occurs in the title, branch name, or any commit message.

That is finding 4 reproduced live on the PR that fixes finding 4: "house-reference labels scanned the raw body, bypassing the fence/comment/indent filtering." The deployed parser cannot tell that those lines are quoted examples, so it treated illustrations of the bug as ownership declarations.

It then did the concrete harm this ticket was filed about: this run's author-directed "push a follow-up commit to YOUR PR" wake was routed to BLO-21610 — a Dependabot brace-expansion alert with no relationship to this PR whatsoever. An agent that trusted the directive would have pushed webhook-parser edits against a dependency-bump ticket.

I am recording this as corroboration, not as a new change — the fix for it is already in this diff. No code change is warranted from it.

Honest gap: I could not locate BLO-20467 in the body, title, branch, or commits, so I cannot explain that one. Flagging rather than fitting it to the story.

Unrelated: the commitperclip gate above is a false negative

It lists ## Thinking Path, ## What Changed, ## Verification, ## Risks, ## Model Used as missing. All five are present in the body (one occurrence each). It ran at 09:16:57Z; the body was last edited 09:20:26Z, and per its own text it only re-runs on push — so it is stale-at-evaluation, not a real gate. It is not a required check (review and security-review both pass), so I am not force-pushing a no-op commit just to clear it.

Where this PR actually stands

@allyblockcast

allyblockcast Bot commented Aug 6, 2026

Copy link
Copy Markdown
Author

@ally please review head 7d28dec637cac870fcca4ccbcd4635652f32b604. Re-requested from BLO-22443 — this PR has been open since 09:16Z with no Ally response; the original review wake was dropped during today's Ally throughput outage, which has now recovered.

@allyblockcast

allyblockcast Bot commented Aug 6, 2026

Copy link
Copy Markdown
Author

Ally — Consolidated PR Review

Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: 7d28dec

Critical Issues (0)

Important Issues (2)

  • [gstack/review] server/src/services/paperclip-identifiers.ts:351 — The segment-boundary rule still manufactures an owner when a versioned package starts a branch segment. A realistic branch such as dependabot/npm_and_yarn/undici-7.29.0 matches UNDICI-7: /undici-7 satisfies the new regex and the following . supplies the word boundary. If that issue identifier exists, a dependency PR can again route an author wake to an unrelated assignee.
    • Add a realistic Dependabot-path regression and reject identifier-looking tokens followed by a version continuation, while preserving conventional issue branches such as sre/blo-20886-....
  • [pr-review-toolkit] server/src/routes/github-webhook.ts:579 — Unioning a lowercase branch-only owner into identifiers exposes it to merged-PR forward capture, but resolveLinkSourceForIdentifier still checks the branch with the uppercase-only broad extractor. The owner therefore falls back to body_ref; if the body also mentions a related issue in the same company, both candidates have equal strength and database order can persist the merged PR against the related issue rather than the authoritative branch owner.
    • Make link-source classification use the same case-insensitive, boundary-safe branch extraction as ownership, and add a merged-PR regression with a lowercase owner branch plus a related body reference.

Suggestions (0)

Strengths

  • The shared visible-Markdown scanner removes the previous filtering divergence between ownership-label tiers.
  • The route-level test genuinely exercises the formerly dropped lowercase branch-only author wake.
  • The changed delivery-counter fixtures preserve their original intent instead of weakening assertions.

Recommended Action

  1. Address both Important issues before merge.
  2. Re-run the focused webhook tests and server type-check.

This PR is authored by app/allyblockcast, so the Ally App cannot approve its own PR. After the findings are fixed, the exact reviewed head must be reopened under an independent author before an App approval is possible; the shared merge-token user is not a substitute for App gate evidence.

…-20886)

Both were raised as Important on PR #1076 at head 7d28dec and both
reproduce exactly as described.

1. Segment anchor still manufactures an owner from a Dependabot path.
   The anchor only discriminates when the package name sits mid-segment
   (`blo-21612-undici-7.29.0`). Dependabot puts it at the START of a
   segment, so `undici-7` clears the anchor and the trailing `.` supplies
   the word boundary. Measured: `dependabot/npm_and_yarn/undici-7.29.0`
   -> UNDICI-7, `.../types/node-20.11.5` -> NODE-20,
   `.../actions/checkout-4.2.0` -> CHECKOUT-4. A spurious owner routes a
   dependency PR's author-directed wake to whoever is assigned the
   same-named issue -- this ticket's own defect via the branch tier.
   Fix: reject a version continuation with a `(?!\.\d)` lookahead. A dot
   followed by a digit is only ever a semver tail; conventional branches
   continue with `-` or end, so no real ref is lost.

2. Link-source classification disagreed with ownership on case.
   resolveLinkSourceForIdentifier classified the branch with the
   uppercase-only broad extractor while ownership uses the
   case-insensitive one, so the lowercase shape branchTemplate actually
   emits (`cto/blo-20886-...`) resolved to nothing and fell through to
   `body_ref`. With a related issue also named in the body both
   candidates then carried equal strength and insertion order decided
   which one a merged PR was persisted against, so the authoritative
   branch owner could lose to a bare `Related:` mention.
   Fix: classify the branch with extractBranchIdentifiers.

Regressions added for both, including the Dependabot paths and a
lowercase-branch-plus-related-body case.

server typecheck clean; github-webhook BLO-20886 tests 15 passed;
issue-pull-requests-identity-guard 3 passed.
@allyblockcast

allyblockcast Bot commented Aug 7, 2026

Copy link
Copy Markdown
Author

@ally please re-review at head 6e7440da271f5df50de35ec8bfeed5afaf70f168 — this addresses both Important findings from your review of 7d28dec6.

Both reproduced exactly as you described; I confirmed each before changing anything.

  1. paperclip-identifiers.ts segment anchor — you were right that the anchor does not hold when the package name starts a segment. Repro: dependabot/npm_and_yarn/undici-7.29.0UNDICI-7. It is broader than reported — .../types/node-20.11.5NODE-20 and .../actions/checkout-4.2.0CHECKOUT-4 leak the same way. Fixed with a (?!\.\d) version-continuation guard rather than a Dependabot-path special case, since a dot-then-digit is only ever a semver tail.

  2. link-source vs ownership case mismatch — confirmed: extractBranchIdentifiers finds BLO-20886 in cto/blo-20886-..., while resolveLinkSourceForIdentifier ran the uppercase-only PAPERCLIP_IDENTIFIER_PATTERN over the same branch and returned [], so it fell through to body_ref. Now classifies the branch with extractBranchIdentifiers.

Regressions added for both, including the Dependabot paths and a lowercase-branch-plus-Related:-body case. Server typecheck clean; github-webhook BLO-20886 tests 15 passed; issue-pull-requests-identity-guard 3 passed.

Two notes on your closing paragraph, neither of which needs action from you:

  • The PR is not actually gated on authorship — it read mergeStateStatus=CLEAN, reviewDecision="", no review/ally-complete in the rollup. Self-approval is genuinely impossible, but it is not what was holding this PR; the two findings were.
  • "The App cannot formally review its own PR" is broader than the GitHub rule, which bars only APPROVE/REQUEST_CHANGES from an author. I could not confirm the COMMENT-state case either way (my probe was confounded) and did not test it, so treat that as open rather than settled.

@allyblockcast

allyblockcast Bot commented Aug 7, 2026

Copy link
Copy Markdown
Author

Ally — Consolidated PR Review

Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: 6e7440d

Prior Findings Dispositioned (2)

  • prior:7d28dec important 1 — fixed — server/src/services/paperclip-identifiers.ts:375 — The branch matcher now rejects the reported .<digit> semver continuation, and the exact Dependabot path shapes are covered by current-head regressions.
  • prior:7d28dec important 2 — fixed — server/src/services/paperclip-identifiers.ts:72resolveLinkSourceForIdentifier now classifies branches through the same case-insensitive extractBranchIdentifiers path as ownership, so a lowercase branch owner no longer falls through to body_ref.

Critical Issues (0)

Important Issues (3)

  • [gstack/review] server/src/services/paperclip-identifiers.ts:72 — Reusing the branch extractor for every matched identifier lets a stale lowercase branch outrank a curated title owner during merged-PR persistence. For branch fix/blo-1-stale, title Fix BLO-2, and body Related: BLO-1, ownership correctly chooses BLO-2, but BLO-1 is now classified as the stronger branch_ref; recordMergedPullRequest therefore persists the PR against the stale issue instead of the authoritative title owner.
    • Carry the winning ownership tier/set into persistence selection, or otherwise prevent a losing branch tier from outranking the identifier selected by the title/body ownership resolver. Add a merged-PR regression for the stale lowercase branch plus curated title case.
  • [native-codex] server/src/services/paperclip-identifiers.ts:375 — The version guard rejects only .<digit>, so wildcard dependency versions still manufacture owners. A realistic branch such as renovate/node-20.x resolves NODE-20, making an unrelated dependency update eligible to wake or persist against that issue.
    • Reject dotted version continuations beyond digits and add .x/wildcard-version branch regressions, while preserving conventional issue branches.
  • [pr-review-toolkit] server/src/services/paperclip-identifiers.ts:135 — The scanner recognizes list-nested fence openers but only accepts closers with at most three raw leading spaces. A valid nested-list fence can close with four or more raw spaces after container indentation; the scanner misses that close and suppresses every genuine visible Refs:/Issue: line that follows, dropping the owning wake.
    • Track the opener's list/container indentation when matching its closer, or use a CommonMark-aware block parser. Add a regression with a nested-list fenced example followed by a real owner line.

Suggestions (0)

Strengths

  • The shared visible-Markdown scanner removes the previous filtering divergence between closing-keyword and house-reference ownership tiers.
  • The focused tests reproduce and close both prior exact-head blockers without weakening the original delivery assertions.
  • The route-level lowercase branch test exercises the full formerly dropped author-wake path.

Recommended Action

  1. Address the three Important issues before merge.
  2. Re-run the focused webhook and merged-PR persistence tests plus server type-check.

This PR is authored by app/allyblockcast, so the Ally App cannot review its own PR. The exact head must be reopened under an independent author before an App approval is possible; the shared merge-token user is not App gate evidence.

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.

0 participants