Search: Task result preview does not render its Calendar card #1401

Closed
opened 2026-10-07 19:07:46 +00:00 by kayg · 11 comments
Owner

Found by job mailglass-1373 (branch job/mailglass-1373, e2e apps/web/e2e/mailglass-1373.mjs) on origin/dev 0419170fe + its change, which does not touch Search previews: the focused Search run captured the Mail preview at 1440 px, then failed with task preview did not render its Calendar card when it selected a Task result. The remaining Search theme and viewport cases did not run.

Expected: selecting a Task (or a dated Task) in Search shows its preview card in the preview pane at once (DESIGN Search preview rules). Find whether the preview is missing, late (async load without the cached item), or the e2e selector is stale; fix the product if the preview does not render within one frame of selection; add a regression.

Found by job mailglass-1373 (branch job/mailglass-1373, e2e apps/web/e2e/mailglass-1373.mjs) on origin/dev 0419170fe + its change, which does not touch Search previews: the focused Search run captured the Mail preview at 1440 px, then failed with `task preview did not render its Calendar card` when it selected a Task result. The remaining Search theme and viewport cases did not run. Expected: selecting a Task (or a dated Task) in Search shows its preview card in the preview pane at once (DESIGN Search preview rules). Find whether the preview is missing, late (async load without the cached item), or the e2e selector is stale; fix the product if the preview does not render within one frame of selection; add a regression.
Author
Owner

Starting #1401 on branch job/taskprev-1401. Worktree HEAD/base SHA: 0419170fe4. I will reproduce Task and Event previews against the shared prebuilt server, then fix the product or stale selector and add a regression test.

Starting #1401 on branch job/taskprev-1401. Worktree HEAD/base SHA: 0419170fe456b58614befcb12d9c9d255118f1c0. I will reproduce Task and Event previews against the shared prebuilt server, then fix the product or stale selector and add a regression test.
Author
Owner

Finding: the Search preview has a deliberate 70 ms delay before it calls loadTask/loadEvent, and renders only a skeleton until that request returns. In the real browser, the existing Search e2e eventually captured the dated Task and Event cards at 1440 px; it waits up to 15 seconds, so it does not check the first frame. The selectors are current. I am adding a truthful summary card from the loaded Search hit (stable link, title, available tags/location), followed by the full Calendar refresh, and a regression that holds the detail request to prove the summary paints within one frame.

Finding: the Search preview has a deliberate 70 ms delay before it calls loadTask/loadEvent, and renders only a skeleton until that request returns. In the real browser, the existing Search e2e eventually captured the dated Task and Event cards at 1440 px; it waits up to 15 seconds, so it does not check the first frame. The selectors are current. I am adding a truthful summary card from the loaded Search hit (stable link, title, available tags/location), followed by the full Calendar refresh, and a regression that holds the detail request to prove the summary paints within one frame.
Author
Owner

The first Search browser pass showed that a broad detail-request matcher could pause an unrelated or duplicate Task read. That let a full Task card refresh before the first-frame assertion, so the probe did not isolate the selected hit. I narrowed the gate to the selected Search hit's exact Note path alias or Event ID and now hold all matching reads until the assertion completes.

The first Search browser pass showed that a broad detail-request matcher could pause an unrelated or duplicate Task read. That let a full Task card refresh before the first-frame assertion, so the probe did not isolate the selected hit. I narrowed the gate to the selected Search hit's exact Note path alias or Event ID and now hold all matching reads until the assertion completes.
Author
Owner

The clean-capture check found a real build identity mismatch. CURRENT labels the shared server with short revision d395b6e25, but /api/v1/version reports full ID d395b6e25da20d8c67b8b9fc1a593b813939c8a7. The first bundle used the short value and showed the Update available toast. I am rebuilding the SPA with the server's full ID before continuing captures.

The clean-capture check found a real build identity mismatch. `CURRENT` labels the shared server with short revision `d395b6e25`, but `/api/v1/version` reports full ID `d395b6e25da20d8c67b8b9fc1a593b813939c8a7`. The first bundle used the short value and showed the Update available toast. I am rebuilding the SPA with the server's full ID before continuing captures.
Author
Owner

Local #1401 profile setup finding: the profile run failed inside waitForAppearanceLoad() after its font-ready evaluation lost the page execution context during a navigation. The focused preview E2E had passed previously. The screenshot path navigated to /today, then changed Appearance and reloaded the same page; under this host load (/proc/loadavg was 16.12 18.56 22.73), that sequence can observe an earlier Appearance response before the reload. I changed the focused capture to set Appearance before its first app navigation and wait for that navigation once. This is an E2E setup race, not a product preview failure; I am rerunning the profile once against the same shared server build.

Local #1401 profile setup finding: the profile run failed inside `waitForAppearanceLoad()` after its font-ready evaluation lost the page execution context during a navigation. The focused preview E2E had passed previously. The screenshot path navigated to `/today`, then changed Appearance and reloaded the same page; under this host load (`/proc/loadavg` was `16.12 18.56 22.73`), that sequence can observe an earlier Appearance response before the reload. I changed the focused capture to set Appearance before its first app navigation and wait for that navigation once. This is an E2E setup race, not a product preview failure; I am rerunning the profile once against the same shared server build.
Author
Owner

Pre-final integration check: git fetch origin reports this branch at ea132d4f1c81a3d757973681f7a0cb6895bf499e and origin/dev at d395b6e25da20d8c67b8b9fc1a593b813939c8a7; the branch is 4 commits ahead and 395 behind. origin/dev has changes in the SearchPreview, Search E2E and perf baseline files touched by #1401. I did not merge because this job prompt explicitly prohibits merges. Final checks will run on the task branch; the orchestrator must integrate the two sides and resolve any resulting conflicts.

Pre-final integration check: `git fetch origin` reports this branch at `ea132d4f1c81a3d757973681f7a0cb6895bf499e` and `origin/dev` at `d395b6e25da20d8c67b8b9fc1a593b813939c8a7`; the branch is 4 commits ahead and 395 behind. `origin/dev` has changes in the SearchPreview, Search E2E and perf baseline files touched by #1401. I did not merge because this job prompt explicitly prohibits merges. Final checks will run on the task branch; the orchestrator must integrate the two sides and resolve any resulting conflicts.
Author
Owner

Perf-lint finding and exact refresh: the final check found a stale render.bound pin for the SearchPreview.svelte loop at line 801. A targeted live inventory then showed all three SearchPreview render loops had moved exact hashes: 002fe5cdc680c8ed (items 1 and 2) now hashes to 16c3162f3a60cee8…, and aa76dba8f415a674 (item 1) to 1d8d40c536c24264…; the MutationObserver exact hash is f4cc83bf08522287…. I updated only the 36 loop hashes and the observer hash in adoption-1058.json and exceptions.json; exception counts, limits, and the ratchet did not change.

Perf-lint finding and exact refresh: the final check found a stale `render.bound` pin for the `SearchPreview.svelte` loop at line 801. A targeted live inventory then showed all three SearchPreview render loops had moved exact hashes: `002fe5cdc680c8ed` (items 1 and 2) now hashes to `16c3162f3a60cee8…`, and `aa76dba8f415a674` (item 1) to `1d8d40c536c24264…`; the `MutationObserver` exact hash is `f4cc83bf08522287…`. I updated only the 36 loop hashes and the observer hash in `adoption-1058.json` and `exceptions.json`; exception counts, limits, and the ratchet did not change.
Author
Owner

Perf-lint follow-up: after the SearchPreview refresh, the next check found contract.blocked-network at ItemPreview.svelte#each:5207e8419240041e:1. The complete live inventory showed 109 ItemPreview findings and 109 ledger entries with the same rule/symbol pairs; all 109 fingerprints had moved with the #1401 summary markup. I updated those hashes in both ledgers and preserved all 109 entries, their limits, and the ratchet counts.

Perf-lint follow-up: after the SearchPreview refresh, the next check found `contract.blocked-network` at `ItemPreview.svelte#each:5207e8419240041e:1`. The complete live inventory showed 109 ItemPreview findings and 109 ledger entries with the same rule/symbol pairs; all 109 fingerprints had moved with the #1401 summary markup. I updated those hashes in both ledgers and preserved all 109 entries, their limits, and the ratchet counts.
Author
Owner

E2E finding: the fixture creates a Task and a Note entry with the same title. A broad Search1401 hybrid response could contain the Note row while omitting the Task row, so matching by accessible name selected the wrong result or timed out. The regression flow now searches with type:task and the exact fixture title, then asserts the visible data-kind, title and indexed destination before holding the detail read. The Event fixture did not appear for type:event, so its check uses the exact Event title and still asserts the Event row kind. No product search behavior changed for this test correction.

E2E finding: the fixture creates a Task and a Note entry with the same title. A broad `Search1401` hybrid response could contain the Note row while omitting the Task row, so matching by accessible name selected the wrong result or timed out. The regression flow now searches with `type:task` and the exact fixture title, then asserts the visible `data-kind`, title and indexed destination before holding the detail read. The Event fixture did not appear for `type:event`, so its check uses the exact Event title and still asserts the Event row kind. No product search behavior changed for this test correction.
Author
Owner

Finished #1401. Head: 8b53b9736bfb1ac31db5475e11bea9d27d860020.

Built

Search now paints the shared Calendar Task/Event card from fields already loaded in the selected Search hit while the detail request is pending. It shows no guessed date, status, completion state or Task link. The full server detail replaces the summary when it arrives. It uses the existing packages/ui ItemPreview; no new visual control was added.

Files: apps/web/src/lib/search/SearchPreview.svelte, packages/ui/src/components/calendar/ItemPreview.svelte, packages/ui/src/components/calendar/model.ts, apps/web/src/lib/search/CalendarSearchPreviewSummary.svelte.test.ts, apps/web/e2e/search.mjs, bench/search-preview-1401.mjs, docs/perf/baseline.json, contracts/perf/adoption-1058.json, contracts/perf/exceptions.json.

UX gaps closed

Pointer hover, touch long-press, and ArrowDown now check the same first-frame summary and detail refresh. The Task test selects the actual Task row when a same-title Note row exists. Captures assert real fixture content and no update toast; the E2E reported zero page errors and CSP reports.

UX gaps left: none found in this flow.

Screenshots

Real production web build, matching shared server build ID, with macOS platform signals; screenshots are attached at actual size. Mono is light; noir is dark. Each cell links light / dark.

Preview 1440 px 820 px 390 px
Dated Task light / dark light / dark light / dark
Undated Task light / dark light / dark light / dark
Event light / dark light / dark light / dark

Keyboard-shortcut / ArrowDown captures: dated Task, undated Task, Event. These show the Search preview after keyboard selection.

Gates

bun run check:

PASS production dependency licences: 759 locked package releases across apps/web, apps/docs and packages
perf-lint: PASS; 0 violations; 22544 scoped exceptions
Warn: Do not use empty rulesets (css)
svelte-check found 0 errors and 1 warning in 1 file

The one warning is the existing empty rule at packages/ui/src/components/calendar/AttachmentDeck.svelte:1055:3.

Focused Vitest:

Test Files  1 passed (1)
     Tests  2 passed (2)

Search E2E (SEARCH_E2E_1401_ONLY=1, real local server and shared prebuilt binary):

PASS #1401 Task and Event summaries paint within one frame, then refresh at 1440px, 820px and 390px in macOS light and dark themes
PASS #1401 ArrowDown shows dated Task, undated Task and Event summaries before detail reads
CSP REPORTS search: 0 across 2 pages

Performance

Expected cost is O(1) summary rendering from the loaded hit, plus one background detail request for the selected item. The local profile used 52 Tasks, 1 Event, 18 selections and 1440/820/390 px; no pre-change first-frame baseline was recorded. On the shared local host under load (load average 21.71–23.72 before, 18.30–22.02 after), first-frame p50/p95 was 674.20/1137.50 ms and detail-refresh p50/p95 was 731.5/1295.6 ms. A 50-request Task detail burst measured p50/p95/max 439.7/871.3/873.9 ms; server RSS mean/peak was 628,456,107/629,161,984 bytes. The profile is recorded in docs/perf/baseline.json; local results are not comparable to the perf VM baseline.

Decisions and known gaps

DESIGN §32 S7 does not define which Search-hit fields are safe in a Calendar summary. The summary uses the loaded title, Event location snippet, and tags; it waits for detail before showing dates, status, completion controls, or a Task Copy link. The Task link needs the stable Task identity, while the legacy hit destination contains a path alias.

The fixture can return a same-title Note and Task. The regression search uses type:task plus the fixture title and verifies the visible row kind and destination. The Event fixture did not appear for type:event, so its search uses the exact title and checks the Event row kind.

I fetched origin/dev before gates. The explicit job instruction prohibits merging; the fetched refs show this branch is 11 commits ahead and 395 behind origin/dev (d395b6e25da20d8c67b8b9fc1a593b813939c8a7), which overlaps SearchPreview, ItemPreview, the E2E and perf baseline. The merge round must resolve both sides and rerun web check, focused tests and Search E2E on the combined branch. No merge, push, deploy or issue closure was performed.

Finished #1401. Head: `8b53b9736bfb1ac31db5475e11bea9d27d860020`. ## Built Search now paints the shared Calendar Task/Event card from fields already loaded in the selected Search hit while the detail request is pending. It shows no guessed date, status, completion state or Task link. The full server detail replaces the summary when it arrives. It uses the existing `packages/ui` ItemPreview; no new visual control was added. Files: `apps/web/src/lib/search/SearchPreview.svelte`, `packages/ui/src/components/calendar/ItemPreview.svelte`, `packages/ui/src/components/calendar/model.ts`, `apps/web/src/lib/search/CalendarSearchPreviewSummary.svelte.test.ts`, `apps/web/e2e/search.mjs`, `bench/search-preview-1401.mjs`, `docs/perf/baseline.json`, `contracts/perf/adoption-1058.json`, `contracts/perf/exceptions.json`. ## UX gaps closed Pointer hover, touch long-press, and ArrowDown now check the same first-frame summary and detail refresh. The Task test selects the actual Task row when a same-title Note row exists. Captures assert real fixture content and no update toast; the E2E reported zero page errors and CSP reports. UX gaps left: none found in this flow. ## Screenshots Real production web build, matching shared server build ID, with macOS platform signals; screenshots are attached at actual size. Mono is light; noir is dark. Each cell links light / dark. | Preview | 1440 px | 820 px | 390 px | |---|---|---|---| | Dated Task | [light](https://git.kayg.org/attachments/12556201-56f3-4dbc-abde-0fb04cae451e) / [dark](https://git.kayg.org/attachments/52821019-c0d6-4fc1-bc35-a1b8e4c881ab) | [light](https://git.kayg.org/attachments/32b774a7-a44d-4df7-bb75-a614acf87e84) / [dark](https://git.kayg.org/attachments/f19d3ec9-3c50-4926-9cb1-ecfc4be29177) | [light](https://git.kayg.org/attachments/97bde073-4f74-4daf-8346-49ea274ecf66) / [dark](https://git.kayg.org/attachments/5addf255-c66e-4973-8a43-f0d2113e3605) | | Undated Task | [light](https://git.kayg.org/attachments/f395b144-7bb3-4d79-bd58-e283969a65c9) / [dark](https://git.kayg.org/attachments/9782e41e-4119-4147-9a05-6ac31ddeb0e7) | [light](https://git.kayg.org/attachments/7b7431a9-479d-4657-b512-74edecb6b8b9) / [dark](https://git.kayg.org/attachments/06e4008a-12f1-4f3f-b52e-45534cb3a480) | [light](https://git.kayg.org/attachments/c8e5fa34-e5be-45b8-93b5-d13f7aec49fe) / [dark](https://git.kayg.org/attachments/96644b9c-79a9-46db-a03c-58b7293a7d89) | | Event | [light](https://git.kayg.org/attachments/05c8134d-cb65-4c3c-9db5-ffd2277dea32) / [dark](https://git.kayg.org/attachments/9b9c4245-4384-447a-9b6f-3401f6cc6b3c) | [light](https://git.kayg.org/attachments/b9e979df-956c-4177-a0be-15fd8fb8a333) / [dark](https://git.kayg.org/attachments/7ae93152-63a8-419c-ac93-2b36263da001) | [light](https://git.kayg.org/attachments/adcad278-e000-411c-8d35-0bd85a2f189b) / [dark](https://git.kayg.org/attachments/23fdbb3b-e8d4-426c-bab7-d3d684bcf32e) | Keyboard-shortcut / ArrowDown captures: [dated Task](https://git.kayg.org/attachments/39a4f7b5-a730-4625-8b50-e904032081b9), [undated Task](https://git.kayg.org/attachments/8214cff5-86ae-4c52-9687-25865a756696), [Event](https://git.kayg.org/attachments/a3ebccef-31ce-498f-bf49-e0c149c37856). These show the Search preview after keyboard selection. ## Gates `bun run check`: ```text PASS production dependency licences: 759 locked package releases across apps/web, apps/docs and packages perf-lint: PASS; 0 violations; 22544 scoped exceptions Warn: Do not use empty rulesets (css) svelte-check found 0 errors and 1 warning in 1 file ``` The one warning is the existing empty rule at `packages/ui/src/components/calendar/AttachmentDeck.svelte:1055:3`. Focused Vitest: ```text Test Files 1 passed (1) Tests 2 passed (2) ``` Search E2E (`SEARCH_E2E_1401_ONLY=1`, real local server and shared prebuilt binary): ```text PASS #1401 Task and Event summaries paint within one frame, then refresh at 1440px, 820px and 390px in macOS light and dark themes PASS #1401 ArrowDown shows dated Task, undated Task and Event summaries before detail reads CSP REPORTS search: 0 across 2 pages ``` ## Performance Expected cost is O(1) summary rendering from the loaded hit, plus one background detail request for the selected item. The local profile used 52 Tasks, 1 Event, 18 selections and 1440/820/390 px; no pre-change first-frame baseline was recorded. On the shared local host under load (load average 21.71–23.72 before, 18.30–22.02 after), first-frame p50/p95 was 674.20/1137.50 ms and detail-refresh p50/p95 was 731.5/1295.6 ms. A 50-request Task detail burst measured p50/p95/max 439.7/871.3/873.9 ms; server RSS mean/peak was 628,456,107/629,161,984 bytes. The profile is recorded in `docs/perf/baseline.json`; local results are not comparable to the perf VM baseline. ## Decisions and known gaps DESIGN §32 S7 does not define which Search-hit fields are safe in a Calendar summary. The summary uses the loaded title, Event location snippet, and tags; it waits for detail before showing dates, status, completion controls, or a Task Copy link. The Task link needs the stable Task identity, while the legacy hit destination contains a path alias. The fixture can return a same-title Note and Task. The regression search uses `type:task` plus the fixture title and verifies the visible row kind and destination. The Event fixture did not appear for `type:event`, so its search uses the exact title and checks the Event row kind. I fetched `origin/dev` before gates. The explicit job instruction prohibits merging; the fetched refs show this branch is 11 commits ahead and 395 behind `origin/dev` (`d395b6e25da20d8c67b8b9fc1a593b813939c8a7`), which overlaps SearchPreview, ItemPreview, the E2E and perf baseline. The merge round must resolve both sides and rerun web check, focused tests and Search E2E on the combined branch. No merge, push, deploy or issue closure was performed.
Author
Owner

Deployed to calternal.cloud in merge round 35a (build cc1af3c12, live 2026-10-09 00:12 IST). Closing.

Deployed to calternal.cloud in merge round 35a (build cc1af3c12, live 2026-10-09 00:12 IST). Closing.
kayg closed this issue 2026-10-08 18:42:01 +00:00
Commenting is not possible because the repository is archived.
No labels
No milestone
No project
No assignees
1 participant
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal-v0#1401
No description provided.