MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-80

2.2.6 Resolve finding #21 — generalize `isReady` + `countOpenBlockers`

Done
Description

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.

Acceptance criteria

  • workItemsService.isReady no longer references the string literal 'done' for terminal classification; uses the per-project terminal-key set.
  • New workflowsService.getTerminalStatusKeysByProjects(projectIds, workspaceId) ships and is used by countOpenBlockers for the batched lookup.
  • Vitest test for the scenario from finding #21: a blocker with 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.
  • Cross-project blocker test: project A and project B both have the default seed; an admin recategorizes 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.
  • Performance: one query for blockers, one query for the per-project terminal sets, regardless of how many blocker projects there are (asserted via Prisma query log spy).
  • The previous v1 hardcode + its inline comment removed; finding #21's note in countOpenBlockers + isReady deleted.
  • Finding #21's entry in PRODECT_FINDINGS.md gets a > Resolved: 2.2.6 appended.

Context refs

  • Finding #21 in prodect_plan/PRODECT_FINDINGS.md
  • lib/services/workItemsService.ts — current isReady + its v1 test
  • lib/repositories/workItemLinkRepository.ts — current countOpenBlockers + the literal to remove
  • workflowsService from 2.2.3 — extend with the batched method