## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - People open a task to read saved work and reply. > - The conversation waits for its initial data so messages do not move after reveal. > - The hidden conversation and editor still render while those reads are pending. > - Profiling showed a useful cold-load gain when that hidden rendering was delayed. > - Prepare ordinary task content after its initial data is ready. Keep the existing preparation path for comment links. > - Keep the skeleton, measured reveal, timeout, and mounted conversation during refresh. ## Linked Issues or Issue Description Follow-up to #16862 and #16849. Related task-layout work: #14758. Searches found no overlapping open rendering change. **What existing behavior does this improve?** Opening a task mounts hidden message rows and the rich editor while supporting reads are still pending. These trees can render again as data arrives before the user can see or use them. **Subsystem affected** UI task conversation startup and its browser acceptance suite. **Proposed behavior** Keep the existing skeleton and outer layout containers. Prepare the hidden conversation and composer when initial history is ready, then use the existing measured reveal. Keep content mounted after reveal. Hash navigation retains eager preparation so older comment targets can be positioned before reveal. The existing 15-second timeout still reveals available history and the composer. ## What Changed - Gate concealed history and composer children with the existing history readiness, revealed-task latch, timeout, and scroll-navigation hash. - Keep data queries, shell geometry, scroll ownership, and the reveal frame unchanged. - Update unit coverage for deferred content, comment-link preparation, task changes, history timeout, and mounted editor identity during refresh. - Add a real browser journey that holds a background task read, retains the same editor and draft, reloads the saved draft, sends one reply, and checks the persisted comment independently. - Document the loading and draft acceptance paths. ## Verification - Repository-wide `pnpm -r typecheck` and `pnpm build` passed locally. Token gates and all 231 task-thread unit cases passed. - Seven regression cases failed against the old rendering behavior before the source change. - All 14 loading and layout browser cases passed again against the final production UI and API build at `15c360aa63e01ad9318aaf7865c07d1ee0d36968`. Coverage includes delayed initial data, reading anchors, media growth, composer sizing, older history, cold comment links, desktop/mobile same-task links and Back, rapid switching, reduced motion, failed reads and retry, stalled history, plan reuse, and cached-task profiling. - The new draft journey retained the same editor during a blocked background task refresh, restored the draft after reload, and persisted exactly one reply. - Final performance comparison used current master `249ab47d41834f174ddd5e9523dc07fa8ebd8e4c` and implementation `15c360aa63e01ad9318aaf7865c07d1ee0d36968` against the same local 160-message Markdown conversation and API process. Conditions: 50 ms network latency, 9 Mbps down/1.5 Mbps up, 4x CPU slowdown, fresh browsers, five alternating pairs per scenario, no CPU profiler, and no concurrent local build/test work. Warm task navigation improved in all five pairs: median paired saving **405 ms**; variant medians **1,787 → 1,456 ms**. Cold results were mixed: paired saving **−89 ms** (slower), despite variant medians **3,188 → 3,021 ms**. There is no consistent cold-load gain established by this comparison. - Earlier prototype comparisons showed paired cold savings of 259–283 ms. Those gains did not consistently reproduce in the final comparison. These are bounded local measurements and do not establish deployed staging latency. - The first prototype delayed comment-link preparation too and failed the older-comment anchor check by 240.5 px. The narrowed implementation keeps hash navigation on the existing eager path. That case and the full suite now pass. The original failure trace is retained locally. - A visual browser walkthrough read the saved conversation and plan, restored a draft after reload, sent it once, reloaded again, and independently checked that exactly one reply was saved. - Greptile reviewed this exact commit at 5/5, with no actionable findings or inline comments. - The duplicate local `pnpm test:run` attempt was interrupted while the same suites were completing in sharded CI so local performance could be measured on an idle host. It is not counted as a local full-suite pass. - All current-head [CI jobs passed](https://github.com/paperclipai/paperclip/actions/runs/39296740548), including the server, workspace, Runner, serialized, browser, typecheck, build, canary, and final verification gates. The first run failed one native-process supervision test (`preserves a real listening process and its marker when a health RPC times out`). Its unchanged shard and dependent gate passed on retry. The specific test also passed locally. No source change was made for that transient failure. ## Risks Delaying preparation can change initial scroll geometry. Hash navigation keeps the existing preparation path because it needs the target present before positioning. The revealed-task latch keeps refreshes from removing history or the editor. The existing timeout still makes saved content and the composer accessible when reads stall. No API, database, permission, or provider changes. ## Model Used OpenAI Codex, GPT-6, with reasoning, repository tools, code execution, and browser verification. A more precise runtime model identifier and context-window size are unavailable. ## 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>
Issue-detail performance baseline
Run the repeatable baseline from the repository root:
pnpm exec playwright test --config tests/perf/issue-detail/playwright.config.ts
The rig starts an isolated seeded Paperclip instance, runs five samples for each scenario/profile, and writes median-ready raw data plus a Markdown table to test-results/issue-detail-perf/.
Scenarios:
- S1 warm in-app navigation: loads the Issues list, clears the waterfall, and clicks the seeded issue.
- S2 cold open: creates a fresh browser context and deep-links to the seeded issue.
Profiles:
- Unthrottled.
- Fast 4G network with 4x CPU slowdown.
Override the sample count (minimum five) or port when needed:
PAPERCLIP_ISSUE_PERF_RUNS=7 PAPERCLIP_ISSUE_PERF_PORT=3210 pnpm exec playwright test --config tests/perf/issue-detail/playwright.config.ts
Outputs include baseline.md, baseline.json, and a Chrome trace for the first run of each scenario/profile. Open *.trace.json in Chrome DevTools Performance to inspect the issue-detail:* user-timing marks.
Set PAPERCLIP_ISSUE_PERF_PROFILE=1 to capture actual Chrome CPU samples and
conversation-readiness transitions in one diagnostic sample per scenario/profile.
Import the resulting *.cpuprofile in DevTools and inspect *.readiness.json for
the task, comments, metadata, activity, runs, runtime, plan and transcript gates.
Each gate records changes with detail.ready; use its last transition before
content paint when a gate becomes pending after mount. These diagnostic runs
include profiler overhead. Run without the flag for before/after latency claims.
Readiness marks stay in the local browser and contain only fixed gate names and
booleans; the harness enables them before navigation.
For bundle comparisons, build the UI and serve the production assets from an
isolated local instance with PAPERCLIP_UI_DEV_MIDDLEWARE=false. The default
managed server above uses development middleware, which does not reproduce
production chunk loading. Keep the server, database, browser profile and sample
count the same for both builds, and avoid concurrent builds or test suites while
measuring latency.
Copy the first run's output outside test-results/issue-detail-perf, then reuse
its persisted task with PAPERCLIP_ISSUE_PERF_SEED_FILE=/path/to/before/baseline.json
and PAPERCLIP_ISSUE_PERF_BASE_URL pointing to that disposable instance. The
harness validates the task identity before measuring. Network spans in the trace
use recorded request start/end timestamps relative to task navigation; they
include completed requests observed during the sample, even after content paint.
Task layout stability
layout-stability.spec.ts exercises the real task route, including Inbox
navigation, with 160 Markdown comments and a resolved interaction. It delays
initial comments, tests failures and retry, tracks a reading anchor across
media growth, composer resizing, properties toggles, older-page loading and
Back navigation, and covers mobile reduced motion and explicit comment links.
The eight scenarios also exercise same-task hash navigation without remounting
on desktop and mobile, and stalled native/log reads that expose Retry after
the 15-second request deadline.
plan-startup.spec.ts also opens tasks with and without a saved plan while
blocking the separate plan endpoint. Both must reveal the saved conversation
from the task response without issuing that redundant read.
It also checks task-list navigation, where a list-only snapshot needs one normal
plan fetch and must still show a saved plan when present.
Videos and per-frame layout.json measurements are written under each test's
output directory. The two-pixel assertion measures a logical row's viewport
offset; total scroll offset legitimately changes when history is prepended.
Ordinary task opens defer the concealed history and composer until their initial data is ready. The skeleton and scroll/composer containers retain their geometry. Comment-link navigation prepares content eagerly so the target can be positioned before reveal. Revealed content stays mounted during refresh. The draft scenario holds a background task read, checks the same editor remains mounted, reloads the saved draft, sends one reply, and independently checks its persisted comment.
To run against an existing disposable local test-drive instance:
PAPERCLIP_ISSUE_PERF_BASE_URL=http://127.0.0.1:3102 \
pnpm exec playwright test --config tests/perf/issue-detail/playwright.config.ts layout-stability.spec.ts
The suite creates its own company and fixtures. The URL override accepts only loopback origins and rejects remote hosts. Use a disposable local instance, not a shared or production instance. Without the override, the harness starts its own isolated instance as before. Live provider walkthroughs additionally require a configured native Paperclip runner and Codex authentication; deterministic browser fixtures do not substitute for watching an actual provider run.
For the live walkthrough, first assign a disposable task to a native Paperclip
runner configured with the Codex provider. Its scratch project should contain
a small sum.mjs fixture. The script sends a paced job, scrolls up, disconnects
and reconnects, steers a follow-up, and records through completion:
PAPERCLIP_LAYOUT_LIVE_URL=http://127.0.0.1:3102/LAY/issues/LAY-8 \
node tests/perf/issue-detail/live-feed.walkthrough.mjs
Inspect test-results/task-layout/live-acceptance/ for the video, screenshots,
per-frame positions, layout shifts, browser timing counters, and reading-anchor
measurement. The live script rejects non-local URLs and uses real provider
capacity. Review the video as well as the assertions: layout-shift scores alone
do not capture programmatic scrolling or replacement flashes.