## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Task lists and inboxes are the main entry points to that work. > - Cold opens fetch overlapping task lists, task descriptions, and run details before useful rows settle. > - Independent responses can insert rows above content that a person has started to read. > - This pull request loads a small task index first and adds details only for visible rows. > - It keeps row order and geometry stable while those details arrive. ## Linked Issues or Issue Description **What existing behavior does this improve?** Cold opens of Tasks and Inbox, including the shared navigation badge. **Subsystem affected** Server list reads, shared response contracts, and the React UI. **Current behavior** The badge fetches full task bodies. Mine fetches several overlapping lists. Historical diagnostics and row metadata add work before the first useful list. A fast failed-run response can appear before task rows, then move down when tasks arrive. **Proposed behavior** Load the selected inbox source and its required context together. Use an index without task descriptions or recovery diagnostics. Fetch diagnostic details for up to 50 visible rows. Keep skeletons, recovery indicators, external status, and live metadata in fixed space. **Reason and benefit** Reduce work on a cold open and prevent loading-induced row movement. On the same 100 local fixture tasks, the compact response was 262,441 decoded bytes and the index was 110,909 bytes. This comparison excludes compression and is not a page latency benchmark. **Breaking changes** Existing full and compact responses remain available. New index/row projections are opt-in and marked as partial data. Mine keeps its existing most-recent-100 display limit, now selected on the server. Existing filters and group preferences remain in the client. No database migration. **Additional context** Refs: #17593. Related: #13095, #17428, #13803. This change addresses collection overfetch and layout stability. It does not replace the blocked-inbox query work or the database activity-index work. ## What Changed - Add authorized index and row projections, with bounded task and parent ID batches. - Replace badge list downloads with unread IDs and aggregate counts. Preserve actor privacy, dismissals, and local archive behavior. - Load only the selected inbox source, plus bounded parent and failed-run context. Return only relevant failures from the bounded run window. - Batch hierarchy reads, including at most one authorized child per collapsed parent in one request. Collapsed markers do not consume the expanded row budget, and partial descendant counts are not shown as totals. Enrich visible rows without replacing membership, ordering, titles, or read state. - Reserve row geometry on desktop and mobile. Reveal the initial inbox only after its sources settle. Preserve legacy task-list reads. - Start collection module downloads during access checks. Keep partial rows out of the task-detail cache. - Keep ETags and concurrent request sharing for the new projections, but skip the short server response TTL so archive undo sees fresh state. ## Verification - On final head `ef6afb2a56`, the full CI workflow passed, including build, typecheck, server/workspace/runner suites, and all eight browser shards. A server shard reported **1,489 passing tests** before runner shutdown canceled the job; its single infrastructure retry passed. Both Greptile and Paperclip Review are **5/5**, with zero unresolved review threads. - The optional external **Entire Gates** check remains `unassessed` because it has no active assessed Change for this comparison. Entire is not set up in this checkout. The repository ruleset requires `ci / verify` and `ci / e2e`; both passed. No gate configuration was changed. - Passed workspace typecheck after the final API batching change. The full production build also passed on the final commit; the exact-retry E2E run built the shipped UI. - Passed token gates and map validation. The final batch-preview pass has 91 passing route/hierarchy/component tests, including privacy, request bounds, 30 collapsed parents in one read, and a 120-child parent alongside a sibling. - Passed the 166-test focused UI/inbox/badge run, 47 additional UI regression tests, and 34 issue-list route tests. The latest regressions cover low-trust agent badge IDs, cold company selection, 30 description-only Mine matches with archive actions, retrying optional row details, and collapsed/expanded siblings. - The full local `pnpm test:run` spanned the rebase and reported failures in five suites before it was stopped; it is not claimed as a complete pass. A fresh serial rerun of all five suites passed **452/452** tests (tool gateway, native resume, project repositories, workspace recovery, and runner selection). Full current-commit coverage is checked by CI. - Real browser: delayed index and row-detail responses on Tasks and Mine. The first placeholder and first row had the same origin and 44-pixel height. Twelve rows on each page had zero position, height, or title-width change after recovery details arrived. - Real browser: reviewed long titles and recovery status at a 390 by 844 viewport. Task text stays on one line and recovery uses a compact indicator. - Three fresh document loads per page on final commit `260893bf92`, with HTTP cache retained on a warm local development server: Tasks 1.031–1.190 seconds; Mine 0.999–1.193 seconds. Browser-control overhead is included. These values must not be compared directly with staging latency or the earlier local samples taken during a full test run. - The browser archive test exposed stale results after Undo. The new projections now bypass the server TTL; immediate-mutation regression tests pass. The final browser retry passed: Archive followed by Undo restored exactly one row and the badge count. - CI exposed a hook-order crash when company selection resolved after the first inbox render. The hook now runs before the empty-company return. All seven affected exact-retry and legacy-continuation browser scenarios pass locally after the fix. - Real browser review regressions: 97 description-only source matches retained Archive actions; deliberately failed optional row reads displayed a retry notice while all 12 observed row positions and heights stayed unchanged. Retry restored the recovery indicator. Network fault injection was removed. - The final production build and workspace typecheck passed. Final focused verification passed 153 UI/helper tests and 26 heartbeat privacy route tests. Final review regressions cover newer diagnostics arriving during the index read, reloading diagnostics when the same task ID changes version, optional source failure/retry without hiding available rows, and required external-status filter failure. The latest-failures route test verifies bounded selection, agent filtering, success suppressing an older failure, and private-run redaction. - Real browser: deliberately failed the run source, then retried it. Twelve available task rows kept the same position and 44-pixel height before, during, and after retry. No loading skeleton replaced them. Fault injection was removed. - Hierarchy review decision: the cold-open barrier stays because inserting ancestors or expanded children after revealing index rows would cause layout shifts. Existing hierarchy context now survives subsequent requests, with a regression proving a new ancestor read does not blank the list. The bounded recursive tree read can still add latency for unusually deep expanded trees. - Tasks now handles a failed external-status filter with an explicit error and Retry, suppressing a false empty state. All 52 Tasks component tests pass, including failure-to-success retry. One existing large-tree test timed out while workspace typechecking ran concurrently; the whole component suite passed when rerun without that competing work. Workspace typecheck, the full production build, and token gates passed for this fix. ## Risks - Saved filters and grouping still operate on the existing bounded collection. This is not a new global server pagination model. - Fixed row slots reduce available title width. Long titles remain accessible through task navigation. - Recovery and related diagnostics arrive after core rows. Their responses cannot replace ordering fields, and stale versions are rejected. - Index/row requests now revalidate against current data instead of a short server TTL. They still share concurrent reads and use ETags. - This has not been deployed by this pull request. Staging latency must be measured after deployment under comparable conditions. ## Model Used OpenAI Codex, GPT-6 family. The exact runtime model ID and context-window size are not exposed in this session. Used code editing, terminal execution, and real-browser diagnostics. ## 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 - [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>
6.4 KiB
Task list and inbox cold opens
Outcome and ownership
Cold-open /issues and /inbox/* should show useful, correctly ordered rows
without inserting earlier rows or changing row geometry as supporting data arrives.
This work owns the collection read contracts, their UI consumers, and navigation
badge reads. Keep issue authorization, mutation semantics, archive/undo, and
existing grouping/filter preferences with their current owners. The separate
staging performance effort (PR #17745) owns runner event-loop stalls.
Branch: codex/list-cold-open, rebased onto 063a498b54.
Endpoint: tested, reviewable PR with checks and review resolved; local test drive.
No staging infrastructure changes are needed for this implementation.
Evidence and acceptance
Baseline on staging, authenticated browser with HTTP cache retained and fresh application state: task rows ~4.5s; Mine task rows ~6.3s. Mine downloaded four overlapping issue responses (500, 167, 52, 52 rows; 2,388,103 decoded bytes). A failed-run row appeared at y=183 before tasks arrived, then moved to y=415. These are individual observations, not percentile benchmarks.
| Journey | Required result | Evidence |
|---|---|---|
| Cold Tasks / Mine | Bounded initial data; final row order before reveal; metadata fills stable slots | Request sizes, delayed-response browser trace, row geometry |
| Other inbox tabs and saved filters/grouping/nesting | Correct membership/order before paging, no false empty state | Focused contract/component tests plus local browser |
| More results and detail/back | No lost or duplicated rows; reading position preserved | Browser walkthrough and paging regressions |
| Archive/undo/read and background activity | Existing optimistic behavior survives; observed rows stay put | Existing regression suites and browser |
| Slow/failed metadata or list read | Stable layout; useful retry/error; no misleading partial inbox | Deferred request component tests and browser |
| Company/account boundaries | No cross-company or private-row leaks in lists or badges | Authorization tests |
Implementation sequence
- Remove full-list badge reads; preserve personal unread/archive/dismissal semantics.
- Add a lean ordering index using existing pagination/filter contracts, then enrich only visible rows (at most 50). Batch hierarchy reads rather than fetching full task bodies per parent. Mine selects its most recent 100 rows server-side; existing client grouping/filtering remains in place. This is not a new global server-side pagination model for every saved view.
- Commit inbox source data together rather than revealing unrelated responses in arrival order.
- Match skeleton and row geometry. Enrichment cannot replace index membership, sorting fields, or row geometry; retain the existing inbox order pin.
- Preload the requested collection module during access/company checks and stop downloading task detail code eagerly on collection opens.
- Verify in the existing isolated local app, run required checks, prepare PR and resolve review.
Use targeted tests while iterating; full repository verification before PR-ready handoff. Do not infer deployed speedup from local measurements or combine the separate server fix's savings.
State
Implemented the bounded index, visible-row enrichment, compact badge reads, and initial snapshot barrier. Legacy task lists retain their existing compact data. Index and row requests retain ETags and in-flight coalescing but skip the short server response TTL, so an archive undo immediately reads the changed state.
Local browser proof uses 160 seeded tasks, nested rows, long titles, unread activity, and a saved recovery action. On desktop, the first Tasks placeholder and task both start at y=163 with height 44. The equivalent inbox values are y=175 and height 44. Delayed row enrichment produced zero position, height, or title-width change across 12 measured rows on each page. Narrow-screen review at 390 by 844 confirmed truncation and a compact recovery indicator.
Three fresh document loads with retained HTTP cache and a warm local Vite server showed Tasks in 2.054–2.170s and Mine in 2.115–2.268s after the archive/undo fix. These include browser-control overhead while the test suite ran on the same computer. They are not comparable to staging latency. For the same 100 local tasks, the old compact response was 262,441 decoded bytes and the index was 110,909 bytes, with no descriptions.
Workspace typecheck, production build, token gates, map validation, and focused UI/server regressions passed. The initial full local run spanned a rebase and was stopped after reporting failures; a clean serial rerun of all five affected suites passed 452 tests. Fresh current-commit CI is the full-suite acceptance gate.
Review fixes preserve description-only inbox matches through a scoped, on-demand
index search and keep unread task IDs board-only. Collapsed parents use a batched
one-child preview instead of spending the expanded row budget or making a request
per parent. Optional-detail failures show a retry notice outside document flow;
12 measured rows remained unchanged and retry restored the recovery indicator.
All seven exact-run retry/legacy continuation browser scenarios passed after
fixing the company-loading hook order. Archive/Undo, read/back, nesting, remote
search and rendering all 164 fixture tasks without duplicates also passed.
PR: https://github.com/paperclipai/paperclip/pull/17756.
Browser evidence is retained in the chat's list-cold-open-test-drive artifact
directory. Existing task-detail PR #17593 is already merged and verified on
staging; its paused hourly monitor is not part of this change.
The final review pass accepts newer diagnostic snapshots while retaining index ordering, and refreshes visible diagnostics when a task version changes. Optional inbox source failures keep available rows and expose retry outside document flow; required external-status filter failures show an error rather than an endless skeleton. A browser-injected failed run source retained 12 rows at the same positions and heights before, during, and after retry. The initial hierarchy barrier remains intentional to avoid inserting ancestors or children above rows already being read; subsequent hierarchy requests retain the previous context. Server route coverage now verifies latest-failure filtering, bounded windows, agent filters, and private-run redaction. The unused client badge implementation and its obsolete tests were removed; the production server badge tests remain.