fix(issues): ignore cancelled child blockers in attention (#7577)
Docker / build-and-push (push) Failing after 3m33s
Refresh Lockfile / refresh (push) Successful in 10m42s
Release / verify_canary (push) Failing after 16m21s
Release / verify_stable (push) Has been skipped
Release / publish_canary (push) Has been skipped
Release / preview_stable (push) Has been skipped
Release / publish_stable (push) Has been skipped
Docker / build-and-push (push) Failing after 3m33s
Refresh Lockfile / refresh (push) Successful in 10m42s
Release / verify_canary (push) Failing after 16m21s
Release / verify_stable (push) Has been skipped
Release / publish_canary (push) Has been skipped
Release / preview_stable (push) Has been skipped
Release / publish_stable (push) Has been skipped
Fixes #7578 ## Thinking Path > - Paperclip orchestrates AI agents for zero-human companies. > - Blocked issue health is surfaced through blockerAttention so operators can see whether blocked work has a live path. > - The blockerAttention graph uses both explicit blockedBy edges and direct child issue edges. > - Done children were already ignored, but cancelled direct children still appeared as unresolved blockers. > - Explicit cancelled dependencies should remain visible as dependency problems, but terminal direct children should not inflate a parent blocker count. > - This pull request narrows child-edge traversal to non-terminal children and adds a regression test for the observed case. > - The benefit is that cancelled child issues no longer make blocked parents look like they have extra unresolved blocker attention. ## What Changed - Added terminal child status filtering for blockerAttention parent-child traversal so cancelled direct children are ignored with done children. - Added a server regression test where a blocked parent has active explicit blockers plus a cancelled direct child; the cancelled child no longer increases unresolved counts or becomes the sample blocker. ## Verification - `perl -e 'alarm shift; exec @ARGV' 300 pnpm exec vitest run server/src/__tests__/issue-blocker-attention.test.ts` -> 19 tests passed. ## Risks - Low risk: the change only affects implicit direct-child blockerAttention edges. - Explicit blockedBy edges to cancelled issues are intentionally unchanged and remain represented as attention-required dependency problems. ## Model Used - OpenAI GPT-5, Codex coding agent in tool-enabled CLI environment; reasoning and code execution used for repository inspection, patching, git operations, and targeted test verification. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] If this change affects the UI, I have included before/after screenshots (not applicable; no UI change) - [x] I have updated relevant documentation to reflect my changes (not applicable; bugfix covered by regression test) - [x] I have considered and documented any risks above - [x] I will address all Greptile and reviewer comments before requesting merge
This commit is contained in:
committed by
GitHub
parent
d1e6662ed8
commit
9ac24317e6
@@ -255,6 +255,49 @@ describeEmbeddedPostgres("issue blocker attention", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("ignores cancelled direct children when counting unresolved blocker attention", async () => {
|
||||
const { companyId, agentId } = await createCompany("PBD");
|
||||
const parentId = await insertIssue({ companyId, identifier: "PBD-1", title: "Parent", status: "blocked" });
|
||||
const activeBlockerOneId = await insertIssue({
|
||||
companyId,
|
||||
identifier: "PBD-2",
|
||||
title: "Running dependency one",
|
||||
status: "todo",
|
||||
assigneeAgentId: agentId,
|
||||
});
|
||||
const activeBlockerTwoId = await insertIssue({
|
||||
companyId,
|
||||
identifier: "PBD-3",
|
||||
title: "Running dependency two",
|
||||
status: "todo",
|
||||
assigneeAgentId: agentId,
|
||||
});
|
||||
await insertIssue({
|
||||
companyId,
|
||||
identifier: "PBD-4",
|
||||
title: "Cancelled child",
|
||||
status: "cancelled",
|
||||
parentId,
|
||||
assigneeAgentId: agentId,
|
||||
});
|
||||
await block({ companyId, blockerIssueId: activeBlockerOneId, blockedIssueId: parentId });
|
||||
await block({ companyId, blockerIssueId: activeBlockerTwoId, blockedIssueId: parentId });
|
||||
await activeRun({ companyId, agentId, issueId: activeBlockerOneId });
|
||||
await activeRun({ companyId, agentId, issueId: activeBlockerTwoId });
|
||||
|
||||
const parent = (await svc.list(companyId, { status: "blocked" })).find((issue) => issue.id === parentId);
|
||||
|
||||
expect(parent?.blockerAttention).toMatchObject({
|
||||
state: "covered",
|
||||
reason: "active_dependency",
|
||||
unresolvedBlockerCount: 2,
|
||||
coveredBlockerCount: 2,
|
||||
stalledBlockerCount: 0,
|
||||
attentionBlockerCount: 0,
|
||||
});
|
||||
expect(parent?.blockerAttention?.sampleBlockerIdentifier).not.toBe("PBD-4");
|
||||
});
|
||||
|
||||
it("covers recursive blocker chains when the downstream leaf has active work", async () => {
|
||||
const { companyId, agentId } = await createCompany("PBR");
|
||||
const parentId = await insertIssue({ companyId, identifier: "PBR-1", title: "Parent", status: "blocked" });
|
||||
|
||||
@@ -1204,6 +1204,7 @@ const BLOCKER_ATTENTION_ACTIVE_WAKE_STATUSES = ["queued", "deferred_issue_execut
|
||||
const BLOCKER_ATTENTION_PENDING_INTERACTION_STATUSES = ["pending"];
|
||||
const BLOCKER_ATTENTION_PENDING_APPROVAL_STATUSES = ["pending", "revision_requested"];
|
||||
const BLOCKER_ATTENTION_OPEN_RECOVERY_ORIGIN_KIND = "harness_liveness_escalation";
|
||||
const BLOCKER_ATTENTION_CHILD_TERMINAL_STATUSES = ["done", "cancelled"];
|
||||
const PRODUCTIVITY_REVIEW_ORIGIN_KIND = "issue_productivity_review";
|
||||
const PRODUCTIVITY_REVIEW_TERMINAL_STATUSES = ["done", "cancelled"];
|
||||
const PRODUCTIVITY_REVIEW_ACTIVITY_ACTIONS = [
|
||||
@@ -1636,7 +1637,7 @@ async function listIssueBlockerAttentionMap(
|
||||
and(
|
||||
eq(issues.companyId, companyId),
|
||||
inArray(issues.parentId, chunk),
|
||||
ne(issues.status, "done"),
|
||||
notInArray(issues.status, BLOCKER_ATTENTION_CHILD_TERMINAL_STATUSES),
|
||||
),
|
||||
);
|
||||
const [explicitBlockerRows, childRows] = await Promise.all([
|
||||
|
||||
Reference in New Issue
Block a user