Skip to content

fix: authoritative uncapped needs-you counts (TASK-126) - #1574

Merged
lilyshen0722 merged 2 commits into
mainfrom
fix/task-126-uncapped-attention
Sep 6, 2026
Merged

lilyshen0722 merged 2 commits into
mainfrom
fix/task-126-uncapped-attention

Conversation

@lilyshen0722

Copy link
Copy Markdown
Contributor

Summary

  • Make /api/activity/decision-queue count every accessible open item, not only the newest 80; return uncapped count and countsByPod while retaining 12-card/8-mention display limits.
  • Activity and the rail use the global total; pod sidebar, inspector and phone use per-pod totals. Activity's pod filter uses the corresponding pod total. Never substitute recap mentions on queue failure or call an omitted card an empty queue.
  • Refresh attention consumers on acknowledgement/ruling/message actions and window focus. No direction C layout/CSS work and no production repair/sweep.

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

  • Frontend: 628/628 across 94 suites; typecheck and production build pass.
  • Backend: focused service/unit set 16/16; new persisted-query suite also 2/2 against a disposable real MongoDB; backend typecheck passes.
  • Regression fixture has 95 accessible open items across two pods, plus revoked-access, other-recipient and resolved exclusions; checks 12/8 display limits and recount after acknowledgement.
  • Mutations, restored: reintroducing .limit(80) gives 1 red (95 expected, 80 actual); replacing endpoint count with items.length in the hook gives 1 red (91 expected, 0 actual).
  • Browser: production components rendered against a local synthetic API fixture at 1440 and 390. Global 95 vs selected-pod 91, omitted pod still has inspector count, phone badge 91, acknowledgement refresh clears counts. This is local fixture proof, not a signed-in live-data walk. A fixture hot reload logged a duplicate createRoot warning; no production-console-clean claim.
  • Three unrelated BYO unit suites now mock their rail boundary so rail reads cannot consume their order-dependent BYO API responses; rail count behavior has its own test.

Gates / limits

  • Review, fresh-head CI and UX carry required before Lily presses; first independent PR of the tranche.
  • Previously broad-resolved attention remains untouched. Counts reflect persisted open rows, not a correction of those historical rows.
  • TASK-127 catch-up freshness is a separate PR. Direction C awaits rendered artboards.

@lilyshen0722 lilyshen0722 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

  1. countsByPod is uncapped. Built from valid, not picked — and the .limit(80) that used to bound the query is gone, so it is uncapped in both senses. attentionQueue.counts.test.js asserts {busy: 94, omitted: 1}, which is past both the old 80-row read limit and the 12-row display cap.
  2. Sidebar and inspector read it. useV2PodAttention.ts takes data.countsByPod straight through.
  3. Activity's recap fallback is gone. Zero occurrences of recap.needsYou remain in V2ActivityPage.tsx.
  4. The 12-row cap still governs the rendered queue. picked is 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 lilyshen0722 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
lilyshen0722 marked this pull request as ready for review September 6, 2026 12:23

@lilyshen0722 lilyshen0722 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 lilyshen0722 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 b4a276041d894382d. 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 to 53198227cf002fd136f9be98203d2d3ea55e07dc at 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 2330faa2 identical to its blob in 1d894382d. The two that overlap — backend/services/attentionItemService.ts and its unit test — are covered by the signature check above: an identical delta against the newer base means the merged file is 1d894382d'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.

@lilyshen0722
lilyshen0722 merged commit b21cd4f into main Sep 6, 2026
16 checks passed
samxu01 pushed a commit that referenced this pull request Sep 6, 2026
…, 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant