## Thinking Path > - Paperclip lets people manage work done by agents. > - Tasks, Inbox, and Needs You must stay usable as work history grows. > - The Coordinator views bypassed some existing collection optimizations. > - Navigation fetched complete decision cards for a count, and Tasks mounted every loaded row. > - This PR uses the canonical attention rules for small count and task-ID responses. It also restores bounded rendering and the optimized Inbox selection. > - The benefit is less data and less initial rendering work without a second definition of pending work. ## Linked Issues or Issue Description **What happened?** Coordinator navigation fetched the full attention feed on every cold collection visit. Tasks rendered the entire loaded collection. Coordinator could also select the old Inbox loader when Streamlined UI was off. **Expected behavior** Fetch only the projection needed by each surface. Start with a bounded set of rows. Keep complete task membership, accurate counts, current permissions, and stable loaded rows. **Steps to reproduce** 1. Enable Coordinator and open Tasks or Inbox with an empty browser query cache. 2. Observe a complete attention-card download for the navigation badge. 3. Open a large task list and observe that every loaded row mounts at once. 4. Disable Streamlined UI while keeping Coordinator enabled, then open Mine and observe three overlapping compact task reads. Related work: #17756, #3744, #17983, and #18312. ## What Changed - Add board-only attention count and complete task-ID projections. Reuse canonical company, privacy, addressee, deduplication, dismissal, snooze, and archive rules. Skip card bodies, plan/image reads, and presentation roster lookup. - Share the small count read across desktop and mobile navigation. Reuse the page-owned response on Needs You and Needs attention. Keep the newest known count through navigation, including zero. - Use the task-ID index for Needs attention and keep task row hydration in batches of at most 100. - Render 100 task rows initially, across the whole expanded tree. Reveal 150 more at a time through scrolling or a button. Keep existing rows through refresh and append. Defer offscreen leaf layout with shared tokens. - Keep Coordinator on the optimized Inbox when Streamlined UI is off. Use index reads for expanded project recents. - Add real-database, component, and production-build browser regression checks. Update the access and feature maps. ## Verification - All required CI checks pass on `1c68ac8957490a6c8585ba0939355f536c8120bf`: typecheck, build, server/workspace tests, serialized database suites, and eight E2E shards. Greptile is **5/5**, with its row-retention finding fixed and the review thread resolved. Storybook checks were intentionally skipped; Paperclip Review completed neutral. - Workspace typecheck and production build passed locally. The final rebased source also passed UI typecheck and production build. Token gates and map checks passed. - The attention service suite passed all 34 tests on disposable PostgreSQL. Final targeted checks cover compact projections, complete-set counts, quality approvals, snoozes, private tasks, and board/company access. Database response capture confirms compact projections do not read plans or images. All 31 OpenAPI tests passed after registering both endpoint contracts. - Six production-build browser journeys passed: desktop, mobile, Coordinator Inbox with Streamlined UI off, Needs You, Needs attention, and an older server page followed by task open/Back. They verify the initial 100-row budget, scrolling, retained DOM identity/height, one owner per attention read, complete task membership, 220 retained rows on Back, and less than 2 px of return-position difference. - The row-budget and Inbox regressions fail against original source and pass with this change. The final three affected UI suites passed 40 tests, and the broader Inbox/IssuesList suites passed 101 tests serially. ### Controlled production-build comparison Baseline `b31ec0a2dc079e32272a8f618425fe92b68bbaec` versus PR head `1c68ac8957490a6c8585ba0939355f536c8120bf`. Isolated baseline asset `index-CS9uPxMx.js`; candidate asset `index-BxJeTQ30.js`. Both UI builds used the same warm disposable API server and database, with 220 tasks and 110 pending board approvals. Chromium **151.0.7922.34**, 1440 × 900 viewport, **4× CPU throttle**, loopback with no added network latency, alternating baseline/candidate order. Cold cases use a new browser context with network cache disabled; warm cases navigate within the same context. Other host workloads are uncontrolled. Readiness requires the destination collection's first visible task row, then two animation frames. Inbox readiness waits for its canonical Mine route so previous-page rows cannot satisfy the measurement. Five paired samples per case; cold Inbox has a second five-pair confirmation wave because the first result was slower. Times below are **milliseconds**; parentheses show min–max. Paired savings are the median of individual before-minus-after pairs, not the difference between medians. | Journey | Pairs | Baseline median (range) | Candidate median (range) | Median paired saving | Faster pairs | | --- | ---: | ---: | ---: | ---: | ---: | | Cold Tasks | 5 | 2752 (2542–3025) | 2300 (2275–2524) | 328 | 5/5 | | Cold Inbox | 10 | 2536 (2283–2927) | 2582 (2117–2856) | 37 | 5/10 | | Warm Tasks navigation | 5 | 2054 (1826–2479) | 1448 (1419–1548) | 635 | 5/5 | | Warm Inbox navigation | 5 | 2020 (1485–2254) | 1568 (1289–1848) | 350 | 5/5 | | Task → Back to Tasks | 5 | 1747 (1609–1948) | 1327 (1238–1355) | 472 | 5/5 | Cold Tasks, warm Tasks, warm Inbox, and Back improved in every measured pair. **Cold Inbox latency is inconclusive:** the two waves pointed in opposite directions, with the candidate faster in 5/10 pairs. This PR claims less badge work there, not a proven cold Inbox latency improvement. The canonical badge response shrank from **200,039 to 359 decoded bytes** (encoded HTTP transfer: **8,125 to 598 bytes**). The compact response also skips card payloads, plans, images, and presentation-only roster work. These are measured mechanism reductions, separate from end-to-end latency. Needs You still owns its full card feed; Needs attention owns its complete task-ID projection. Reproduction setup and assertions are in `tests/perf/collections/README.md`. <details> <summary>Individual paired readiness measurements (ms)</summary> | Journey | Pair | Baseline | Candidate | | --- | ---: | ---: | ---: | | Cold Tasks | 1 | 3025 | 2283 | | Cold Tasks | 2 | 2542 | 2275 | | Cold Tasks | 3 | 2752 | 2424 | | Cold Tasks | 4 | 2783 | 2300 | | Cold Tasks | 5 | 2641 | 2524 | | Cold Inbox | 1 | 2927 | 2702 | | Cold Inbox | 2 | 2422 | 2629 | | Cold Inbox | 3 | 2409 | 2606 | | Cold Inbox | 4 | 2621 | 2769 | | Cold Inbox | 5 | 2539 | 2856 | | Cold Inbox | 6 | 2283 | 2117 | | Cold Inbox | 7 | 2466 | 2558 | | Cold Inbox | 8 | 2674 | 2286 | | Cold Inbox | 9 | 2696 | 2389 | | Cold Inbox | 10 | 2533 | 2183 | | Warm Tasks navigation | 1 | 2479 | 1423 | | Warm Tasks navigation | 2 | 2228 | 1548 | | Warm Tasks navigation | 3 | 2054 | 1419 | | Warm Tasks navigation | 4 | 1826 | 1448 | | Warm Tasks navigation | 5 | 1954 | 1518 | | Warm Inbox navigation | 1 | 2020 | 1661 | | Warm Inbox navigation | 2 | 2198 | 1848 | | Warm Inbox navigation | 3 | 1733 | 1497 | | Warm Inbox navigation | 4 | 2254 | 1568 | | Warm Inbox navigation | 5 | 1485 | 1289 | | Task → Back to Tasks | 1 | 1721 | 1330 | | Task → Back to Tasks | 2 | 1747 | 1275 | | Task → Back to Tasks | 3 | 1948 | 1355 | | Task → Back to Tasks | 4 | 1609 | 1327 | | Task → Back to Tasks | 5 | 1790 | 1238 | </details> ### Failed attempts and limits - The local full repository command hit embedded PostgreSQL startup failures and 16 failures in the untouched native session executor suite; it was stopped. The full local suite is not claimed green. CI on the final revision provides the broad test result. - A concurrent local UI run hit timing failures under host contention. Inbox/IssuesList subsequently passed all 101 tests serially. No tests were weakened to address these timeouts. - CI caught two integration gaps introduced during this work: missing OpenAPI entries for the new endpoints, and an attention fixture incompatible with the routine filters added on master. Both were fixed; the affected 31 OpenAPI and 40 UI tests pass. - The final revision's first CI attempt failed two untouched tests: scheduled-retry batch cancellation and the tool-access audience/archive race. Both failed jobs passed when rerun without changing the revision. Their original failed attempts remain visible in [the workflow history](https://github.com/paperclipai/paperclip/actions/runs/41083379875). - Browser evidence uses synthetic disposable data. This PR is not deployed staging or production verification. ## Risks - New attention sources must preserve canonical eligibility. Both projections call the existing collector, so they share its permissions and complete-set behavior. - The count and task-ID caches must not stand in for full decision cards. Each projection has its own key under the existing attention invalidation prefix. - A row budget can omit descendants if applied per parent. The tests cover one global budget across a large expanded tree, refresh, append, reset, and incremental loading. - Local browser evidence uses synthetic data and is not staging deployment proof. ## Model Used OpenAI Codex, GPT-6 family, with reasoning, tool use, and code execution. The exact backend model deployment ID and context window were not exposed in this session. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass (affected suites; broad-run limitations are listed above) - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
9.8 KiB
Inbox, decisions, and search — agent access
Product map: Inbox, decisions, and search.
Feature ID: inbox-search.
Review date: 2026-10-12. Source commit: f981fa3cc10cb8663a87dec60345fbff46d54d0a.
This is a source-reviewed baseline of representative operations and known gaps. Each capability needs operation-level review before its entire scope can be qualified. No live agent execution is claimed. Follow the map contract for format, permission checks, evidence and maintenance.
views
Capability ID: inbox-search/views.
The issue-list API accepts optional includeAgentCreatedTasks; false restricts
independent results to human creation before search/pagination. Omission retains
existing agent/API behavior. includeParticipatedTasks=true requires a board user
and retains only that authenticated user's existing participation as a creation
visibility exception. Company/private-task access checks still apply. Collection
preferences and hierarchy presentation are browser state, not additional authority.
Source-reviewed behavior; live agent access verification remains separate.
Collection projections view=index and view=rows use the same issue-list
company/private-task checks. Optional ids and parentIds batches accept at most
100 UUIDs and cannot be combined with blocked attention. They grant no new access.
listProjection marks omitted task bodies so clients do not reuse them as detail.
The sidebar count endpoint returns only readable unread Mine task IDs; failed-run
counts apply the run read decision. No new agent execution or mutation path is added.
Review: baseline.
API: present — representative operation IDs:
GET /api/agents/me/inbox-lite.GET /api/agents/me/inbox/mine.GET /api/companies/{companyId}/issues.
Agent access: partial — REST / native call_api; MCP inbox/task search.
Policy: Own agent context; responsible-user inbox policy and explicit opt-in/grants for cross-user archive; decision policy separately. Automatic read cleanup does not count as user participation, including historical reader-attributed rows. Explicit user recovery decisions still count. Automatic creator attribution, synchronized comments, and manual routine runs do not count. Direct board/API comments, human assignment, and open addressed requests qualify. These admission rules do not grant task access. Historical questions and requests on closed tasks do not qualify. Request unread state uses the person's read or reply time, independent of automatic task updates.
Evidence: source-only — implementation at
the reviewed commit above. The cleanup exclusion in
issue queries was reviewed at
89770ba707fb1ebd4cabd184eec2e7fee226e0c3.
Issue service regressions
cover membership, unread counts, timestamps, and retained audit rows.
Verification: not-run — exercise a listed permitted operation with actual agent
credentials/run context; inspect its result and applicable persistence/attribution.
Assert the relevant denied actor, owner, company or mode from Policy. Review the
remaining operations before qualifying the whole capability. Seed a completed
issue with only automatic read cleanup under a user identity: Mine and Unread
must exclude it while activity history retains the row. An explicit recovery
decision must still establish participation. No live user-inbox proof is claimed.
Gaps: Agent inbox and Mine support exist; board attention/unread/dismissal aggregation is a separate user-only surface.
triage
Capability ID: inbox-search/triage.
Review: baseline.
API: present — representative operation IDs:
POST /api/issues/{id}/inbox-archive.DELETE /api/issues/{id}/inbox-archive.
Agent access: partial — REST / native call_api; existing garden-inbox workflow.
Policy: Own agent context; responsible-user inbox policy and explicit opt-in/grants for cross-user archive; decision policy separately.
Evidence: source-only — implementation at
the reviewed commit above.
Verification: not-run — exercise a listed permitted operation with actual agent
credentials/run context; inspect its result and applicable persistence/attribution.
Assert the relevant denied actor, owner, company or mode from Policy. Review the
remaining operations before qualifying the whole capability.
Gaps: Archive has an agent-aware responsible-user policy. Personal read/unread/dismissal does not have equivalent agent semantics everywhere.
decisions
Capability ID: inbox-search/decisions.
Review: baseline.
API: present — representative operation IDs:
GET /api/companies/{companyId}/decision-queues.GET /api/companies/{companyId}/decision-queues/{key}/items.GET /api/companies/{companyId}/attention/summary— canonical outstanding counts without decision cards, images, plan bodies, or pagination. Board user and company access are required. Task privacy, addressee, dismissal, snooze, archive, and complete-set counting rules match the attention feed. This read grants no agent or resolution authority.GET /api/companies/{companyId}/attention/tasks— the same canonical counts and distinct accessible task IDs for Needs attention. It has the same board, company, privacy, addressee, dismissal, snooze, and archive gates as the feed.GET /api/companies/{companyId}/attention/history— read-only canonical interaction/Decisions-v1 projection with cursor, search, outcome, agent, project, date, and chronological sort filters. Each source and v1 target must pass task privacy checks. Bounded to 100 records per page; no second decision write store.
The history route is operator-only: a board user with company access is required. It does not grant an agent resolver authority. Existing task interaction and decision routes remain the mutation owners. Source evidence: history service and route. Real temporary PostgreSQL regression coverage is in decision-history.test.ts; this is not a deployed user/agent credential test.
GET /api/companies/{companyId}/attention also projects existing pending
interactions from persistent agent chats, including ask_user_questions, under
the same board, company, task-read, and addressee rules. The execution-container
exclusion does not suppress an explicit human request. Resolution still uses
the owning interaction API and its resolver policy; ordinary chat prose and idle
review status do not become decision records. Source and isolated PostgreSQL
coverage: attention service and
regressions.
Agent access: partial — REST / native call_api under decision queue authorization.
Policy: Own agent context; responsible-user inbox policy and explicit opt-in/grants for cross-user archive; decision policy separately.
Evidence: source-only — implementation at
the reviewed commit above.
Verification: not-run — exercise a listed permitted operation with actual agent
credentials/run context; inspect its result and applicable persistence/attribution.
Assert the relevant denied actor, owner, company or mode from Policy. Review the
remaining operations before qualifying the whole capability.
Gaps: Queue browsing/managing may be allowed; governed approval decisions still have separate board/resolver boundaries.
search
Capability ID: inbox-search/search.
Review: baseline.
API: present — representative operation IDs:
GET /api/companies/{companyId}/search.GET /api/companies/{companyId}/issues.
Agent access: available — Native search_tasks for tasks; REST / native call_api for
broader search.
Policy: Own agent context; responsible-user inbox policy and explicit opt-in/grants for cross-user archive; decision policy separately.
Evidence: source-only — implementation at
the reviewed commit above.
Verification: not-run — exercise a listed permitted operation with actual agent
credentials/run context; inspect its result and applicable persistence/attribution.
Assert the relevant denied actor, owner, company or mode from Policy. Review the
remaining operations before qualifying the whole capability.
Gaps: General company search and dedicated task search are different result sets; use the appropriate one.
toasts
Capability ID: inbox-search/toasts.
Review: baseline.
API: present — GET /api/companies/{companyId}/events/ws (WebSocket upgrade); viewer-specific
toastEligible metadata on authorized events. No notification mutation endpoint.
Agent access: not-applicable — toast presentation is a human browser concern.
Agent-authenticated event subscriptions retain authorization but do not qualify
for human toasts. Agent task mutations may notify participating humans.
Policy: Company membership and task/run read checks precede human participation checks. Creator, current human assignee, commenter, deliberate actor, or explicit user mention qualifies; agent ownership and responsible-user attribution alone do not. See toast semantics.
Evidence: source-only — socket projection
and eligibility.
Verification: not-run — Connect two users and an agent; assert per-user eligibility without
losing authorized cache updates. Deny revoked membership and private task access.
Confirm relevance lookup failure yields a quiet authorized event.
Gaps: Browser recipes and regression sources are not deployment acceptance proof.