Description
LINK_REGEX in src/lib/pr-linking.ts has no word boundary before the closing keywords (closes, fixes, resolves), so any word that ends in one of those keywords followed by #N creates a spurious row in pr_issue_links. Because the table uses additive INSERT OR IGNORE (links are never removed), the false entry persists across all future syncs.
Steps to Reproduce
- Open or sync a repo where a merged PR body or title contains any of:
"bugfix #<N>" or "hotfix #<N>" (matches fix #N)
"discloses #<N>" (matches closes #N)
"encloses #<N>" or "forecloses #<N>" (matches closes #N)
- Navigate to the Issues dashboard and filter by Completed.
- Observe that issue
#<N> appears as "completed" (linked to the merged PR) even though the PR never intentionally closed that issue.
Expected Behavior
Only PRs that explicitly use a standalone closing keyword (Closes #N, Fixes #N, Resolves #N) should create an entry in pr_issue_links. Words containing a keyword as a suffix (bugfix, hotfix, discloses, etc.) should not match.
Actual Behavior
The regex in src/lib/pr-linking.ts:3 matches the keyword anywhere inside a word:
// current — no word boundary
const LINK_REGEX =
/(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s*:?\s*(?:...)?#(\d+)/gi;
Examples of unintended matches:
| PR body text |
Keyword matched |
Spurious link |
bugfix #42 |
fix |
issue #42 |
hotfix #42 |
fix |
issue #42 |
discloses #42 |
closes |
issue #42 |
encloses #42 |
closes |
issue #42 |
Because pr_issue_links is append-only (INSERT OR IGNORE, refresh.ts:39), the false link is never cleaned up. The Completed filter (issues/route.ts:104) checks HAS_MERGED_PR_SQL against this table, so the affected issue is permanently misclassified.
Proposed Fix
Add \b (word boundary) before the keyword group:
// fixed
const LINK_REGEX =
/\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s*:?\s*(?:(?:https?:\/\/github\.com\/)?([\w.-]+\/[\w.-]+))?#(\d+)/gi;
\b is a zero-width assertion — it matches the position between a non-word character (or start of string) and a word character. This correctly rejects bugfix #42 (the f in fix is preceded by g, a word char) while still matching Fixes #42 at a word start.
Environment
- Browser: N/A (server-side data pipeline)
- OS: N/A
- Node version: any
Additional Context
- Affected file:
src/lib/pr-linking.ts line 3
- Downstream consumers:
src/lib/refresh.ts:29-46 (replacePrIssueLinks), src/app/api/issues/route.ts:31-34 (HAS_MERGED_PR_SQL)
- The additive link-table design (
refresh.ts comment, lines 22-28) means no self-correction occurs after a false link is written — a one-time data migration to delete known-spurious rows may be needed after the regex is patched.
Description
LINK_REGEXinsrc/lib/pr-linking.tshas no word boundary before the closing keywords (closes,fixes,resolves), so any word that ends in one of those keywords followed by#Ncreates a spurious row inpr_issue_links. Because the table uses additiveINSERT OR IGNORE(links are never removed), the false entry persists across all future syncs.Steps to Reproduce
"bugfix #<N>"or"hotfix #<N>"(matchesfix #N)"discloses #<N>"(matchescloses #N)"encloses #<N>"or"forecloses #<N>"(matchescloses #N)#<N>appears as "completed" (linked to the merged PR) even though the PR never intentionally closed that issue.Expected Behavior
Only PRs that explicitly use a standalone closing keyword (
Closes #N,Fixes #N,Resolves #N) should create an entry inpr_issue_links. Words containing a keyword as a suffix (bugfix,hotfix,discloses, etc.) should not match.Actual Behavior
The regex in
src/lib/pr-linking.ts:3matches the keyword anywhere inside a word:Examples of unintended matches:
bugfix #42fixhotfix #42fixdiscloses #42closesencloses #42closesBecause
pr_issue_linksis append-only (INSERT OR IGNORE,refresh.ts:39), the false link is never cleaned up. The Completed filter (issues/route.ts:104) checksHAS_MERGED_PR_SQLagainst this table, so the affected issue is permanently misclassified.Proposed Fix
Add
\b(word boundary) before the keyword group:\bis a zero-width assertion — it matches the position between a non-word character (or start of string) and a word character. This correctly rejectsbugfix #42(thefinfixis preceded byg, a word char) while still matchingFixes #42at a word start.Environment
Additional Context
src/lib/pr-linking.tsline 3src/lib/refresh.ts:29-46(replacePrIssueLinks),src/app/api/issues/route.ts:31-34(HAS_MERGED_PR_SQL)refresh.tscomment, lines 22-28) means no self-correction occurs after a false link is written — a one-time data migration to delete known-spurious rows may be needed after the regex is patched.