You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
bug(llc): Kanban board renders zero cards always — frontend reads an 'items' key the board API never sends #13993
Found while reviewing PR #13955; independently verified. Supersedes the framing of #13954.
The Kanban board renders zero cards, in every column, always
Three defects stacked. The first alone is sufficient.
1. Response shape mismatch — the frontend reads a key the backend never sends.
autobot-backend/llc/api/boards.py:245-248 returns {"board": ..., "columns": columns_with_items}.
There is no top-level items key; items are nested inside each column.
services/api.ts is a raw JSON passthrough — nothing rewrites the envelope.
So items is unconditionally [] and every column renders empty.
2. The payload omits the two fields the column/swimlane filters need. _work_item_summary() (boards.py:95-107) emits neither assignee_type nor column_id. grep -n "assignee_type" autobot-backend/llc/api/boards.py returns nothing. Even if (1) were fixed
by flattening the nested items, both humanItems() and agentItems() would still return empty.
3. The vocabulary mismatch — this is #13954. KanbanBoardView.vue:159 filters on assignee_type === 'human'; the backend only ever writes 'user'. Real, but third in the chain.
Also: autobot-frontend/src/views/llc/workItemTypes.ts:40 types the field 'human' | 'agent' | null
— 'user' is missing entirely, so the correct fix is a type change, not only the comparison. And the
comment at workItemTypes.ts:11 ("column_id and assignee_type are only present on board API
responses") is inverted: they are present on the work-items response (api/work_items.py:382)
and absent from the board response.
Why this matters beyond the board
A feature shipped, has a view, has tests, and displays nothing — and nothing reported it. That is the
lying-detector class tracked in #13852: no error, no empty-state distinction between "no work" and
"never received any work". A board with zero cards looks exactly like a board with an empty backlog.
Ask
Decide the canonical shape: flatten to a top-level items, or have the frontend read columns[].items. The backend already nests them, so the frontend read is probably the bug.
Add assignee_type and column_id to _work_item_summary().
Canonical shape decision: the nested columns[].items response is correct; the frontend reads were the bug. Column membership is derived from each column's status_filter rather than stored on the item, so the grouping only exists in board context — flattening server-side would discard the relationship the endpoint exists to compute. Rationale in the PR body.
All four Ask items delivered. Two things beyond the issue text, found by grepping every caller of the endpoint rather than only the two files named:
GanttTimelineView.spec.ts:62 is why nothing reported this — its fixture mocked { items: [...] }, a shape the API never returns, so the test asserted the broken contract and stayed green through a total feature failure. Correcting the fixture alone turns it red.
Also corrected the workItemTypes.ts provenance comment twice: the original was inverted, and the first correction was still wrong. assignee_type is emitted by both the work-items response (work_items.py:381) and now the board-items response; only column_id is board-items-only.
Closing #13954 here — its stated fix is fully delivered and no 'human' literal remains anywhere in the frontend.
Delivered by PR #14043, merged as 93f64153f. Verified in base: boards.py now emits assignee_type/column_id.
Three causes, all verified empirically rather than taken from the report: the frontend read a top-level items key the endpoint never returns (items are nested per column), _work_item_summary() never emitted assignee_type/column_id, and the frontend filtered on assignee_type === 'human' while the backend only ever writes 'user' or 'agent'.
SprintBoardView had the identical nesting bug against the same endpoint and was fixed in the same PR — one root cause, one change.
Review confirmed the part that mattered: no writer of 'human' exists anywhere (checked every writer plus migrations and fixtures), so the 'user'-only filter cannot silently drop cards. The only "human" literal in the module belongs to an unrelated CoWorkerType enum.
Follow-up #14044 — now that cards render, clicking one opens a detail panel with blank description, labels and acceptance criteria: the board payload omits them and WorkItemDetail never re-fetches. The backend has that data, so it reads as data loss.
Activity
docs(llc/frontend): correct the workItemTypes assignee_type/column_id…
fix(llc/frontend): flatten nested board items in the Gantt board scope (
mrveiss commentedon Aug 11, 2026
PR #14043 — awaiting CI + review.
Canonical shape decision: the nested
columns[].itemsresponse is correct; the frontend reads were the bug. Column membership is derived from each column'sstatus_filterrather than stored on the item, so the grouping only exists in board context — flattening server-side would discard the relationship the endpoint exists to compute. Rationale in the PR body.All four Ask items delivered. Two things beyond the issue text, found by grepping every caller of the endpoint rather than only the two files named:
GanttTimelineView.vue:420had the same bug — board-scoped timelines (UX: sprint-board Timeline button opens company-wide Gantt (loses board/sprint context) — scoping decision #11701) resolved to an empty id set and filtered the roadmap to zero rows. Same root cause, fixed here.GanttTimelineView.spec.ts:62is why nothing reported this — its fixture mocked{ items: [...] }, a shape the API never returns, so the test asserted the broken contract and stayed green through a total feature failure. Correcting the fixture alone turns it red.Also corrected the
workItemTypes.tsprovenance comment twice: the original was inverted, and the first correction was still wrong.assignee_typeis emitted by both the work-items response (work_items.py:381) and now the board-items response; onlycolumn_idis board-items-only.Closing #13954 here — its stated fix is fully delivered and no
'human'literal remains anywhere in the frontend.Filed, not fixed here:
api.get<>generics in LLC views;_generated/has zero LLC coverage).Mutation proof (4 mutations, red→restore→green) and all verification output are in the PR body.
fix(llc): flatten nested board items in the Gantt board scope (#13993)
fix(llc): read board items from the nested column shape the API sends (…
fix(llc): flatten nested board items in the Gantt board scope (#13993)
docs(llc): correct the boards composable call list (#13993)
fix(llc): render Kanban and Sprint board cards from the nested column…
mrveiss commentedon Aug 11, 2026
Delivered by PR #14043, merged as
93f64153f. Verified in base:boards.pynow emitsassignee_type/column_id.Three causes, all verified empirically rather than taken from the report: the frontend read a top-level
itemskey the endpoint never returns (items are nested per column),_work_item_summary()never emittedassignee_type/column_id, and the frontend filtered onassignee_type === 'human'while the backend only ever writes'user'or'agent'.SprintBoardViewhad the identical nesting bug against the same endpoint and was fixed in the same PR — one root cause, one change.Review confirmed the part that mattered: no writer of
'human'exists anywhere (checked every writer plus migrations and fixtures), so the'user'-only filter cannot silently drop cards. The only"human"literal in the module belongs to an unrelatedCoWorkerTypeenum.Follow-up #14044 — now that cards render, clicking one opens a detail panel with blank description, labels and acceptance criteria: the board payload omits them and
WorkItemDetailnever re-fetches. The backend has that data, so it reads as data loss.