Estimate: 10m · Depends on: 2.2.3
Close finding #21 — Story 1.4.4's workItemsService.isReady + workItemLinkRepository.countOpenBlockers currently hardcode 'done' as the terminal-status literal. Now that 2.2.3 exposes workflowsService.getTerminalStatusKeys(projectId), swap the literal for the set. The blocker is "open" iff its status is NOT in its project's terminal-key set.
The blocker can live in a DIFFERENT project than the work item being readiness-checked (cross-project blocks are legal in the work_item_link model from 1.4.3). So the resolution uses each blocker's own project's terminal set, not the readiness-check work item's project's set. The repository method takes a typed input shape and resolves terminal sets per project in one batched query — not N+1.
Implementation shape: countOpenBlockers(workItemId, getProjectTerminalSet: (projectId: string) => Promise<Set<string>>) stays a repo method — the service layer composes workflowsService.getTerminalStatusKeys in. To avoid N+1, fetch all unique blocker project IDs in one query, then call a batched workflowsService.getTerminalStatusKeysByProjects(projectIds, workspaceId) (new method added on top of 2.2.3) returning Map<projectId, Set<string>> in one round-trip.
workItemsService.isReady no longer references the string literal 'done' for terminal classification; uses the per-project terminal-key set.workflowsService.getTerminalStatusKeysByProjects(projectIds, workspaceId) ships and is used by countOpenBlockers for the batched lookup.status: 'cancelled' in a default-seeded project (where cancelled is category: 'done' out of the box) counts as resolved; if a test recategorizes cancelled to todo in one project's workflow, the same blocker there still counts as blocking — proving the resolution reads each project's live category, not a hardcoded set.cancelled in project B to category: 'todo'; a work item in project A blocked by a cancelled blocker in project B still counts as blocked, while one blocked by a cancelled blocker in project A is ready — the readiness check correctly uses each blocker's own project's terminal set.countOpenBlockers + isReady deleted.PRODECT_FINDINGS.md gets a > Resolved: 2.2.6 appended.prodect_plan/PRODECT_FINDINGS.mdlib/services/workItemsService.ts — current isReady + its v1 testlib/repositories/workItemLinkRepository.ts — current countOpenBlockers + the literal to removeworkflowsService from 2.2.3 — extend with the batched method