fix: address issue reference review findings

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-04-21 09:18:18 -05:00
1 parent 951eba9d31
commit 483dcf0210
7 files changed
+126 -36

No files matched your search

@@ -31,4 +31,20 @@ END $$;--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "issue_reference_mentions_company_source_issue_idx" ON "issue_reference_mentions" USING btree ("company_id","source_issue_id");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "issue_reference_mentions_company_target_issue_idx" ON "issue_reference_mentions" USING btree ("company_id","target_issue_id");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "issue_reference_mentions_company_issue_pair_idx" ON "issue_reference_mentions" USING btree ("company_id","source_issue_id","target_issue_id");--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "issue_reference_mentions_company_source_mention_uq" ON "issue_reference_mentions" USING btree ("company_id","source_issue_id","target_issue_id","source_kind","source_record_id");
DELETE FROM "issue_reference_mentions"
WHERE "id" IN (
SELECT "id"
FROM (
SELECT
"id",
row_number() OVER (
PARTITION BY "company_id", "source_issue_id", "target_issue_id", "source_kind", "source_record_id"
ORDER BY "created_at", "id"
) AS "row_number"
FROM "issue_reference_mentions"
) AS "duplicates"
WHERE "duplicates"."row_number" > 1
);--> statement-breakpoint
DROP INDEX IF EXISTS "issue_reference_mentions_company_source_mention_uq";--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "issue_reference_mentions_company_source_mention_record_uq" ON "issue_reference_mentions" USING btree ("company_id","source_issue_id","target_issue_id","source_kind","source_record_id") WHERE "source_record_id" IS NOT NULL;--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "issue_reference_mentions_company_source_mention_null_record_uq" ON "issue_reference_mentions" USING btree ("company_id","source_issue_id","target_issue_id","source_kind") WHERE "source_record_id" IS NULL;
@@ -8583,8 +8583,8 @@
"method": "btree",
"with": {}
},
"issue_reference_mentions_company_source_mention_uq": {
"name": "issue_reference_mentions_company_source_mention_uq",
"issue_reference_mentions_company_source_mention_record_uq": {
"name": "issue_reference_mentions_company_source_mention_record_uq",
"columns": [
{
"expression": "company_id",
@@ -8620,7 +8620,42 @@
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
"with": {},
"where": "\"issue_reference_mentions\".\"source_record_id\" is not null"
},
"issue_reference_mentions_company_source_mention_null_record_uq": {
"name": "issue_reference_mentions_company_source_mention_null_record_uq",
"columns": [
{
"expression": "company_id",
"isExpression": false,
"asc": true,
"nulls": "last"
},
{
"expression": "source_issue_id",
"isExpression": false,
"asc": true,
"nulls": "last"
},
{
"expression": "target_issue_id",
"isExpression": false,
"asc": true,
"nulls": "last"
},
{
"expression": "source_kind",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {},
"where": "\"issue_reference_mentions\".\"source_record_id\" is null"
}
},
"foreignKeys": {
@@ -13985,4 +14020,4 @@
"schemas": {},
"tables": {}
}
}
}
@@ -1,3 +1,4 @@
import { sql } from "drizzle-orm";
import { index, pgTable, text, timestamp, uniqueIndex, uuid } from "drizzle-orm/pg-core";
import { companies } from "./companies.js";
import { issues } from "./issues.js";
@@ -30,12 +31,18 @@ export const issueReferenceMentions = pgTable(
table.sourceIssueId,
table.targetIssueId,
),
companySourceMentionUq: uniqueIndex("issue_reference_mentions_company_source_mention_uq").on(
companySourceMentionWithRecordUq: uniqueIndex("issue_reference_mentions_company_source_mention_record_uq").on(
table.companyId,
table.sourceIssueId,
table.targetIssueId,
table.sourceKind,
table.sourceRecordId,
),
).where(sql`${table.sourceRecordId} is not null`),
companySourceMentionWithoutRecordUq: uniqueIndex("issue_reference_mentions_company_source_mention_null_record_uq").on(
table.companyId,
table.sourceIssueId,
table.targetIssueId,
table.sourceKind,
).where(sql`${table.sourceRecordId} is null`),
}),
);
@@ -39,6 +39,12 @@ describe("issue references", () => {
]);
});
it("trims unmatched square brackets from issue path tokens", () => {
expect(findIssueReferenceMatches("See /issues/PAP-123] for context.")).toEqual([
{ index: 4, length: 15, identifier: "PAP-123", matchedText: "/issues/PAP-123" },
]);
});
it("extracts and dedupes references from markdown", () => {
expect(extractIssueReferenceIdentifiers("PAP-1 [again](/issues/pap-1) PAP-2")).toEqual(["PAP-1", "PAP-2"]);
});
+4 -1
View File
@@ -78,7 +78,10 @@ function trimTrailingPunctuation(token: string): string {
const last = trimmed[trimmed.length - 1]!;
if (!".,!?;:".includes(last) && last !== ")" && last !== "]") break;
if ((last === ")" || last === "]") && (trimmed.match(/\(/g)?.length ?? 0) >= (trimmed.match(/\)/g)?.length ?? 0)) {
if (
(last === ")" && (trimmed.match(/\(/g)?.length ?? 0) >= (trimmed.match(/\)/g)?.length ?? 0))
|| (last === "]" && (trimmed.match(/\[/g)?.length ?? 0) >= (trimmed.match(/\]/g)?.length ?? 0))
) {
break;
}
trimmed = trimmed.slice(0, -1);
+22 -18
View File
@@ -181,26 +181,30 @@ export function issueReferenceService(db: Db) {
}
async function syncIssue(issueId: string, dbOrTx: any = db) {
const issue = await issueById(issueId, dbOrTx);
if (!issue) throw notFound("Issue not found");
const runSync = async (tx: any) => {
const issue = await issueById(issueId, tx);
if (!issue) throw notFound("Issue not found");
await replaceSourceMentions({
companyId: issue.companyId,
sourceIssueId: issue.id,
sourceKind: "title",
sourceRecordId: null,
documentKey: null,
text: issue.title,
}, dbOrTx);
await replaceSourceMentions({
companyId: issue.companyId,
sourceIssueId: issue.id,
sourceKind: "title",
sourceRecordId: null,
documentKey: null,
text: issue.title,
}, tx);
await replaceSourceMentions({
companyId: issue.companyId,
sourceIssueId: issue.id,
sourceKind: "description",
sourceRecordId: null,
documentKey: null,
text: issue.description,
}, dbOrTx);
await replaceSourceMentions({
companyId: issue.companyId,
sourceIssueId: issue.id,
sourceKind: "description",
sourceRecordId: null,
documentKey: null,
text: issue.description,
}, tx);
};
return dbOrTx === db ? db.transaction(runSync) : runSync(dbOrTx);
}
async function syncComment(commentId: string, dbOrTx: any = db) {
+29 -10
View File
@@ -13,24 +13,43 @@ export function IssueReferencePill({
strikethrough?: boolean;
className?: string;
}) {
const issueLabel = issue.identifier ?? issue.id;
const issueLabel = issue.identifier ?? issue.title;
const classNames = cn(
"paperclip-mention-chip paperclip-mention-chip--issue",
"inline-flex items-center gap-1 rounded-full border border-border px-2 py-0.5 text-xs no-underline",
issue.identifier && "hover:bg-accent/50 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring",
strikethrough && "opacity-60 line-through decoration-muted-foreground",
className,
);
const content = (
<>
{issue.status ? <StatusIcon status={issue.status} className="h-3 w-3 shrink-0" /> : null}
<span>{issue.identifier ?? issue.title}</span>
</>
);
if (!issue.identifier) {
return (
<span
data-mention-kind="issue"
className={classNames}
title={issue.title}
aria-label={`Issue: ${issue.title}`}
>
{content}
</span>
);
}
return (
<Link
to={`/issues/${issueLabel}`}
data-mention-kind="issue"
className={cn(
"paperclip-mention-chip paperclip-mention-chip--issue",
"inline-flex items-center gap-1 rounded-full border border-border px-2 py-0.5 text-xs no-underline",
"hover:bg-accent/50 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring",
strikethrough && "opacity-60 line-through decoration-muted-foreground",
className,
)}
className={classNames}
title={issue.title}
aria-label={`Issue ${issueLabel}: ${issue.title}`}
>
{issue.status ? <StatusIcon status={issue.status} className="h-3 w-3 shrink-0" /> : null}
<span>{issue.identifier ?? issue.title}</span>
{content}
</Link>
);
}