Project

General

Profile

Actions

Feature #1554

closed
RA

Feature #1511: Replace ticket view with Kanban board for CTO and assigned developers

API: per-category counts on the board payload, so the tab pills stop undercounting

Feature #1554: API: per-category counts on the board payload, so the tab pills stop undercounting

Added by Redmine Admin 1 day ago. Updated 1 day ago.

Status:
Shipped
Priority:
Normal
Assignee:
-
Start date:
08/03/2026
Due date:
% Done:

0%

Estimated time:
spec_ref:

docs/superpowers/specs/2026-08-03-kanban-board-design.md

build_status:
build_number:
ci_run_url:
scan_status:
scan_report_url:
deploy_status:
preprod_url:
deployed_at:
branch:
feat/1554-board-category-counts
pr_url:
https://github.com/omdev-tech/PipeLiner-Client/pull/100
security_key:
severity:
paused:

Description

**Surface:** backend (`api/`)
**Depends on:** #1542 (merged)
**Blocks:** the Feature/Bug/SecOps pill counts being correct in #1546
**Found during:** #1546 review

### The regression, and why this epic owns it

The ticketing screen's Feature / Bug / SecOps tab pills (#85) show a count per category. They are computed client-side by `tabCategoryCounts` over the board payload.

That worked while the route used `mode=list`, which returned every ticket. #1542 switched it to **board mode, capped at 50 per status** — so the pills now count only what survived the cap.

**Concrete failure:** a project with 60 tickets in Spec, 12 of them bugs, all ranked below the top 50. The **Bug pill reads 0** while twelve bug tickets exist. A client clicks Bug, sees an empty board, and concludes their bug reports were lost.

This is a regression introduced by this epic, not pre-existing — hence a child of #1511 rather than a parked follow-up. It carries no complexity score of its own; #1511 is pinned at 7/10 and already charged.

### Why it cannot be fixed in the frontend

The tickets are not in the response at all. No amount of client-side counting can recover them. #1546 has made the pills and the column headers derive from the same slice, so they at least never contradict each other on screen — but both are still wrong against the project's real totals.

### What to build

Return per-category totals on the board payload, computed server-side over the **whole** project before the per-status cap is applied — the same "count before you truncate" discipline the sort already follows.

Shape is yours to choose; the frontend needs Feature, Bug and SecOps totals for the project, honouring the caller's D2 visibility scope (a developer's pills must count only the tickets a developer can see). Note the existing convention: `tabCategoryCounts` excludes Shipped from the pill counts, since a shipped ticket is done and must not inflate what the client reads as open work — preserve that.

## Acceptance criteria
- [ ] The board payload carries per-category counts (feature / bug / secops) computed over the whole project, not over the capped slice.
- [ ] Counts respect the D2 visibility matrix: a developer's counts include only tickets they can see, a client's include the whole project, CTO/admin all — parametrised over every viewer kind.
- [ ] Shipped is excluded from the counts, matching the existing `tabCategoryCounts` behaviour (regression test).
- [ ] Test the exact failure above: 60 tickets in one status, 12 of them bugs ranked below the cap, and the bug count reads **12**, not 0.
- [ ] No new per-ticket call and no N+1 — a call/query-count assertion, as on the rest of this endpoint.
- [ ] A degraded or absent aggregator still yields a schema-valid 200 with the counts zeroed or null, never a 500.
- [ ] `src/features/tasks/types.ts`'s `tabCategoryCounts` is updated to read the server counts, and its stale client-side derivation is removed rather than left as a fallback that could silently disagree.
- [ ] Gates green: `python -m pytest -q` (**run serially**), `ruff check .`, `mypy src`; and the frontend gates for the consuming change.

Actions

Also available in: PDF Atom