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-442

6.15.2 Filtered board read — thread a compiled filter predicate (6.1 AST) into getBoard/loadColumnCards; cap + `truncated` over the filtered set; Scrum-scope compose

Done
Description

Estimate: 32m

Make the board projection filterable. Pure backend, no UI — the reusable read 6.15.3 consumes.

boardsService.getBoard grows an optional filter input — a FilterAst (built inline from a board query) OR a resolved saved-filter id (reuse the 6.2 saved-filter resolution + access gate). The service compiles it with the SAME resolveFilterAst (lib/filters/registry.ts) the /issues list uses, producing the Prisma where fragment, and threads it into loadColumnCards so each column’s card read is AND-ed with the predicate (mirror workItemRepository.findByProjectFiltered — the shipped precedent that applies a compiled filter to a project read). Repository writes still require tx; this is read-only.

Cap / truncated over the FILTERED set (3.8). The BOARD_ISSUE_CAP count + the truncated flag + the virtualization bound must be computed over the filtered result, not the whole board — so a filter that brings the set under the cap clears truncated (that is the over-cap banner’s whole purpose). Scrum compose (4.5): the predicate ANDs with the existing active-sprint scope filter — it narrows WITHIN the sprint, never widening it. Tenant + permission: the explicit workspaceId gate (finding #26) and the 6.4 project-access scope are unchanged and still apply.

Acceptance criteria

  • getBoard accepts an optional filter (inline AST or saved-filter id); when present, each column projects ONLY matching cards; when absent, behaviour is byte-identical to today (no regression to the unfiltered board).
  • truncated / the cap count / the virtualization bound are computed over the filtered set; a filter under the cap clears truncated. On a Scrum board the predicate composes with (does not replace) the 4.5 sprint scope.
  • Invalid / unauthorized saved-filter id → a typed error the route maps (no raw Prisma escaping the service); workspace + 6.4 scope preserved; no schema change (migrate dev = "No difference detected"); pnpm test:coverage ≥ 90% on changed files.

Context refs

  • lib/services/boardsService.ts (getBoard + loadColumnCards + the 3.8 cap/truncated logic + the 4.5 sprint-scope filter), lib/mappers/boardMappers.ts, lib/dto/boards.ts
  • lib/filters/ast.ts + lib/filters/registry.ts (resolveFilterAst — the 6.1 compiler) and workItemRepository.findByProjectFiltered (the shipped "apply a compiled filter to a project read" precedent)
  • lib/savedFilters/* (6.2 — saved-filter resolution + access.ts gate)
  • finding #26 (explicit workspaceId tenant filter); motir-core/CLAUDE.md (4-layer)