fix: authoritative uncapped needs-you counts (TASK-126) - #1574
Conversation
lilyshen0722
left a comment
There was a problem hiding this comment.
Gate at be61e910. Clear — all four lines from the ruling are met, and each is pinned by a test that fails when I break it.
The four properties
countsByPodis uncapped. Built fromvalid, notpicked— and the.limit(80)that used to bound the query is gone, so it is uncapped in both senses.attentionQueue.counts.test.jsasserts{busy: 94, omitted: 1}, which is past both the old 80-row read limit and the 12-row display cap.- Sidebar and inspector read it.
useV2PodAttention.tstakesdata.countsByPodstraight through. - Activity's recap fallback is gone. Zero occurrences of
recap.needsYouremain inV2ActivityPage.tsx. - The 12-row cap still governs the rendered queue.
pickedis unchanged.
Baselines: backend unit 12/12, the counts service suite 2/2, frontend 26/26 across the four affected suites.
Mutations, including one that corrected me
Deriving countsByPod from the capped picked list — the exact defect this task exists to fix:
- against
attentionItemService.test.js(unit): 12 passed, no red - against
attentionQueue.counts.test.js(service): 1 red
I ran the unit suite first and was about to report the property unpinned. It is pinned; I had simply mutated against the wrong file. Worth recording because the unit suite is the one a reviewer reaches for by habit, and on this property it cannot see the difference.
Second mutation, on the consumer side: making the hook rebuild per-pod counts from data.items — the original bug from #1549 — gives 1 red. So both halves are guarded, the endpoint's and the frontend's.
Note on the new counts suite
attentionQueue.counts.test.js uses setupMongoDb without an INTEGRATION_TEST gate, so it runs on mongodb-memory-server locally and in the ordinary Tests job — not only in the real-DB tier. That is the right choice for this property and it means the guard is reachable without the tier that has been flaky today.
It does call setupMongoDb/closeMongoDb, the same process-global pair implicated in the showcase ordering problem on #1573. No sign of trouble here, and this file seeds and clears its own data, so noting it rather than raising it.
Unchanged
The 79 items stamped 'replied' under the old broad filter still want a corrective pass, and they are the population these counts are now accurately reporting.
lilyshen0722
left a comment
There was a problem hiding this comment.
UX-GATE: CHANGES_REQUESTED @ be61e91 — one item: the rail count is an MUI (20px round pill, Roboto 12px, theme colour, padding 0 6px) inside the Signal rail. Render the same element the mobile tab already uses (.v2-mobile-tabs__badge: 16px, mono 700 10px, cobalt, white ring) as a .v2-rail__badge twin — one count, one shape on both icon surfaces, no MUI in the v2 shell. Note, not a block: with the hook now throwing when countsByPod is absent, the inspector's needs-you card renders neither 'Nothing open.' nor a count (walked at 1440 against the live API, which predates this PR) — same will happen on any endpoint error; consider keeping the empty copy for attentionCount === null. Zero state otherwise identical to b4a2760 at 1440/390 (grid 56/232/788/300, header 64, composer 111, tab bar 54, Activity empty copy).
lilyshen0722
left a comment
There was a problem hiding this comment.
UX-GATE: APPROVED @ 2330faa — supersedes the CHANGES_REQUESTED stamp: per lily-shen's ruling (pod 64342) the rail badge shape (.v2-rail__badge twin of .v2-mobile-tabs__badge, mono 11 per the C floor) moves to PR 3 as its first item and #1574 presses on code gate + CI. Still carried into PR 3: the null-count inspector state (show 'Nothing open.' when attentionCount is null, not a blank card).
lilyshen0722
left a comment
There was a problem hiding this comment.
CODE GATE: carried @ 2330faa2 — content-identical to the gated head be61e910.
2330faa2 is a merge commit, parents be61e910 + 1d894382d; the PR's own base moved b4a27604 → 1d894382d. Compared base-relative rather than by tree, since a base move relocates hunk context:
git diff $(git merge-base origin/main <head>) <head>, keeping only+/-lines and dropping the+++/---headers, sha1s to53198227cf002fd136f9be98203d2d3ea55e07dcat both heads. The PR contributes exactly the same lines relative to two different bases.- The merge dropped nothing of main's. Of the 20 files main moved between the two bases, every one that is not a PR file has a blob in
2330faa2identical to its blob in1d894382d. The two that overlap —backend/services/attentionItemService.tsand its unit test — are covered by the signature check above: an identical delta against the newer base means the merged file is1d894382d's version plus this PR's delta, so #1573's narrow resolver survives intact.
The four properties I mutated at be61e910 therefore stand without re-running: countsByPod built from valid with .limit(80) removed (deriving it from picked gives 1 red in attentionQueue.counts.test.js), the consumers reading it (1 red), recap.needsYou at zero occurrences in V2ActivityPage.tsx, and the 12-row cap retained.
CI at this head: 11 pass, 2 skipping, Test & Coverage still in progress; gh run list --branch shows no startup_failure and nothing absent from the check list.
One coordination note, not a gate objection: #1577 (the replied-stamp audit) touches the same two overlapping files. git merge-tree --write-tree 2330faa2 acf4638ac is clean, so the two can land in either order.
…, 34px pitch, sizes, phone meta Merges main so the sidebar reads `attention.countByPod` (#1574's uncapped totals) instead of reducing the capped list — the regression sprint-review caught. Deletes the legacy `.v2-pods__row { margin-bottom: 6px }` block so the pitch is the board's 34px. Adds the pin/unpin control as a sibling of each row (hover, focus, or always when pinned) so Pinned can fill. Kind heads indent 24px, row name 14/500, description 13px, search focus is the 2px inset cobalt ring, Pinned and Recent collapse and remember it for the session, the 390 pod header shows the compact `N · N agents` meta. The 42-pod state is a jsdom fixture in the scale test. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JavWvyVL478UfEhJjcfMgX
Summary
/api/activity/decision-queuecount every accessible open item, not only the newest 80; return uncappedcountandcountsByPodwhile retaining 12-card/8-mention display limits.Implements TASK-126, rulings 63938 / 64287 / 64301. The rail had no badge on main; this adds the requested count with the existing MUI Badge component, keeping the rail dimensions and CSS unchanged. Inspector exposes its full total and an Activity exit even when none of its cards are in the capped sample.
Verification
.limit(80)gives 1 red (95 expected, 80 actual); replacing endpoint count withitems.lengthin the hook gives 1 red (91 expected, 0 actual).Gates / limits