diff --git a/server/src/__tests__/issues-service.test.ts b/server/src/__tests__/issues-service.test.ts index 0dbc66b68f..b56785ff38 100644 --- a/server/src/__tests__/issues-service.test.ts +++ b/server/src/__tests__/issues-service.test.ts @@ -3200,6 +3200,114 @@ describeEmbeddedPostgres("issueService blockers and dependency wake readiness", expect(blockedRelations.blockedBy.map((relation) => relation.id)).toEqual([blockerId]); }); + it("returns blocked-by summaries on newly created issues", async () => { + const companyId = randomUUID(); + await db.insert(companies).values({ + id: companyId, + name: "Paperclip", + issuePrefix: `T${companyId.replace(/-/g, "").slice(0, 6).toUpperCase()}`, + requireBoardApprovalForNewAgents: false, + }); + + const blockerId = randomUUID(); + await db.insert(issues).values({ + id: blockerId, + companyId, + title: "Blocker", + status: "todo", + priority: "high", + }); + + const created = await svc.create(companyId, { + title: "Blocked issue", + status: "blocked", + priority: "medium", + blockedByIssueIds: [blockerId], + }); + + expect(created.blockedBy.map((relation) => relation.id)).toEqual([blockerId]); + expect(created.blockedBy[0]).toEqual(expect.objectContaining({ + title: "Blocker", + status: "todo", + priority: "high", + })); + expect(created.blocks).toEqual([]); + }); + + it("returns blocked-by summaries on newly created child issues", async () => { + const companyId = randomUUID(); + await db.insert(companies).values({ + id: companyId, + name: "Paperclip", + issuePrefix: `T${companyId.replace(/-/g, "").slice(0, 6).toUpperCase()}`, + requireBoardApprovalForNewAgents: false, + }); + + const parentId = randomUUID(); + const blockerId = randomUUID(); + await db.insert(issues).values([ + { + id: parentId, + companyId, + title: "Parent", + status: "todo", + priority: "medium", + }, + { + id: blockerId, + companyId, + title: "Blocker", + status: "todo", + priority: "high", + }, + ]); + + const { issue: child } = await svc.createChild(parentId, { + title: "Blocked child issue", + status: "blocked", + priority: "medium", + blockedByIssueIds: [blockerId], + }); + + expect(child.parentId).toBe(parentId); + expect(child.blockedBy.map((relation) => relation.id)).toEqual([blockerId]); + expect(child.blocks).toEqual([]); + }); + + it("returns blocks summaries when child creation blocks the parent", async () => { + const companyId = randomUUID(); + await db.insert(companies).values({ + id: companyId, + name: "Paperclip", + issuePrefix: `T${companyId.replace(/-/g, "").slice(0, 6).toUpperCase()}`, + requireBoardApprovalForNewAgents: false, + }); + + const parentId = randomUUID(); + await db.insert(issues).values({ + id: parentId, + companyId, + title: "Parent", + status: "todo", + priority: "medium", + }); + + const { issue: child } = await svc.createChild(parentId, { + title: "Parent-blocking child", + status: "todo", + priority: "medium", + blockParentUntilDone: true, + }); + + expect(child.blocks.map((relation) => relation.id)).toEqual([parentId]); + expect(child.blocks[0]).toEqual(expect.objectContaining({ + title: "Parent", + status: "todo", + priority: "medium", + })); + expect(child.blockedBy).toEqual([]); + }); + it("adds terminal blockers to immediate blocked-by summaries", async () => { const companyId = randomUUID(); await db.insert(companies).values({ diff --git a/server/src/services/issues.ts b/server/src/services/issues.ts index 0fa0913587..ca2f48959e 100644 --- a/server/src/services/issues.ts +++ b/server/src/services/issues.ts @@ -3991,6 +3991,23 @@ export function issueService(db: Db) { return empty; } + async function withIssueRelationSummaries( + companyId: string, + rows: T[], + dbOrTx: DbReader = db, + ): Promise> { + if (rows.length === 0) return []; + const relationMap = await getIssueRelationSummaryMap( + companyId, + rows.map((row) => row.id), + dbOrTx, + ); + return rows.map((row) => ({ + ...row, + ...(relationMap.get(row.id) ?? { blockedBy: [], blocks: [] }), + })); + } + async function assertNoBlockingCycles( companyId: string, issueId: string, @@ -4960,7 +4977,7 @@ export function issueService(db: Db) { actorUserId, ...issueData } = data; - const child = await issueService(db).create(parent.companyId, { + let child = await issueService(db).create(parent.companyId, { ...issueData, parentId: parent.id, projectId: issueData.projectId ?? parent.projectId, @@ -4983,6 +5000,7 @@ export function issueService(db: Db) { [...new Set([...existingBlockers.map((row) => row.blockerIssueId), child.id])], { agentId: actorAgentId ?? null, userId: actorUserId ?? null }, ); + [child] = await withIssueRelationSummaries(parent.companyId, [child], db); } return { @@ -5484,7 +5502,8 @@ export function issueService(db: Db) { ); } const [enriched] = await withIssueLabels(tx, [issue]); - return enriched; + const [withRelations] = await withIssueRelationSummaries(companyId, [enriched], tx); + return withRelations; }); },