Task notes: centred checkbox title row and a visual metadata chips header (due, reminder, priority, tags, linked from) #659

Open
opened 2026-10-02 04:26:37 +00:00 by kayg · 60 comments
Owner

Owner report (2026-10-02)

Opening a Task note ("Pay Electricity Bill") shows the title row with the checkbox not vertically centred on the title text. "We need to draw task metadata in a visually appealing way in task notes!"

Proposed design (opinionated default; the owner can adjust it on this issue)

A Task header at the top of the note Card, built from the same pieces as the Calendar item card (#628), so a Task looks the same everywhere:

  1. Title row: a large round checkbox centred on the title's first line (optical centre, not the box top). Ticking it completes the Task with the shared check animation and an Undo toast; the title strikes through and dims.
  2. Metadata chips row under the title, in this order, each shown only when set, with "+ Add date / + Add priority…" ghost chips on hover/focus:
    • When: clock icon + time chips (due / scheduled / start, e.g. "Due · Fri 2 Oct · 18:00"); overdue in the danger tint, today in the accent tint.
    • Repeat (if set), Reminder (bell + time), Priority (flag, tinted by level), List / area calendar, Tags (leaf names, same chips as Calendar).
    • Linked from: the Log entry or note that created it (chip, opens it).
    • Created time in the tertiary role, at the end.
  3. Every chip is an editor control: clicking it opens the shared popover (date/time picker, priority menu, tag picker), keyboard operable, with Copy link in the ⋯ menu. Changes write the Task's frontmatter (file over app). The same write path as Calendar ticking (#657), MCP and CalDAV Reminders (#643/#647).
  4. Phones: the chips wrap; the popovers become sheets.
  5. No metadata as raw frontmatter text in the body.

Tests

e2e like a User: open a Task note, tick it, set a due date and priority from the chips, reload, and see the same values in the Calendar and in the Reminders projection. 3× zoom checks of checkbox/title centre and chip icon/text centre lines in both themes.

## Owner report (2026-10-02) Opening a Task note ("Pay Electricity Bill") shows the title row with the checkbox **not vertically centred** on the title text. "We need to draw task metadata in a visually appealing way in task notes!" ## Proposed design (opinionated default; the owner can adjust it on this issue) A **Task header** at the top of the note Card, built from the same pieces as the Calendar item card (#628), so a Task looks the same everywhere: 1. **Title row:** a large round checkbox centred on the title's first line (optical centre, not the box top). Ticking it completes the Task with the shared check animation and an Undo toast; the title strikes through and dims. 2. **Metadata chips row** under the title, in this order, each shown only when set, with "+ Add date / + Add priority…" ghost chips on hover/focus: - **When:** clock icon + time chips (due / scheduled / start, e.g. "Due · Fri 2 Oct · 18:00"); overdue in the danger tint, today in the accent tint. - **Repeat** (if set), **Reminder** (bell + time), **Priority** (flag, tinted by level), **List** / area calendar, **Tags** (leaf names, same chips as Calendar). - **Linked from:** the Log entry or note that created it (chip, opens it). - **Created** time in the tertiary role, at the end. 3. Every chip is an **editor control**: clicking it opens the shared popover (date/time picker, priority menu, tag picker), keyboard operable, with Copy link in the ⋯ menu. Changes write the Task's frontmatter (file over app). The same write path as Calendar ticking (#657), MCP and CalDAV Reminders (#643/#647). 4. Phones: the chips wrap; the popovers become sheets. 5. No metadata as raw frontmatter text in the body. ## Tests e2e like a User: open a Task note, tick it, set a due date and priority from the chips, reload, and see the same values in the Calendar and in the Reminders projection. 3× zoom checks of checkbox/title centre and chip icon/text centre lines in both themes.
Author
Owner

Starting #659 on branch job/taskmeta-659 from origin/dev at base c4a61e8cf0. I am reviewing the existing Task note renderer, shared Calendar card components, and the Tasks write path before implementing the header.

Starting #659 on branch job/taskmeta-659 from origin/dev at base c4a61e8cf090170f35b1bed3350d9de20c83ecd5. I am reviewing the existing Task note renderer, shared Calendar card components, and the Tasks write path before implementing the header.
Author
Owner

Finding: the Notes plugin already has and , but the web client has no shared Tasks write helper. I will add that helper in the Tasks layer and ask #657 to use it for Calendar ticking.

Finding: the Notes plugin already has and , but the web client has no shared Tasks write helper. I will add that helper in the Tasks layer and ask #657 to use it for Calendar ticking.
Author
Owner

Finding: the Notes plugin already has POST /api/v1/notes/tasks/tick and PATCH /api/v1/notes/tasks/properties, but the web client has no shared Tasks write helper. I will add that helper in the Tasks layer and ask #657 to use it for Calendar ticking.

Finding: the Notes plugin already has POST /api/v1/notes/tasks/tick and PATCH /api/v1/notes/tasks/properties, but the web client has no shared Tasks write helper. I will add that helper in the Tasks layer and ask #657 to use it for Calendar ticking.
Author
Owner

Finding: the editor package test showed that Task node views mount before TipTap exposes editor.view; reading that view during setup made the editor fail to initialize. The Task node now reads the host's live editorProps.attributes callback instead. The focused existing task-item test passes after the fix.

Finding: the editor package test showed that Task node views mount before TipTap exposes `editor.view`; reading that view during setup made the editor fail to initialize. The Task node now reads the host's live `editorProps.attributes` callback instead. The focused existing task-item test passes after the fix.
Author
Owner

Browser finding for #659: In the real production E2E flow, setting a Task reminder could race a Task projection refresh while the root block anchor existed only in the fallback editor. The refresh compared the complete local Markdown with the server body and incorrectly raised the “Note body also changed” conflict, so the scheduled anchor save never reached the reminder API. I changed the comparison to ignore exactly that known pending root anchor when the remainder still matches the server body; unrelated body edits still conflict. The next production E2E run is checking the fix.

Browser finding for #659: In the real production E2E flow, setting a Task reminder could race a Task projection refresh while the root block anchor existed only in the fallback editor. The refresh compared the complete local Markdown with the server body and incorrectly raised the “Note body also changed” conflict, so the scheduled anchor save never reached the reminder API. I changed the comparison to ignore exactly that known pending root anchor when the remainder still matches the server body; unrelated body edits still conflict. The next production E2E run is checking the fix.
Author
Owner

Benchmark finding for #659: The first local profile run stopped before collecting samples because the accessible-name query for “Task header” also matched the header actions group for a Task named “Profile Task header”. I am tightening that query to exact-name matching and rerunning the profile. No product behavior was affected.

Benchmark finding for #659: The first local profile run stopped before collecting samples because the accessible-name query for “Task header” also matched the header actions group for a Task named “Profile Task header”. I am tightening that query to exact-name matching and rerunning the profile. No product behavior was affected.
Author
Owner

Completed #659 on branch job/taskmeta-659.

Built

Added the Task header to Task Notes. It shows the shared animated checkbox and title, then editable When dates and times, repeat, reminder, priority, List/area, Tags, linked Journal day, and Created metadata. Task status, title, fields, and tags use the shared Notes Tasks API client path. The Markdown root checkbox remains the stored mirror and stays hidden in the Note body so it does not duplicate the header control. The header includes Undo for status and property writes, a Task action menu, and Copy link to the stable /t/<task-id> route.

The Notes reminder picker now waits for the Task root block anchor to reach the server. In fallback mode, an overlapping Task refresh accepts only the known pending anchor while it checks for concurrent Note edits. Tag drafts also survive an incoming Task refresh. The shared TagPill supports shortened tag labels and a 44 px action hit target.

Files

  • apps/web/src/lib/notes/TaskHeader.svelte; apps/web/src/lib/tasks/writes.ts; apps/web/src/lib/tasks/writes.test.ts
  • apps/web/src/lib/notes/NoteView.svelte; apps/web/src/lib/notes/NoteEditorSurface.svelte; apps/web/src/lib/notes/editor-types/index.d.ts
  • apps/web/src/lib/components/TagEditor.svelte; packages/ui/src/components/TagPill.svelte
  • packages/editor/src/Editor.svelte; packages/editor/src/source.ts; packages/editor/src/components/TaskItemView.svelte
  • apps/web/e2e/task-header-659.mjs; apps/web/e2e/harness.mjs; apps/web/package.json; bench/task-header-659.mjs

UX gaps closed

  • Status is keyboard operable with Space and has Undo; title editing supports Escape and save. E2E also covers menus with Escape, Copy link, date/time, priority, list, repeat, Tags, reminders, Calendar navigation, and the CalDAV Reminders projection.
  • Metadata controls reuse shared popovers, pills, checkbox, and tag editor. Added/removal actions meet the 44 px touch target, and full tag values remain available to assistive technology.
  • The root Task checkbox mirror updates with title/status edits without showing two controls. Fallback body conflicts preserve the local edit and do not overwrite a newer Note.
  • The production E2E passed at 390, 820, and 1440 px in light and dark themes.

UX gaps left

No functional gap remained in the exercised flow. The six screenshots are attached for Claude’s visual review; I did not judge their visual quality.

Decisions not specified in DESIGN

  • Task reminders use the existing Note block-reminder picker and a stable block ID on the root Task block; no Task-only reminder model or server endpoint was added.
  • The fallback refresh may disregard exactly one generated root anchor only when removing it makes the local body equal the latest server body. Other differences stay conflicts.
  • Task List uses the existing area projection and tag relationship. Copy link uses /t/<stable task id>.
  • Task writes use the existing Notes plugin read, properties, and tick routes. No server schema or route changed.

Gates

App bun run check (verbatim):

$ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json
User browser caches use userStorage; only documented device/public-link exceptions remain.
Text sizes and UI shape values use shared role tokens.
UI transitions and animation options use shared motion tokens or documented exceptions.
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/taskmeta-659/apps/web
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings

App bun run test -- --maxWorkers=1 (verbatim summary):

 Test Files  154 passed (154)
      Tests  1056 passed (1056)
   Start at  09:16:41
   Duration  242.88s (environment 29%, import 25%, transform 23%, tests 18%, setup 6%)

Editor bun run check (verbatim):

$ bun run typecheck
$ svelte-check --tsconfig ./tsconfig.json
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/taskmeta-659/packages/editor
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings

Editor bun run test -- --maxWorkers=1 (verbatim summary):

 Test Files  18 passed (18)
      Tests  402 passed (402)
   Start at  09:20:57
   Duration  31.10s (transform 1.93s, setup 227ms, import 6.00s, tests 6.98s, environment 14.80s)

Production E2E (verbatim):

PASS Task Note 390px light
PASS Task Note 390px dark
PASS Task Note 820px light
PASS Task Note 820px dark
PASS Task Note 1440px light
PASS Task Note 1440px dark
E2E exit status: 0

No Rust source changed, so Rust crate gates were not run. API routes and contracts did not change, so the endpoint adversarial round was not applicable. cargo clean output: Removed 7237 files, 4.6GiB total. apps/web/build was removed.

Performance

The profile ran locally because flock -n /root/perf.lock on the perf VM returned busy. This host had load average 13.26 / 12.07 / 13.52. The 100-tag Task read measured p50 20.3 ms, p95 42.4 ms; production header load p50 1797.2 ms, p95 2123.0 ms; checkbox click-to-persist 401.9 ms; sampled server RSS 132,206,592 to 236,355,584 bytes; JS/CSS bundle 5,115,445 bytes. A 32-request burst took 252.0 ms, with request p50 164.4 ms, p95 200.3 ms, and server RSS after the burst 238,227,456 bytes. The available baseline is Notes list p50/p95 1.3/3.1 ms and Notes daily 1.5/4.8 ms; those endpoints differ from the Task read and are context only. docs/perf/baseline.json has no Task header baseline, so these numbers do not establish a regression.

Head

95f5b9e7460db1f8e6aebd93c39deeeb2438c7dd

Screenshots

Completed #659 on branch `job/taskmeta-659`. ## Built Added the Task header to Task Notes. It shows the shared animated checkbox and title, then editable When dates and times, repeat, reminder, priority, List/area, Tags, linked Journal day, and Created metadata. Task status, title, fields, and tags use the shared Notes Tasks API client path. The Markdown root checkbox remains the stored mirror and stays hidden in the Note body so it does not duplicate the header control. The header includes Undo for status and property writes, a Task action menu, and Copy link to the stable `/t/<task-id>` route. The Notes reminder picker now waits for the Task root block anchor to reach the server. In fallback mode, an overlapping Task refresh accepts only the known pending anchor while it checks for concurrent Note edits. Tag drafts also survive an incoming Task refresh. The shared TagPill supports shortened tag labels and a 44 px action hit target. ## Files - `apps/web/src/lib/notes/TaskHeader.svelte`; `apps/web/src/lib/tasks/writes.ts`; `apps/web/src/lib/tasks/writes.test.ts` - `apps/web/src/lib/notes/NoteView.svelte`; `apps/web/src/lib/notes/NoteEditorSurface.svelte`; `apps/web/src/lib/notes/editor-types/index.d.ts` - `apps/web/src/lib/components/TagEditor.svelte`; `packages/ui/src/components/TagPill.svelte` - `packages/editor/src/Editor.svelte`; `packages/editor/src/source.ts`; `packages/editor/src/components/TaskItemView.svelte` - `apps/web/e2e/task-header-659.mjs`; `apps/web/e2e/harness.mjs`; `apps/web/package.json`; `bench/task-header-659.mjs` ## UX gaps closed - Status is keyboard operable with Space and has Undo; title editing supports Escape and save. E2E also covers menus with Escape, Copy link, date/time, priority, list, repeat, Tags, reminders, Calendar navigation, and the CalDAV Reminders projection. - Metadata controls reuse shared popovers, pills, checkbox, and tag editor. Added/removal actions meet the 44 px touch target, and full tag values remain available to assistive technology. - The root Task checkbox mirror updates with title/status edits without showing two controls. Fallback body conflicts preserve the local edit and do not overwrite a newer Note. - The production E2E passed at 390, 820, and 1440 px in light and dark themes. ## UX gaps left No functional gap remained in the exercised flow. The six screenshots are attached for Claude’s visual review; I did not judge their visual quality. ## Decisions not specified in DESIGN - Task reminders use the existing Note block-reminder picker and a stable block ID on the root Task block; no Task-only reminder model or server endpoint was added. - The fallback refresh may disregard exactly one generated root anchor only when removing it makes the local body equal the latest server body. Other differences stay conflicts. - Task List uses the existing `area` projection and tag relationship. Copy link uses `/t/<stable task id>`. - Task writes use the existing Notes plugin read, properties, and tick routes. No server schema or route changed. ## Gates App `bun run check` (verbatim): ```text $ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json User browser caches use userStorage; only documented device/public-link exceptions remain. Text sizes and UI shape values use shared role tokens. UI transitions and animation options use shared motion tokens or documented exceptions. Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/taskmeta-659/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` App `bun run test -- --maxWorkers=1` (verbatim summary): ```text Test Files 154 passed (154) Tests 1056 passed (1056) Start at 09:16:41 Duration 242.88s (environment 29%, import 25%, transform 23%, tests 18%, setup 6%) ``` Editor `bun run check` (verbatim): ```text $ bun run typecheck $ svelte-check --tsconfig ./tsconfig.json Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/taskmeta-659/packages/editor Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` Editor `bun run test -- --maxWorkers=1` (verbatim summary): ```text Test Files 18 passed (18) Tests 402 passed (402) Start at 09:20:57 Duration 31.10s (transform 1.93s, setup 227ms, import 6.00s, tests 6.98s, environment 14.80s) ``` Production E2E (verbatim): ```text PASS Task Note 390px light PASS Task Note 390px dark PASS Task Note 820px light PASS Task Note 820px dark PASS Task Note 1440px light PASS Task Note 1440px dark E2E exit status: 0 ``` No Rust source changed, so Rust crate gates were not run. API routes and contracts did not change, so the endpoint adversarial round was not applicable. `cargo clean` output: `Removed 7237 files, 4.6GiB total`. `apps/web/build` was removed. ## Performance The profile ran locally because `flock -n /root/perf.lock` on the perf VM returned busy. This host had load average `13.26 / 12.07 / 13.52`. The 100-tag Task read measured p50 `20.3 ms`, p95 `42.4 ms`; production header load p50 `1797.2 ms`, p95 `2123.0 ms`; checkbox click-to-persist `401.9 ms`; sampled server RSS `132,206,592` to `236,355,584` bytes; JS/CSS bundle `5,115,445` bytes. A 32-request burst took `252.0 ms`, with request p50 `164.4 ms`, p95 `200.3 ms`, and server RSS after the burst `238,227,456` bytes. The available baseline is Notes list p50/p95 `1.3/3.1 ms` and Notes daily `1.5/4.8 ms`; those endpoints differ from the Task read and are context only. `docs/perf/baseline.json` has no Task header baseline, so these numbers do not establish a regression. ## Head `95f5b9e7460db1f8e6aebd93c39deeeb2438c7dd` ## Screenshots - [390 px light](https://git.kayg.org/attachments/c9a21185-3b0a-422a-8d2b-2d19f88e602a), [390 px dark](https://git.kayg.org/attachments/f53f1f30-6d13-484d-86a8-d03cb6eb516f) - [820 px light](https://git.kayg.org/attachments/79185f2a-85ff-4ee8-b86f-9ef89b50bf41), [820 px dark](https://git.kayg.org/attachments/a1580e05-bba1-4cb6-8659-79cf3cb440e4) - [1440 px light](https://git.kayg.org/attachments/6ea3c3f2-c30b-4fad-891b-c2ff5bfb7ab0), [1440 px dark](https://git.kayg.org/attachments/57602265-c267-497a-8a88-c5786c6aa78c)
Author
Owner

Touch finding for #659: The new phone-width Playwright tap test measured a 44 px Task checkbox, but its center tap timed out because the global .edge-swipe.edge-back overlay intercepted the pointer. The Task header placed its control too close to the reserved 20 px edge-back zone. I am shifting that target inward on phone widths and rerunning the production E2E.

Touch finding for #659: The new phone-width Playwright tap test measured a 44 px Task checkbox, but its center tap timed out because the global `.edge-swipe.edge-back` overlay intercepted the pointer. The Task header placed its control too close to the reserved 20 px edge-back zone. I am shifting that target inward on phone widths and rerunning the production E2E.
Author
Owner

The phone-width touch run found that the global edge-back gesture intercepted the Task checkbox’s center tap. I moved the checkbox target 8 px inward at phone widths to clear the reserved 20 px gesture zone. The E2E now verifies its target is at least 44 × 44 px, taps it to complete the Task, then taps it again to reopen it. The production E2E exits 0.

Final app check after the phone CSS fix (verbatim):

$ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json
User browser caches use userStorage; only documented device/public-link exceptions remain.
Text sizes and UI shape values use shared role tokens.
UI transitions and animation options use shared motion tokens or documented exceptions.
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/taskmeta-659/apps/web
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings

Final app test run after that change (verbatim summary):

 Test Files  154 passed (154)
      Tests  1056 passed (1056)
   Start at  09:53:02
   Duration  255.27s (import 29%, environment 27%, transform 21%, tests 17%, setup 5%, worker 1%)

Final production E2E (verbatim):

PASS Task Note 390px light
PASS Task Note 390px dark
PASS Task Note 820px light
PASS Task Note 820px dark
PASS Task Note 1440px light
PASS Task Note 1440px dark
E2E exit status: 0

Cleanup after the final server build: Removed 7238 files, 4.6GiB total; apps/web/build is removed. The working tree is clean.

Final head: 60d35e7e8b0c5ef4ff70de7049154eab2cf9559d.

The screenshots below were captured from the final production build and replace the earlier attachment set:

UX gaps closed: phone taps now work through the Task checkbox without colliding with edge-back; the checkbox target remains at least 44 px. UX gaps left: no functional gaps found in the exercised flow; Claude visual review is pending.

## #659 final update — supersedes the previous head and screenshot links The phone-width touch run found that the global edge-back gesture intercepted the Task checkbox’s center tap. I moved the checkbox target 8 px inward at phone widths to clear the reserved 20 px gesture zone. The E2E now verifies its target is at least 44 × 44 px, taps it to complete the Task, then taps it again to reopen it. The production E2E exits 0. Final app check after the phone CSS fix (verbatim): ```text $ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json User browser caches use userStorage; only documented device/public-link exceptions remain. Text sizes and UI shape values use shared role tokens. UI transitions and animation options use shared motion tokens or documented exceptions. Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/taskmeta-659/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` Final app test run after that change (verbatim summary): ```text Test Files 154 passed (154) Tests 1056 passed (1056) Start at 09:53:02 Duration 255.27s (import 29%, environment 27%, transform 21%, tests 17%, setup 5%, worker 1%) ``` Final production E2E (verbatim): ```text PASS Task Note 390px light PASS Task Note 390px dark PASS Task Note 820px light PASS Task Note 820px dark PASS Task Note 1440px light PASS Task Note 1440px dark E2E exit status: 0 ``` Cleanup after the final server build: `Removed 7238 files, 4.6GiB total`; `apps/web/build` is removed. The working tree is clean. Final head: `60d35e7e8b0c5ef4ff70de7049154eab2cf9559d`. The screenshots below were captured from the final production build and replace the earlier attachment set: - [390 px light](https://git.kayg.org/attachments/a5d4fe32-7766-45fe-a303-77eb219dbb13), [390 px dark](https://git.kayg.org/attachments/2eea3430-5785-488a-a904-fc5cdf8a7d45) - [820 px light](https://git.kayg.org/attachments/54ad7014-1f6e-470f-90d3-597d2f8b4658), [820 px dark](https://git.kayg.org/attachments/4b00ccd9-0f4a-4b95-b343-3732dfdb119c) - [1440 px light](https://git.kayg.org/attachments/679597c5-8cd6-4b2d-a98d-48df8ab353ea), [1440 px dark](https://git.kayg.org/attachments/e76e682e-0020-4c4e-8aab-4f0234eb1c5d) UX gaps closed: phone taps now work through the Task checkbox without colliding with edge-back; the checkbox target remains at least 44 px. UX gaps left: no functional gaps found in the exercised flow; Claude visual review is pending.
Author
Owner

Round 2 started for #659. Branch: job/taskmeta-659. Base: origin/dev at c4a61e8cf090170f35b1bed3350d9de20c83ecd5. Starting HEAD: 60d35e7e8b0c5ef4ff70de7049154eab2cf9559d. I am tracing the WebSocket path and Task note room identity first, then I will fix the layout, add the reconnect assertion, and recapture all requested viewports and themes.

Round 2 started for #659. Branch: `job/taskmeta-659`. Base: `origin/dev` at `c4a61e8cf090170f35b1bed3350d9de20c83ecd5`. Starting HEAD: `60d35e7e8b0c5ef4ff70de7049154eab2cf9559d`. I am tracing the WebSocket path and Task note room identity first, then I will fix the layout, add the reconnect assertion, and recapture all requested viewports and themes.
Author
Owner

Finding: routeCurrentBuild excludes ws:/wss: requests from Playwright routing, and startHttpsFront tunnels the raw WebSocket upgrade and frames to the real server. The existing e2e checks body text only before the Task metadata writes; its screenshot loop then waits only for the Task header. The attached worktree screenshots show the Note skeleton with “Reconnecting… your changes are kept”. I am adding room/body readiness assertions after the writes and before each capture to tell a product disconnect from an early screenshot.

Finding: `routeCurrentBuild` excludes `ws:`/`wss:` requests from Playwright routing, and `startHttpsFront` tunnels the raw WebSocket upgrade and frames to the real server. The existing e2e checks body text only before the Task metadata writes; its screenshot loop then waits only for the Task header. The attached worktree screenshots show the Note skeleton with “Reconnecting… your changes are kept”. I am adding room/body readiness assertions after the writes and before each capture to tell a product disconnect from an early screenshot.
Author
Owner

Finding from the new readiness assertion: all three retries in the HTTPS e2e context fail before the server receives a frame. Playwright reports net::ERR_BLOCKED_BY_LOCAL_NETWORK_ACCESS_CHECKS for the correct /api/v1/collab/notes/<Note UUID> URL; no WebSocket frames are sent or received. The Note ID is the resolved UUID. I am applying the repository Chromium permission workaround to the fixture, then I will rerun to check for any product-side reconnect.

Finding from the new readiness assertion: all three retries in the HTTPS e2e context fail before the server receives a frame. Playwright reports `net::ERR_BLOCKED_BY_LOCAL_NETWORK_ACCESS_CHECKS` for the correct `/api/v1/collab/notes/<Note UUID>` URL; no WebSocket frames are sent or received. The Note ID is the resolved UUID. I am applying the repository Chromium permission workaround to the fixture, then I will rerun to check for any product-side reconnect.
Author
Owner

Finding confirmed: the HTTPS front already upgrades WebSockets, but the e2e contexts lacked Chromium’s local-network-access grant. With the same permission used by settings-shortcut.mjs (#541), the correct Note room opens and all six screenshots now wait for real editor content and a healthy room. This exposed the Markdown mirror cascade: .editor-surface .cal-prose ul[data-type="taskList"] li overrides the lower-specificity display:none; the Task root mirror now matches that list scope so ordinary checklist rows remain visible.

Finding confirmed: the HTTPS front already upgrades WebSockets, but the e2e contexts lacked Chromium’s `local-network-access` grant. With the same permission used by `settings-shortcut.mjs` (#541), the correct Note room opens and all six screenshots now wait for real editor content and a healthy room. This exposed the Markdown mirror cascade: `.editor-surface .cal-prose ul[data-type="taskList"] li` overrides the lower-specificity `display:none`; the Task root mirror now matches that list scope so ordinary checklist rows remain visible.
Author
Owner

Layout finding: the production DOM has no shared editor Card around the Task header and Note body. The new e2e geometry assertion fails on that condition, and the 390 px screenshots show the chip row and Created text starting at the viewport edge. I am wrapping the existing editor content in the shared Card and adding geometry checks for the inset, chip flow, icon gaps, and checkbox centre.

Layout finding: the production DOM has no shared editor Card around the Task header and Note body. The new e2e geometry assertion fails on that condition, and the 390 px screenshots show the chip row and Created text starting at the viewport edge. I am wrapping the existing editor content in the shared Card and adding geometry checks for the inset, chip flow, icon gaps, and checkbox centre.
Author
Owner

Layout trace found that the shared Pill hit-floor rule kept faded desktop Add chips at position: relative, despite the earlier .task-add rule. Those controls therefore reserved the reported 110 px before “Linked from”. The desktop selector now overrides the hit-floor rule; the keyboard-focus path restores the chips to the wrap row.

Layout trace found that the shared Pill hit-floor rule kept faded desktop Add chips at `position: relative`, despite the earlier `.task-add` rule. Those controls therefore reserved the reported 110 px before “Linked from”. The desktop selector now overrides the hit-floor rule; the keyboard-focus path restores the chips to the wrap row.
Author
Owner

Design change (owner, 2026-10-02): the chip row in the note body is dropped ("the task pills look so bad… show the task info in the inspector tab morph rather than shoe-horn them there"). New design:

  • The Task note body keeps only the title row: a round checkbox centred on the title's first line (tick + Undo), and nothing else above the text.
  • All Task metadata (status, due/scheduled/start, repeat, reminders, priority, list/area, tags, linked from, created) lives in the Inspector, in a Task section at the top of Info. These are standard Inspector rows (label left, value right, baseline-aligned, shared row primitive from #465), each editable in place (date/time picker, menus, tag picker), keyboard operable, writing through the shared Tasks write path.
  • The Inspector opens from the Tab Bar morph (Format · Style · Info, #583) once that lands. Until then it opens from the existing shared Inspector (⌘I / ⋯ → Info, #465). The section is one component, so it moves into the morph unchanged.
  • Calendar items and the Task header use the same Task section component when they show Task details (no second implementation).
**Design change (owner, 2026-10-02):** the chip row in the note body is dropped ("the task pills look so bad… show the task info in the inspector tab morph rather than shoe-horn them there"). New design: - The Task note body keeps only the title row: a round checkbox centred on the title's first line (tick + Undo), and nothing else above the text. - All Task metadata (status, due/scheduled/start, repeat, reminders, priority, list/area, tags, linked from, created) lives in the **Inspector**, in a **Task** section at the top of Info. These are standard Inspector rows (label left, value right, baseline-aligned, shared row primitive from #465), each editable in place (date/time picker, menus, tag picker), keyboard operable, writing through the shared Tasks write path. - The Inspector opens from the Tab Bar morph (Format · Style · Info, #583) once that lands. Until then it opens from the existing shared Inspector (⌘I / ⋯ → Info, #465). The section is one component, so it moves into the morph unchanged. - Calendar items and the Task header use the same Task section component when they show Task details (no second implementation).
Author
Owner

Finding for #659 round 2: the issue's latest owner comment replaces the chip-row design with a title-only Task Note and a Task section in the shared Inspector. The current unstaged TaskHeader still renders all metadata chips. The shared Inspector (#465) is not on origin/dev; branch origin/job/info-inspector-465 is at 624574249aac004102afd10bc10785c6a47fc33. I will integrate that Inspector work and add the Task section to its existing details flow. This job remains on job/taskmeta-659, based at origin/dev c4a61e8cf0.

Finding for #659 round 2: the issue's latest owner comment replaces the chip-row design with a title-only Task Note and a Task section in the shared Inspector. The current unstaged TaskHeader still renders all metadata chips. The shared Inspector (#465) is not on origin/dev; branch origin/job/info-inspector-465 is at 624574249aac004102afd10bc10785c6a47fc33. I will integrate that Inspector work and add the Task section to its existing details flow. This job remains on job/taskmeta-659, based at origin/dev c4a61e8cf090170f35b1bed3350d9de20c83ecd5.
Author
Owner

Build finding while integrating #465: bun run check reports two unresolved pdfjs-dist imports in InfoPanel.svelte. Both apps/web and packages/ui already declare pdfjs-dist 5.7.284, and bun.lock contains the matching package entry; the current node_modules tree does not contain it. I will sync the existing frozen lockfile rather than change the dependency version.

Build finding while integrating #465: `bun run check` reports two unresolved `pdfjs-dist` imports in InfoPanel.svelte. Both apps/web and packages/ui already declare `pdfjs-dist` 5.7.284, and bun.lock contains the matching package entry; the current node_modules tree does not contain it. I will sync the existing frozen lockfile rather than change the dependency version.
Author
Owner

Inspector integration finding resolved: the initial bun run check could not resolve pdfjs-dist because the package was absent from node_modules. The app and UI manifests and bun.lock already declare 5.7.284; bun install --frozen-lockfile --filter @calternal/web installed the locked package without changing the manifests. The shared Inspector merge now passes bun run check (0 errors, 0 warnings) and the focused Inspector/InfoPanel/Overlay tests pass (3 files, 21 tests).

Inspector integration finding resolved: the initial `bun run check` could not resolve `pdfjs-dist` because the package was absent from node_modules. The app and UI manifests and bun.lock already declare 5.7.284; `bun install --frozen-lockfile --filter @calternal/web` installed the locked package without changing the manifests. The shared Inspector merge now passes `bun run check` (0 errors, 0 warnings) and the focused Inspector/InfoPanel/Overlay tests pass (3 files, 21 tests).
Author
Owner

Finding during UX review: the Calendar Task Inspector's Retry button cleared local Task state, but its read effect only depended on the Task id and supplied Task prop, so it did not issue another read. I added an explicit retry generation. I also keyed Calendar reminder loading by Task identity/title so edits to status, tags or dates do not refetch the Note and reminders. The production web build completed after these changes; the Task Note E2E will verify the edited surface and retry behavior.

Finding during UX review: the Calendar Task Inspector's Retry button cleared local Task state, but its read effect only depended on the Task id and supplied Task prop, so it did not issue another read. I added an explicit retry generation. I also keyed Calendar reminder loading by Task identity/title so edits to status, tags or dates do not refetch the Note and reminders. The production web build completed after these changes; the Task Note E2E will verify the edited surface and retry behavior.
Author
Owner

Resuming #659 after the build-host resize. Branch job/taskmeta-659 is intact at 6fe49f83a, with the shared Inspector integration committed. The latest owner direction is in place as unstaged work: the Note body has a title-only Task header and Task metadata is moving into the shared Inspector through TaskSection. I am continuing the readiness and alignment checks, then I will capture the requested production screenshots and run the final gates.

Resuming #659 after the build-host resize. Branch `job/taskmeta-659` is intact at `6fe49f83a`, with the shared Inspector integration committed. The latest owner direction is in place as unstaged work: the Note body has a title-only Task header and Task metadata is moving into the shared Inspector through `TaskSection`. I am continuing the readiness and alignment checks, then I will capture the requested production screenshots and run the final gates.
Author
Owner

Finding: the first bun run check reported two TypeScript errors at TaskSection.svelte:592: the date-picker callback lost the dialog.kind === 'date' narrowing, and dialog.field widened to DateField | TextField. I captured the narrowed field as a branch-local dateField; the web check is running again.

Finding: the first `bun run check` reported two TypeScript errors at `TaskSection.svelte:592`: the date-picker callback lost the `dialog.kind === 'date'` narrowing, and `dialog.field` widened to `DateField | TextField`. I captured the narrowed field as a branch-local `dateField`; the web check is running again.
Author
Owner

Resolved: apps/web now passes bun run check with svelte-check found 0 errors and 0 warnings. Its serial unit suite passed 156 files and 1070 tests. The E2E now also checks a wrapped phone title and keeps Calendar open while the Inspector changes priority, so the live Calendar projection is checked before reload.

Resolved: `apps/web` now passes `bun run check` with `svelte-check found 0 errors and 0 warnings`. Its serial unit suite passed `156` files and `1070` tests. The E2E now also checks a wrapped phone title and keeps Calendar open while the Inspector changes priority, so the live Calendar projection is checked before reload.
Author
Owner

E2E finding: the production run failed at the title projection assertion before screenshots. Source inspection showed the test filled Prepare next cycle but then waited for Prepare the autumn planning cycle and priorities; the test input and expected value had diverged. The Task write response was not asserted in that run. I am aligning the input and expected title, and adding a PATCH status assertion plus the latest projection to timeout diagnostics before rerunning.

E2E finding: the production run failed at the title projection assertion before screenshots. Source inspection showed the test filled `Prepare next cycle` but then waited for `Prepare the autumn planning cycle and priorities`; the test input and expected value had diverged. The Task write response was not asserted in that run. I am aligning the input and expected title, and adding a PATCH status assertion plus the latest projection to timeout diagnostics before rerunning.
Author
Owner

Production E2E finding: the Task title PATCH returned 200 and the long title projected correctly after the harness input fix. The next run failed while adding a time to the Due date: after the click on the 44 px Set a time label, the test could no longer find its checkbox in the Due date dialog. The screenshot loop did not run. I am recording the dialog state and original click error so I can tell whether the nested Inspector dismissed the picker or the test lost its target during the checkbox update.

Production E2E finding: the Task title PATCH returned 200 and the long title projected correctly after the harness input fix. The next run failed while adding a time to the Due date: after the click on the 44 px Set a time label, the test could no longer find its checkbox in the Due date dialog. The screenshot loop did not run. I am recording the dialog state and original click error so I can tell whether the nested Inspector dismissed the picker or the test lost its target during the checkbox update.
Author
Owner

Follow-up E2E finding: the due-date time editor now completes, and the Task status/priority/list writes pass. The flow then fills the Repeat rule dialog, but Playwright reports that its Save button detaches repeatedly before the click can stabilize. The failing run still stops before screenshots. I am capturing the dialog and Task section DOM at that point to distinguish an open-dialog rerender from a dismissal.

Follow-up E2E finding: the due-date time editor now completes, and the Task status/priority/list writes pass. The flow then fills the Repeat rule dialog, but Playwright reports that its Save button detaches repeatedly before the click can stabilize. The failing run still stops before screenshots. I am capturing the dialog and Task section DOM at that point to distinguish an open-dialog rerender from a dismissal.
Author
Owner

Refined finding: the detached Save button is not specific to Repeat parsing. In the next production run, Playwright found the same unstable/detached button in the Task list form after it filled Home. The date and priority controls had completed. I am capturing the live dialogs, form markup, and animation state at this earlier failure to locate the shared nested editor issue.

Refined finding: the detached Save button is not specific to Repeat parsing. In the next production run, Playwright found the same unstable/detached button in the Task list form after it filled `Home`. The date and priority controls had completed. I am capturing the live dialogs, form markup, and animation state at this earlier failure to locate the shared nested editor issue.
Author
Owner

Nested-surface finding: the Task Tags picker opens, but its Save target never receives pointer input. Playwright reports the parent Inspector's .fs-scrim intercepting every click on TagEditor Save. OverlaySurface accepted nested, but still used the regular overlay layer (scrim 200, surface 201); the Inspector's menu layer (scrim 300, surface 301) therefore sat above it. I am adding explicit nested overlay layer roles above the Inspector while keeping the parent Inspector open.

Nested-surface finding: the Task Tags picker opens, but its Save target never receives pointer input. Playwright reports the parent Inspector's `.fs-scrim` intercepting every click on TagEditor Save. `OverlaySurface` accepted `nested`, but still used the regular overlay layer (`scrim 200`, `surface 201`); the Inspector's menu layer (`scrim 300`, `surface 301`) therefore sat above it. I am adding explicit nested overlay layer roles above the Inspector while keeping the parent Inspector open.
Author
Owner

Production E2E completed the Task edits and reminder creation, then the Inspector geometry check found a 3.38 px first-baseline gap in the Scheduled row (label 320.30 px, value 316.92 px). Task IconLabel values used --leading-ui (1.35) while InspectorRow uses --text-small-leading; I aligned the Task values to the shared row leading and will verify in the next production run.

Production E2E completed the Task edits and reminder creation, then the Inspector geometry check found a 3.38 px first-baseline gap in the Scheduled row (label 320.30 px, value 316.92 px). Task IconLabel values used `--leading-ui` (1.35) while InspectorRow uses `--text-small-leading`; I aligned the Task values to the shared row leading and will verify in the next production run.
Author
Owner

Production E2E isolated the Reminder dismissal: TaskSection delegates reminder creation to NoteView to preserve the root block anchor, but NoteView mounted BlockReminderPicker without nested. Opening that picker therefore announced a top-level transient surface and dismissed the owning Info Inspector. The failure state had no dialog or Task section after the preset click. I am passing nested ownership through the NoteView path and layering nested FloatingSurface instances above their Inspector.

Production E2E isolated the Reminder dismissal: TaskSection delegates reminder creation to NoteView to preserve the root block anchor, but NoteView mounted BlockReminderPicker without `nested`. Opening that picker therefore announced a top-level transient surface and dismissed the owning Info Inspector. The failure state had no dialog or Task section after the preset click. I am passing nested ownership through the NoteView path and layering nested FloatingSurface instances above their Inspector.
Author
Owner

The Reminder trigger is receiving the click and the Inspector remains open, but the picker does not mount when NoteView's reminder handler returns early because its Task/editor/User state is not ready. The row showed Add reminder before those prerequisites were available. I am gating that action until the handler can act; the real empty/loading state will remain visible while NoteView finishes loading.

The Reminder trigger is receiving the click and the Inspector remains open, but the picker does not mount when NoteView's reminder handler returns early because its Task/editor/User state is not ready. The row showed `Add reminder` before those prerequisites were available. I am gating that action until the handler can act; the real empty/loading state will remain visible while NoteView finishes loading.
Author
Owner

Finding: creating the Task root block anchor can refresh the Note Inspector while the Markdown save is pending, which detaches the Reminder trigger captured at click time. NoteView now waits for the Svelte refresh and anchors the picker to the current connected Reminder button. The production E2E passed reminder creation on the next run and reached the later Task list dialog; I am investigating that separate dialog handoff now.

Finding: creating the Task root block anchor can refresh the Note Inspector while the Markdown save is pending, which detaches the Reminder trigger captured at click time. `NoteView` now waits for the Svelte refresh and anchors the picker to the current connected Reminder button. The production E2E passed reminder creation on the next run and reached the later Task list dialog; I am investigating that separate dialog handoff now.
Author
Owner

The latest production E2E stopped before the Note title mounted: Playwright timed out waiting for the Task title group after navigation. At that time the shared host load average was 119, and the local server process was in disk wait. This run produced no screenshot evidence. The checker and production build had passed; I am making one bounded repeat with focus and dialog diagnostics enabled.

The latest production E2E stopped before the Note title mounted: Playwright timed out waiting for the Task title group after navigation. At that time the shared host load average was 119, and the local server process was in disk wait. This run produced no screenshot evidence. The checker and production build had passed; I am making one bounded repeat with focus and dialog diagnostics enabled.
Author
Owner

Finding: the production E2E observed the Space action update the Task to done, then clicked the visible Undo action; the server projection remained done. This is a distinct failure from the Reminder trigger refresh. I am adding request/result diagnostics for the Undo path before changing the write behavior.

Finding: the production E2E observed the Space action update the Task to `done`, then clicked the visible Undo action; the server projection remained `done`. This is a distinct failure from the Reminder trigger refresh. I am adding request/result diagnostics for the Undo path before changing the write behavior.
Author
Owner

Finding from NoteView: each same-path Task Note update cleared inspectorFile before stat() returned. That unmounted the shared InfoPanel during a Task write and could discard a nested editor or menu. The refresh now keeps the mounted Inspector for the same path, updates its metadata when stat() completes, and still reloads when the path changes. I will verify the list editor against the production build.

Finding from `NoteView`: each same-path Task Note update cleared `inspectorFile` before `stat()` returned. That unmounted the shared InfoPanel during a Task write and could discard a nested editor or menu. The refresh now keeps the mounted Inspector for the same path, updates its metadata when `stat()` completes, and still reloads when the path changes. I will verify the list editor against the production build.
Author
Owner

The production flow now opens and saves the List editor after a Task write; keeping the same-path Inspector mounted resolved that handoff. The next step stopped at Repeat: clicking Save did not add a recurrence to the server projection. I am isolating the parser response and the repeat form state before changing the implementation.

The production flow now opens and saves the List editor after a Task write; keeping the same-path Inspector mounted resolved that handoff. The next step stopped at Repeat: clicking Save did not add a recurrence to the server projection. I am isolating the parser response and the repeat form state before changing the implementation.
Author
Owner

The full production E2E flow previously reached Repeat Save but the Task projection stayed without recurrence; the separate authenticated parser probe returned HTTP 200 and a canonical every Monday recurrence. I added failure diagnostics to the E2E case to capture the parser response, properties write, open form, and current projection together.

The resumed diagnostic run did not reach the app: prepare-media-runtime.sh stopped because this shared host had 6,393 local threads (its bounded setup rejects 4,090+). The staged runtime's saved process cap is 3,299, below current usage, so I did not bypass or raise the limit. I will report this as an environment-limited verification gap.

The full production E2E flow previously reached Repeat Save but the Task projection stayed without `recurrence`; the separate authenticated parser probe returned HTTP 200 and a canonical `every Monday` recurrence. I added failure diagnostics to the E2E case to capture the parser response, properties write, open form, and current projection together. The resumed diagnostic run did not reach the app: `prepare-media-runtime.sh` stopped because this shared host had 6,393 local threads (its bounded setup rejects 4,090+). The staged runtime's saved process cap is 3,299, below current usage, so I did not bypass or raise the limit. I will report this as an environment-limited verification gap.
Author
Owner

The production E2E run started successfully with the staged sandbox cap and exercised Space on the Task title row. At the 6-second Task projection timeout, the server still reported done, the checkbox was busy, and no Undo write response had arrived; shared-host load was 111.76 (1-minute) to 130.72 (15-minute) with 6,637 local threads. I extended the E2E's bounded write-propagation wait to 15 seconds. This changes only test tolerance; status and Undo expectations remain unchanged. The run stopped before Inspector or screenshot coverage.

The production E2E run started successfully with the staged sandbox cap and exercised Space on the Task title row. At the 6-second Task projection timeout, the server still reported `done`, the checkbox was busy, and no Undo write response had arrived; shared-host load was 111.76 (1-minute) to 130.72 (15-minute) with 6,637 local threads. I extended the E2E's bounded write-propagation wait to 15 seconds. This changes only test tolerance; status and Undo expectations remain unchanged. The run stopped before Inspector or screenshot coverage.
Author
Owner

The next production E2E run confirmed the 8-second Undo window was expiring during the test's sequential Calendar Done wait: Playwright found the Undo button, then it detached before the click. This was test ordering under shared-host load, not a missing Undo action. I changed the flow to start the Task projection and Undo waits together, click Undo as soon as both are ready, and latch Calendar's transient Done state in the background so the cross-view assertion stays intact without consuming the action window.

The next production E2E run confirmed the 8-second Undo window was expiring during the test's sequential Calendar `Done` wait: Playwright found the Undo button, then it detached before the click. This was test ordering under shared-host load, not a missing Undo action. I changed the flow to start the Task projection and Undo waits together, click Undo as soon as both are ready, and latch Calendar's transient `Done` state in the background so the cross-view assertion stays intact without consuming the action window.
Author
Owner

The focused rerun after the Undo test fix stopped before screenshots: the initial .task-header locator did not become visible within Playwright's 30-second default wait. The local production app and browser had started; this run made no Inspector or capture assertions. Given the repeated shared-host delays and the instruction not to wait for the host to go quiet or loop, I am proceeding with the remaining gates and reporting the screenshot set as pending instead of attaching the old chip-row images (those show the pre-change design).

The focused rerun after the Undo test fix stopped before screenshots: the initial `.task-header` locator did not become visible within Playwright's 30-second default wait. The local production app and browser had started; this run made no Inspector or capture assertions. Given the repeated shared-host delays and the instruction not to wait for the host to go quiet or loop, I am proceeding with the remaining gates and reporting the screenshot set as pending instead of attaching the old chip-row images (those show the pre-change design).
Author
Owner

The one local calternal.task-inspector-659.local.v2 run completed on Linux x64. The profile recorded an after-run load average of 142.64 / 136.70 / 131.63. With a 100-tag Task: Task read API p50/p95 69.8 / 324.8 ms; production title load 14.3 / 34.4 s; Inspector open 7.3 / 23.2 s; checkbox INP 5.33 s; 32-request burst p50/p95 3.84 / 4.67 s; peak sampled server RSS 244,297,728 bytes. docs/perf/baseline.json has no Task Inspector profile. Its Notes list baseline is 1.3 / 3.1 ms and daily baseline 1.5 / 4.8 ms, which are different routes and fixtures. This run was under extreme host contention, so these measurements are diagnostic and not a regression comparison.

The one local `calternal.task-inspector-659.local.v2` run completed on Linux x64. The profile recorded an after-run load average of 142.64 / 136.70 / 131.63. With a 100-tag Task: Task read API p50/p95 69.8 / 324.8 ms; production title load 14.3 / 34.4 s; Inspector open 7.3 / 23.2 s; checkbox INP 5.33 s; 32-request burst p50/p95 3.84 / 4.67 s; peak sampled server RSS 244,297,728 bytes. `docs/perf/baseline.json` has no Task Inspector profile. Its Notes list baseline is 1.3 / 3.1 ms and daily baseline 1.5 / 4.8 ms, which are different routes and fixtures. This run was under extreme host contention, so these measurements are diagnostic and not a regression comparison.
Author
Owner

Finished report — Forgejo #659

Head: 013f6785a8e3d5d886fff4a73c7ef38ce2a346b0

Built

  • Removed Task metadata chips from the Note body. The Task Note now shows its title row and round completion checkbox, centered on the title's first line.
  • Added one shared Task section to the Info Inspector in Notes and Calendar. It uses the Inspector row primitive and the existing apps/web/src/lib/tasks/writes.ts write and Undo path for editable fields.
  • Kept the Task Inspector mounted during same-path Note refreshes so open editors do not disappear during saves. Kept the root Markdown checkbox mirror hidden while preserving it for editor synchronization.
  • Added production E2E coverage and a Task Inspector performance profile. Added repeat-save diagnostics and bounded write waits to the E2E case.

Main files

  • apps/web/src/lib/notes/TaskHeader.svelte
  • apps/web/src/lib/tasks/TaskSection.svelte
  • apps/web/src/lib/notes/NoteView.svelte
  • apps/web/src/lib/files/InfoPanel.svelte
  • apps/web/src/routes/calendar/[view]/[date]/+page.svelte
  • packages/editor/src/components/TaskItemView.svelte
  • apps/web/e2e/task-header-659.mjs
  • bench/task-header-659.mjs

Gates (output excerpts copied verbatim)

cargo fmt --check

(no stdout or stderr was observed; the command session closed before its exit status was recorded)

cargo clippy -p calternal-fs --all-targets -- -D warnings

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 18m 05s
GATE_EXIT=0

cargo test -p calternal-fs

    Finished `test` profile [unoptimized + debuginfo] target(s) in 14m 13s
    test result: ok. 51 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 98.33s
    test result: ok. 43 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 317.20s
    test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
GATE_EXIT=0

bun run check had passed earlier after the final NoteView refresh fix:

svelte-check found 0 errors and 0 warnings

The resumed production E2E did not reach screenshots. One full run exposed a stale 8-second Undo toast during a sequential Calendar wait; the test now latches the Calendar Done state and clicks Undo promptly. The following focused run stopped before the Task title row appeared within Playwright's 30-second wait. The earlier repeat flow still has an unresolved verification: the parser probe returned HTTP 200 with every Monday, while a prior UI run did not persist a recurrence. I added diagnostics, but the resumed run did not reach that step.

cargo clippy -p calternal-plugin-files --all-targets -- -D warnings was stopped at the four-hour job cap while still compiling openssl-sys; no project diagnostic had appeared. cargo test for that crate, all calternal-server gates, the final bun run check/bun run test gates for web and editor, and the adversarial round were not run before the cap.

Performance

The single local calternal.task-inspector-659.local.v2 profile completed under heavy load (after-run load average 142.64 / 136.70 / 131.63). With 100 Task tags: Task read p50/p95 69.8 / 324.8 ms; title load 14.3 / 34.4 s; Inspector open 7.3 / 23.2 s; checkbox INP 5.33 s; 32-request burst p50/p95 3.84 / 4.67 s; peak sampled server RSS 244,297,728 bytes. docs/perf/baseline.json has no Task Inspector profile; Notes list/daily figures are different routes and fixtures, so these high-load numbers are diagnostic, not a regression comparison.

UX gaps closed / left

Closed: Task body no longer repeats metadata; status remains editable by keyboard and pointer with Undo; Task fields share the existing write path and update Calendar/Notes projections; nested editors and reminder actions stay attached through same-path refresh; Task title/checkbox geometry has a first-line center assertion; healthy Note readiness checks reject a visible Reconnecting state.

Left: Current macOS-emulated screenshots for the Note title row and open Inspector at 390, 820, and 1440 px in both themes, including 3× row crops. The existing PNGs show the removed chip row and were not attached. Repeat-save UI verification and the gates listed above remain pending.

Mac checks pending: The macOS VM remains offline. Rerun the production screenshot flow with the existing Playwright macOS platform emulation when a browser run can reach the Task view; then attach all 18 requested images.

Decisions not covered by DESIGN

I preserved the mounted Task Inspector section while NoteView re-stats the same file path after a write. This keeps nested edits stable during a refresh; path changes still reload the Inspector record. No other product design choice was needed beyond the owner's latest #659 direction.

No push, deploy, or merge was performed. origin/dev was merged once as required. cargo clean and web build-output removal were started; the shared target filesystem was in uninterruptible I/O wait when the report was prepared.

## Finished report — Forgejo #659 **Head:** `013f6785a8e3d5d886fff4a73c7ef38ce2a346b0` ### Built - Removed Task metadata chips from the Note body. The Task Note now shows its title row and round completion checkbox, centered on the title's first line. - Added one shared Task section to the Info Inspector in Notes and Calendar. It uses the Inspector row primitive and the existing `apps/web/src/lib/tasks/writes.ts` write and Undo path for editable fields. - Kept the Task Inspector mounted during same-path Note refreshes so open editors do not disappear during saves. Kept the root Markdown checkbox mirror hidden while preserving it for editor synchronization. - Added production E2E coverage and a Task Inspector performance profile. Added repeat-save diagnostics and bounded write waits to the E2E case. ### Main files - `apps/web/src/lib/notes/TaskHeader.svelte` - `apps/web/src/lib/tasks/TaskSection.svelte` - `apps/web/src/lib/notes/NoteView.svelte` - `apps/web/src/lib/files/InfoPanel.svelte` - `apps/web/src/routes/calendar/[view]/[date]/+page.svelte` - `packages/editor/src/components/TaskItemView.svelte` - `apps/web/e2e/task-header-659.mjs` - `bench/task-header-659.mjs` ### Gates (output excerpts copied verbatim) `cargo fmt --check` ```text (no stdout or stderr was observed; the command session closed before its exit status was recorded) ``` `cargo clippy -p calternal-fs --all-targets -- -D warnings` ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 18m 05s GATE_EXIT=0 ``` `cargo test -p calternal-fs` ```text Finished `test` profile [unoptimized + debuginfo] target(s) in 14m 13s test result: ok. 51 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 98.33s test result: ok. 43 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 317.20s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s GATE_EXIT=0 ``` `bun run check` had passed earlier after the final NoteView refresh fix: ```text svelte-check found 0 errors and 0 warnings ``` The resumed production E2E did not reach screenshots. One full run exposed a stale 8-second Undo toast during a sequential Calendar wait; the test now latches the Calendar `Done` state and clicks Undo promptly. The following focused run stopped before the Task title row appeared within Playwright's 30-second wait. The earlier repeat flow still has an unresolved verification: the parser probe returned HTTP 200 with `every Monday`, while a prior UI run did not persist a recurrence. I added diagnostics, but the resumed run did not reach that step. `cargo clippy -p calternal-plugin-files --all-targets -- -D warnings` was stopped at the four-hour job cap while still compiling `openssl-sys`; no project diagnostic had appeared. `cargo test` for that crate, all `calternal-server` gates, the final `bun run check`/`bun run test` gates for web and editor, and the adversarial round were not run before the cap. ### Performance The single local `calternal.task-inspector-659.local.v2` profile completed under heavy load (after-run load average 142.64 / 136.70 / 131.63). With 100 Task tags: Task read p50/p95 69.8 / 324.8 ms; title load 14.3 / 34.4 s; Inspector open 7.3 / 23.2 s; checkbox INP 5.33 s; 32-request burst p50/p95 3.84 / 4.67 s; peak sampled server RSS 244,297,728 bytes. `docs/perf/baseline.json` has no Task Inspector profile; Notes list/daily figures are different routes and fixtures, so these high-load numbers are diagnostic, not a regression comparison. ### UX gaps closed / left **Closed:** Task body no longer repeats metadata; status remains editable by keyboard and pointer with Undo; Task fields share the existing write path and update Calendar/Notes projections; nested editors and reminder actions stay attached through same-path refresh; Task title/checkbox geometry has a first-line center assertion; healthy Note readiness checks reject a visible Reconnecting state. **Left:** Current macOS-emulated screenshots for the Note title row and open Inspector at 390, 820, and 1440 px in both themes, including 3× row crops. The existing PNGs show the removed chip row and were not attached. Repeat-save UI verification and the gates listed above remain pending. **Mac checks pending:** The macOS VM remains offline. Rerun the production screenshot flow with the existing Playwright macOS platform emulation when a browser run can reach the Task view; then attach all 18 requested images. ### Decisions not covered by DESIGN I preserved the mounted Task Inspector section while NoteView re-stats the same file path after a write. This keeps nested edits stable during a refresh; path changes still reload the Inspector record. No other product design choice was needed beyond the owner's latest #659 direction. No push, deploy, or merge was performed. `origin/dev` was merged once as required. `cargo clean` and web build-output removal were started; the shared target filesystem was in uninterruptible I/O wait when the report was prepared.
Author
Owner

Correction to the closing line above: I did perform the required local git merge origin/dev on this job branch. No merge into dev, push, or deploy was performed. The generated apps/web/build directory has been removed; cargo clean was started but remained blocked in filesystem I/O at report time.

Correction to the closing line above: I did perform the required local `git merge origin/dev` on this job branch. No merge into `dev`, push, or deploy was performed. The generated `apps/web/build` directory has been removed; `cargo clean` was started but remained blocked in filesystem I/O at report time.
Author
Owner

Cleanup update: cargo clean has now completed successfully. Verbatim output: Removed 9810 files, 5.3GiB total (exit 0). apps/web/build is also removed.

Cleanup update: `cargo clean` has now completed successfully. Verbatim output: `Removed 9810 files, 5.3GiB total` (exit 0). `apps/web/build` is also removed.
Author
Owner

Independent read-only review started on job/rev2-taskmeta-659, base 440e19dce2. Review target: 013f6785a. No builds, tests, servers or browsers will run.

Independent read-only review started on job/rev2-taskmeta-659, base 440e19dce23040ac8ebaae88f0469b6535b1afcb. Review target: 013f6785a. No builds, tests, servers or browsers will run.
Author
Owner

Independent read-only review of job/taskmeta-659 at 013f6785a: request changes.

Review branch: job/rev2-taskmeta-659; review head: 7bf65bc73bdce69fa89a9e6b01b1daa6961c68a2.
Committed files: review-taskmeta-659.md and audit-findings.md. No product code changed.

Findings, ranked:

  1. P1 — Root reminder identity breaks the Task projection. apps/web/src/lib/notes/NoteView.svelte:381 anchors the root; crates/calternal-notes-core/src/tasks/extract.rs:264 and tasks/line.rs:492,526 recognize only an unanchored root. This creates a second Task identity and prevents later root writes. Fix: give the root reminder an explicit identity shared by the index and all writers, while preserving same-title anchored children. #934.
  2. P1 — An old refresh can persist a stale root checkbox. apps/web/src/lib/notes/TaskHeader.svelte:186 and apps/web/src/lib/tasks/TaskSection.svelte:483 accept reads that started before a local write. NoteView.svelte:425,320 publishes the old state to the editable mirror. The live writer preserves frontmatter and saves the body, so the checkbox can disagree with Done frontmatter. Fix: invalidate reads at write start and check a mutation epoch before publishing their results. #936; its follow-up evidence distinguishes the live writer from the separate HTTP body route.
  3. P2 — Hidden root hides child content. packages/editor/src/components/TaskItemView.svelte:56 hides the whole li, including nested checklist and paragraph blocks. Fix: hide only the redundant checkbox/title, and keep nested content visible and accessible. #935.
  4. P2 — Inline Task editors always fail. apps/web/src/routes/calendar/[view]/[date]/+page.svelte:2242 exposes TaskSection for inline Tasks. Its save at TaskSection.svelte:315 calls a route that rejects inline Tasks at crates/plugins/notes/src/tasks_api.rs:358. Fix: use tick for completion and provide a working promotion/open action for unsupported fields. #937.
  5. P2 — Accepted state waits for the server; Undo lacks a revision guard. apps/web/src/lib/notes/TaskHeader.svelte:69,90 waits before publishing state and restores old status without a receipt or revision. Fix: adopt the shared optimistic mutation and guarded durable inverse contract. Evidence added to existing #667; no duplicate shared owner was created.

No P3 finding. No new Task route or cross-User authorization defect found in this source scope. The complete review gives each regression test and reuse/comment check.

Verification: git diff --check returned exit 0 with no output. Build/test gate output: none; the LIGHT job prohibits builds and tests. No server, browser, screenshot, adversarial round or performance measurement ran. No pushes, deploys or merges. No build output was created.

Known gaps: runtime reproduction and visual/macOS evidence remain for the build and merge jobs. review-taskmeta-659.md lists exact merge-round commands and the behaviours they must prove. No product UX gap was fixed in this review.

Decisions: no product design decision. DESIGN §58 is absent at the target and fetched origin/dev; interactive-path review uses the owner rules on #663 and #667. Four independent fixes were filed as #934–#937, and the shared mutation gap stays with #667. Issues remain open.

Independent read-only review of `job/taskmeta-659` at `013f6785a`: **request changes**. Review branch: `job/rev2-taskmeta-659`; review head: `7bf65bc73bdce69fa89a9e6b01b1daa6961c68a2`. Committed files: `review-taskmeta-659.md` and `audit-findings.md`. No product code changed. Findings, ranked: 1. **P1 — Root reminder identity breaks the Task projection.** `apps/web/src/lib/notes/NoteView.svelte:381` anchors the root; `crates/calternal-notes-core/src/tasks/extract.rs:264` and `tasks/line.rs:492,526` recognize only an unanchored root. This creates a second Task identity and prevents later root writes. Fix: give the root reminder an explicit identity shared by the index and all writers, while preserving same-title anchored children. [#934](https://git.kayg.org/kayg/calternal/issues/934). 2. **P1 — An old refresh can persist a stale root checkbox.** `apps/web/src/lib/notes/TaskHeader.svelte:186` and `apps/web/src/lib/tasks/TaskSection.svelte:483` accept reads that started before a local write. `NoteView.svelte:425,320` publishes the old state to the editable mirror. The live writer preserves frontmatter and saves the body, so the checkbox can disagree with Done frontmatter. Fix: invalidate reads at write start and check a mutation epoch before publishing their results. [#936](https://git.kayg.org/kayg/calternal/issues/936); its follow-up evidence distinguishes the live writer from the separate HTTP body route. 3. **P2 — Hidden root hides child content.** `packages/editor/src/components/TaskItemView.svelte:56` hides the whole `li`, including nested checklist and paragraph blocks. Fix: hide only the redundant checkbox/title, and keep nested content visible and accessible. [#935](https://git.kayg.org/kayg/calternal/issues/935). 4. **P2 — Inline Task editors always fail.** `apps/web/src/routes/calendar/[view]/[date]/+page.svelte:2242` exposes TaskSection for inline Tasks. Its save at `TaskSection.svelte:315` calls a route that rejects inline Tasks at `crates/plugins/notes/src/tasks_api.rs:358`. Fix: use tick for completion and provide a working promotion/open action for unsupported fields. [#937](https://git.kayg.org/kayg/calternal/issues/937). 5. **P2 — Accepted state waits for the server; Undo lacks a revision guard.** `apps/web/src/lib/notes/TaskHeader.svelte:69,90` waits before publishing state and restores old status without a receipt or revision. Fix: adopt the shared optimistic mutation and guarded durable inverse contract. Evidence added to existing [#667](https://git.kayg.org/kayg/calternal/issues/667); no duplicate shared owner was created. No P3 finding. No new Task route or cross-User authorization defect found in this source scope. The complete review gives each regression test and reuse/comment check. Verification: `git diff --check` returned exit 0 with no output. **Build/test gate output: none**; the LIGHT job prohibits builds and tests. No server, browser, screenshot, adversarial round or performance measurement ran. No pushes, deploys or merges. No build output was created. Known gaps: runtime reproduction and visual/macOS evidence remain for the build and merge jobs. `review-taskmeta-659.md` lists exact merge-round commands and the behaviours they must prove. No product UX gap was fixed in this review. Decisions: no product design decision. DESIGN §58 is absent at the target and fetched origin/dev; interactive-path review uses the owner rules on #663 and #667. Four independent fixes were filed as #934–#937, and the shared mutation gap stays with #667. Issues remain open.
Author
Owner

Started #659 on job/taskmeta-659 at 013f6785a8e3d5d886fff4a73c7ef38ce2a346b0, based on merge-base 440e19dce23040ac8ebaae88f0469b6535b1afcb (current origin/dev: c4faf184d). I am first addressing the independent P1/P2 findings, then I will reproduce recurrence persistence and complete the requested responsive/macOS screenshots.

Started #659 on `job/taskmeta-659` at `013f6785a8e3d5d886fff4a73c7ef38ce2a346b0`, based on merge-base `440e19dce23040ac8ebaae88f0469b6535b1afcb` (current `origin/dev`: `c4faf184d`). I am first addressing the independent P1/P2 findings, then I will reproduce recurrence persistence and complete the requested responsive/macOS screenshots.
Author
Owner

Confirmed the root-reminder identity defect with two failing calternal-notes-core regressions. An anchored Task root plus a same-title anchored child projects as 3 rows instead of the expected File Task + one child; task_root_checkbox_status returns None for the anchored root. I am adding explicit root-anchor metadata so index projection and title/status writers can distinguish the root from the child without changing either block ID.

Confirmed the root-reminder identity defect with two failing `calternal-notes-core` regressions. An anchored Task root plus a same-title anchored child projects as 3 rows instead of the expected File Task + one child; `task_root_checkbox_status` returns `None` for the anchored root. I am adding explicit root-anchor metadata so index projection and title/status writers can distinguish the root from the child without changing either block ID.
Author
Owner

P1 #936 source evidence: TaskHeader and TaskSection each accepted any refresh that had started before a local Task write. NoteView then copied that stale status into the editable Markdown mirror. The branch now shares one per-Task mutation epoch across both surfaces, rejects old responses, and schedules one canonical reread after writes. The e2e now holds a real by-id response through a checkbox or Inspector mutation and checks the Task projection and saved Markdown; the focused run is pending.

P1 #936 source evidence: TaskHeader and TaskSection each accepted any refresh that had started before a local Task write. NoteView then copied that stale status into the editable Markdown mirror. The branch now shares one per-Task mutation epoch across both surfaces, rejects old responses, and schedules one canonical reread after writes. The e2e now holds a real by-id response through a checkbox or Inspector mutation and checks the Task projection and saved Markdown; the focused run is pending.
Author
Owner

P2 #937 source evidence: TaskSection rendered property editors for inline Tasks, while the existing properties route rejects those Tasks with 400. The branch now keeps date, repeat, priority, list and tags static for inline Tasks, links to the source Note, and sends its To do/Done actions through the existing tick route. The e2e covers those limits, keyboard completion, Undo and the stable block link; the focused run is pending.

P2 #937 source evidence: TaskSection rendered property editors for inline Tasks, while the existing properties route rejects those Tasks with 400. The branch now keeps date, repeat, priority, list and tags static for inline Tasks, links to the source Note, and sends its To do/Done actions through the existing tick route. The e2e covers those limits, keyboard completion, Undo and the stable block link; the focused run is pending.
Author
Owner

P2 #935 source evidence: the Task Note root NodeView applied display:none and aria-hidden to the entire list item, hiding nested checklists and paragraphs. The branch now hides only the root checkbox and duplicate title paragraph; nested content stays visible and editable. The e2e toggles a same-title child, edits nested prose, reloads and verifies both persist; the focused run is pending.

P2 #935 source evidence: the Task Note root NodeView applied display:none and aria-hidden to the entire list item, hiding nested checklists and paragraphs. The branch now hides only the root checkbox and duplicate title paragraph; nested content stays visible and editable. The e2e toggles a same-title child, edits nested prose, reloads and verifies both persist; the focused run is pending.
Author
Owner

Decision for #659: the Calendar projects recurrence rules that have a fixed civil-date anchor (due, then scheduled, start, or created). A when done rule remains stored and visible in the Inspector, but the Calendar does not invent a future date for it because that date depends on a completion event. DESIGN does not define a separate when done occurrence projection.

Decision for #659: the Calendar projects recurrence rules that have a fixed civil-date anchor (due, then scheduled, start, or created). A `when done` rule remains stored and visible in the Inspector, but the Calendar does not invent a future date for it because that date depends on a completion event. DESIGN does not define a separate `when done` occurrence projection.
Author
Owner

Post-merge clippy finding: cargo clippy -p calternal-plugin-notes --all-targets -- -D warnings rejected two let...else branches in task_recurrence at tasks_api.rs:822 and :825 under clippy::question_mark. I am replacing them with ? propagation and will rerun the plugin gates.

Post-merge clippy finding: `cargo clippy -p calternal-plugin-notes --all-targets -- -D warnings` rejected two `let...else` branches in `task_recurrence` at `tasks_api.rs:822` and `:825` under `clippy::question_mark`. I am replacing them with `?` propagation and will rerun the plugin gates.
Author
Owner

Post-merge Notes test finding: cargo test -p calternal-plugin-notes passed 170 tests and failed the existing tests::daily_and_composer_preserve_unrelated_bytes assertion at crates/plugins/notes/src/lib.rs:9417. Its GET /journal/2026-09-24 returned 404 after the preceding POST /journal/log returned 201. The test expectation is unchanged; I am rerunning it alone to determine whether the missing snapshot is reproducible.

Post-merge Notes test finding: `cargo test -p calternal-plugin-notes` passed 170 tests and failed the existing `tests::daily_and_composer_preserve_unrelated_bytes` assertion at `crates/plugins/notes/src/lib.rs:9417`. Its GET `/journal/2026-09-24` returned 404 after the preceding POST `/journal/log` returned 201. The test expectation is unchanged; I am rerunning it alone to determine whether the missing snapshot is reproducible.
Author
Owner

Focused follow-up for the existing Journal test: cargo test -p calternal-plugin-notes tests::daily_and_composer_preserve_unrelated_bytes -- --exact --nocapture passed (1 passed; 170 filtered out). The full plugin run had 170 passing tests and this one 404 failure; no Journal handler or expectation was changed in #659. This points to an order/load-sensitive test issue, but the cause is not proven here; the merge round should rerun the full plugin test target and inspect if it recurs.

Focused follow-up for the existing Journal test: `cargo test -p calternal-plugin-notes tests::daily_and_composer_preserve_unrelated_bytes -- --exact --nocapture` passed (`1 passed; 170 filtered out`). The full plugin run had 170 passing tests and this one 404 failure; no Journal handler or expectation was changed in #659. This points to an order/load-sensitive test issue, but the cause is not proven here; the merge round should rerun the full plugin test target and inspect if it recurs.
Author
Owner

E2E follow-up finding: POST /api/v1/notes/tasks returned 201 and a stable Task ID, but an immediate GET /api/v1/notes/{task_id} returned 404 Note not found. Added this assertion to task_create_attach_tick_detach_and_replay; the focused Rust test failed at that lookup (404 vs 200). The Task create path wrote only the Task projection, while Note lookup resolves through note_items. I’m fixing the create indexing path to project the new Task into both indexes, then I’ll rerun the regression and browser flow.

E2E follow-up finding: POST /api/v1/notes/tasks returned 201 and a stable Task ID, but an immediate GET /api/v1/notes/{task_id} returned 404 `Note not found`. Added this assertion to `task_create_attach_tick_detach_and_replay`; the focused Rust test failed at that lookup (404 vs 200). The Task create path wrote only the Task projection, while Note lookup resolves through `note_items`. I’m fixing the create indexing path to project the new Task into both indexes, then I’ll rerun the regression and browser flow.
Author
Owner

Live body-save finding from the required screen proof: after typing into a Task Note paragraph, an immediate no-store GET of /api/v1/notes/{task_id} returned the edited Markdown. About one second later, a second GET returned the original body. This also reproduces when editing top-level prose before any Task metadata edits; the browser reported one open Note WebSocket and no Notes body PUT or Task write. I am tracing the write sequence because this is a data-loss condition and blocks the full screenshot run.

Live body-save finding from the required screen proof: after typing into a Task Note paragraph, an immediate no-store GET of `/api/v1/notes/{task_id}` returned the edited Markdown. About one second later, a second GET returned the original body. This also reproduces when editing top-level prose before any Task metadata edits; the browser reported one open Note WebSocket and no Notes body PUT or Task write. I am tracing the write sequence because this is a data-loss condition and blocks the full screenshot run.
Author
Owner

Correction to my earlier live body-save finding: that report was caused by the E2E probe, not a reproduced server data-loss bug. Playwright 1.63.0 treated the async waitForFunction callback's Promise as truthy before its fetch predicate completed. The returned handle later resolved to false; direct no-store reads stayed at the original Markdown, and the trace showed no body write or Yjs edit update.

The editor also requires a real text-mode transition: one click selects a block in reading mode, then Enter opens it for editing. I changed the regression flow to enter text mode and poll Notes from Node with the existing bounded waitForTaskNote helper. The full production E2E now passes that body-save and reload check before finding the Inspector baseline issue recorded below.

Correction to my earlier live body-save finding: that report was caused by the E2E probe, not a reproduced server data-loss bug. Playwright 1.63.0 treated the async `waitForFunction` callback's Promise as truthy before its fetch predicate completed. The returned handle later resolved to `false`; direct no-store reads stayed at the original Markdown, and the trace showed no body write or Yjs edit update. The editor also requires a real text-mode transition: one click selects a block in reading mode, then Enter opens it for editing. I changed the regression flow to enter text mode and poll Notes from Node with the existing bounded `waitForTaskNote` helper. The full production E2E now passes that body-save and reload check before finding the Inspector baseline issue recorded below.
Author
Owner

Forgejo #659 final report

Built

  • Task Notes show one root checkbox and title in the header. The Markdown root identity remains stable, and same-title child Tasks and nested content remain distinct and editable.
  • Notes and Calendar share the Task Inspector. It shows status, due/scheduled/start dates and times, repeat, reminders, priority, list, tags, linked Journal day, and creation date. Task links use stable identities; status and property edits stay in sync across the Note and Calendar, with guarded Undo.
  • Recurrence persists through save and reload and projects repeating instances into Calendar. The Task Note and Inspector E2E also covers the checkbox, root title rename, nested edits, reminders, Copy link, and the linked Journal day.
  • Fixed the root-title selector for the editor's nested content wrapper. Added attachment-retention assertions through tick, reopen, and metadata writes. The failing “Linked from” test query was a tooltip-versus-accessible-name mismatch; the API and Inspector both retained and rendered the Journal link.

Files (62)

  • apps/web/e2e/harness.mjs
  • apps/web/e2e/info-inspector-perf.mjs
  • apps/web/e2e/info-inspector.mjs
  • apps/web/e2e/task-header-659.mjs
  • apps/web/package.json
  • apps/web/src/lib/calendar/data.test.ts
  • apps/web/src/lib/calendar/data.ts
  • apps/web/src/lib/components/InspectorRow.svelte.test.ts
  • apps/web/src/lib/components/OverlaySurface.svelte.test.ts
  • apps/web/src/lib/components/TagEditor.svelte
  • apps/web/src/lib/editor/format/BlockReminderPicker.svelte
  • apps/web/src/lib/files/FilesBrowser.svelte
  • apps/web/src/lib/files/InfoPanel.svelte
  • apps/web/src/lib/files/InfoPanel.svelte.test.ts
  • apps/web/src/lib/files/api.test.ts
  • apps/web/src/lib/files/api.ts
  • apps/web/src/lib/files/model.test.ts
  • apps/web/src/lib/files/model.ts
  • apps/web/src/lib/notes/NoteEditorSurface.svelte
  • apps/web/src/lib/notes/NoteView.svelte
  • apps/web/src/lib/notes/TaskHeader.svelte
  • apps/web/src/lib/notes/editor-types/index.d.ts
  • apps/web/src/lib/overlay/state.svelte.ts
  • apps/web/src/lib/photos/PhotoViewer.svelte
  • apps/web/src/lib/photos/PhotosView.svelte
  • apps/web/src/lib/tasks/TaskSection.svelte
  • apps/web/src/lib/tasks/writes.test.ts
  • apps/web/src/lib/tasks/writes.ts
  • apps/web/src/routes/calendar/[view]/[date]/+page.svelte
  • bench/info-inspector/README.md
  • bench/info-inspector/run.sh
  • bench/task-header-659.mjs
  • bun.lock
  • contracts/openapi.json
  • crates/calternal-fs/src/blob.rs
  • crates/calternal-fs/src/root.rs
  • crates/calternal-fs/tests/storage.rs
  • crates/calternal-notes-core/src/tasks/extract.rs
  • crates/calternal-notes-core/src/tasks/line.rs
  • crates/plugins/files/src/lib.rs
  • crates/plugins/files/src/listing.rs
  • crates/plugins/files/src/shares.rs
  • crates/plugins/notes/migrations/0025_task_recurrence.sql
  • crates/plugins/notes/src/lib.rs
  • crates/plugins/notes/src/store.rs
  • crates/plugins/notes/src/tasks_api.rs
  • packages/api-client/src/generated.ts
  • packages/editor/src/Editor.svelte
  • packages/editor/src/components/TaskItemView.svelte
  • packages/editor/src/source.ts
  • packages/ui/src/components/Inspector.svelte
  • packages/ui/src/components/InspectorRow.svelte
  • packages/ui/src/components/OverlaySurface.svelte
  • packages/ui/src/components/PopoverSurface.svelte
  • packages/ui/src/components/TagPill.svelte
  • packages/ui/src/components/calendar/ItemPreview.svelte
  • packages/ui/src/components/calendar/model.ts
  • packages/ui/src/components/menu/FloatingSurface.svelte
  • packages/ui/src/components/menu/Menu.svelte
  • packages/ui/src/components/viewer/QuickLook.svelte
  • packages/ui/src/index.ts
  • packages/ui/src/tokens.css

Commits and head

  • 1be403679 Assert Task attachments survive metadata writes
  • ac1ba7e08 Verify Task note flows and capture Inspector states
  • HEAD: ac1ba7e0811f47399f32b5db21cd90053a797b34

Gate output

$ cargo fmt --check
(no output; exit code 0)

$ cargo clippy -p calternal-plugin-notes --all-targets -- -D warnings
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 14.57s

$ cargo test -p calternal-plugin-notes task_create_attach_tick_detach_and_replay -- --nocapture
running 1 test
test tests::task_create_attach_tick_detach_and_replay ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 170 filtered out; finished in 1.14s

$ bun run check
$ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json
User browser caches use userStorage; only documented device/public-link exceptions remain.
Text sizes and UI shape values use shared role tokens.
UI transitions and animation options use shared motion tokens or documented exceptions.
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/taskmeta-659/apps/web
Getting Svelte diagnostics...
svelte-check found 0 errors and 0 warnings

$ bunx vitest run src/lib/tasks/writes.test.ts src/lib/calendar/data.test.ts --maxWorkers=2
 RUN  v5.0.1 /home/kayg/Developer/calternal-wt/taskmeta-659/apps/web
 Test Files  2 passed (2)
      Tests  32 passed (32)
   Start at  05:03:09
   Duration  6.26s (transform 88%, import 10%, tests 2%)

$ bun run build
✓ built in 35.80s
> Using @sveltejs/adapter-static
  Wrote site to "build"
  ✔ done

$ bun run test:e2e:task-header-659
PASS Task Note 390px light
PASS Notes and Calendar Task Inspectors 390px light; title and every row cropped at 3× device scale
PASS Task Note 390px dark
PASS Notes and Calendar Task Inspectors 390px dark; title and every row cropped at 3× device scale
PASS Task Note 820px light
PASS Notes and Calendar Task Inspectors 820px light; title and every row cropped at 3× device scale
PASS Task Note 820px dark
PASS Notes and Calendar Task Inspectors 820px dark; title and every row cropped at 3× device scale
PASS Task Note 1440px light
PASS Notes and Calendar Task Inspectors 1440px light; title and every row cropped at 3× device scale
PASS Task Note 1440px dark
PASS Notes and Calendar Task Inspectors 1440px dark; title and every row cropped at 3× device scale

$ CARGO_PROFILE_DEV_DEBUG=line-tables-only CARGO_INCREMENTAL=0 CARGO_BUILD_JOBS=4 SCCACHE_DISABLE=1 TMPDIR="$PWD/target/tmp" cargo clean
     Removed 16483 files, 9.0GiB total

Earlier in this job, calternal-notes-core clippy/tests and calternal-server clippy/tests passed. The core test suite ran 521 + 19 + 5 + 7 + 12 tests; server had 107 passed and 3 ignored.

Known gaps

  • An earlier full cargo test -p calternal-plugin-notes run had 170 passing tests and one unrelated failure: tests::daily_and_composer_preserve_unrelated_bytes received HTTP 404 from the daily Journal GET after a 201 POST. The final focused attachment regression passed; the full suite was not rerun.
  • Per-crate calternal-fs and calternal-plugin-files gates were not run in this job. The job exceeded the four-hour limit; run them in the merge round.
  • The full web unit suite, full E2E suite, and adversarial matrices were left for the merge round.
  • The when done recurrence is stored and shown, but does not create Calendar instances. DESIGN does not define how that rule chooses a next date.

UX gaps closed

  • Keyboard, pointer, and touch status actions use the same behavior; the phone Inspector action targets meet the 44 px requirement. The screenshot run asserts title and Inspector baseline alignment, stable deep links, and the macOS shortcut platform on all widths and themes.
  • Recurrence save/reload and Calendar projection, status and Undo synchronization, root and child identity, reminder editing, Copy link, and linked-day navigation pass in the focused production-build E2E.

UX gaps left

  • when done recurrence has no Calendar projection until its next-date rule is defined. Actual touch activation and VoiceOver were not separately exercised; the mobile target sizes and semantic names/roles/states were checked in the browser.

Decisions

  • Keep when done recurrence visible and stored, without projecting an occurrence until DESIGN defines the next-date rule.

Screenshots

Attached 156 PNGs (phone/tablet/desktop, light/dark, macOS emulation, plus 3× title and row crops): download the screenshot archive.

For the merge round

Run the remaining Rust crate gates:

CARGO_PROFILE_DEV_DEBUG=line-tables-only CARGO_INCREMENTAL=0 CARGO_BUILD_JOBS=4 TMPDIR="$PWD/target/tmp" cargo clippy -p calternal-fs --all-targets -- -D warnings
CARGO_PROFILE_DEV_DEBUG=line-tables-only CARGO_INCREMENTAL=0 CARGO_BUILD_JOBS=4 TMPDIR="$PWD/target/tmp" cargo test -p calternal-fs
CARGO_PROFILE_DEV_DEBUG=line-tables-only CARGO_INCREMENTAL=0 CARGO_BUILD_JOBS=4 TMPDIR="$PWD/target/tmp" cargo clippy -p calternal-plugin-files --all-targets -- -D warnings
CARGO_PROFILE_DEV_DEBUG=line-tables-only CARGO_INCREMENTAL=0 CARGO_BUILD_JOBS=4 TMPDIR="$PWD/target/tmp" cargo test -p calternal-plugin-files
CARGO_PROFILE_DEV_DEBUG=line-tables-only CARGO_INCREMENTAL=0 CARGO_BUILD_JOBS=4 TMPDIR="$PWD/target/tmp" cargo test -p calternal-plugin-notes

The Notes suite must resolve or explain the Journal GET 404. Then run bun run test, bun run test:e2e, and tests/adversarial/run.sh against the merged server. These prove the combined web regressions, app flows, and API robustness/authz matrices. The profile bench/task-header-659.mjs is present; no performance measurement was made because this issue is not a performance issue.

# Forgejo #659 final report ## Built - Task Notes show one root checkbox and title in the header. The Markdown root identity remains stable, and same-title child Tasks and nested content remain distinct and editable. - Notes and Calendar share the Task Inspector. It shows status, due/scheduled/start dates and times, repeat, reminders, priority, list, tags, linked Journal day, and creation date. Task links use stable identities; status and property edits stay in sync across the Note and Calendar, with guarded Undo. - Recurrence persists through save and reload and projects repeating instances into Calendar. The Task Note and Inspector E2E also covers the checkbox, root title rename, nested edits, reminders, Copy link, and the linked Journal day. - Fixed the root-title selector for the editor's nested content wrapper. Added attachment-retention assertions through tick, reopen, and metadata writes. The failing “Linked from” test query was a tooltip-versus-accessible-name mismatch; the API and Inspector both retained and rendered the Journal link. ## Files (62) - `apps/web/e2e/harness.mjs` - `apps/web/e2e/info-inspector-perf.mjs` - `apps/web/e2e/info-inspector.mjs` - `apps/web/e2e/task-header-659.mjs` - `apps/web/package.json` - `apps/web/src/lib/calendar/data.test.ts` - `apps/web/src/lib/calendar/data.ts` - `apps/web/src/lib/components/InspectorRow.svelte.test.ts` - `apps/web/src/lib/components/OverlaySurface.svelte.test.ts` - `apps/web/src/lib/components/TagEditor.svelte` - `apps/web/src/lib/editor/format/BlockReminderPicker.svelte` - `apps/web/src/lib/files/FilesBrowser.svelte` - `apps/web/src/lib/files/InfoPanel.svelte` - `apps/web/src/lib/files/InfoPanel.svelte.test.ts` - `apps/web/src/lib/files/api.test.ts` - `apps/web/src/lib/files/api.ts` - `apps/web/src/lib/files/model.test.ts` - `apps/web/src/lib/files/model.ts` - `apps/web/src/lib/notes/NoteEditorSurface.svelte` - `apps/web/src/lib/notes/NoteView.svelte` - `apps/web/src/lib/notes/TaskHeader.svelte` - `apps/web/src/lib/notes/editor-types/index.d.ts` - `apps/web/src/lib/overlay/state.svelte.ts` - `apps/web/src/lib/photos/PhotoViewer.svelte` - `apps/web/src/lib/photos/PhotosView.svelte` - `apps/web/src/lib/tasks/TaskSection.svelte` - `apps/web/src/lib/tasks/writes.test.ts` - `apps/web/src/lib/tasks/writes.ts` - `apps/web/src/routes/calendar/[view]/[date]/+page.svelte` - `bench/info-inspector/README.md` - `bench/info-inspector/run.sh` - `bench/task-header-659.mjs` - `bun.lock` - `contracts/openapi.json` - `crates/calternal-fs/src/blob.rs` - `crates/calternal-fs/src/root.rs` - `crates/calternal-fs/tests/storage.rs` - `crates/calternal-notes-core/src/tasks/extract.rs` - `crates/calternal-notes-core/src/tasks/line.rs` - `crates/plugins/files/src/lib.rs` - `crates/plugins/files/src/listing.rs` - `crates/plugins/files/src/shares.rs` - `crates/plugins/notes/migrations/0025_task_recurrence.sql` - `crates/plugins/notes/src/lib.rs` - `crates/plugins/notes/src/store.rs` - `crates/plugins/notes/src/tasks_api.rs` - `packages/api-client/src/generated.ts` - `packages/editor/src/Editor.svelte` - `packages/editor/src/components/TaskItemView.svelte` - `packages/editor/src/source.ts` - `packages/ui/src/components/Inspector.svelte` - `packages/ui/src/components/InspectorRow.svelte` - `packages/ui/src/components/OverlaySurface.svelte` - `packages/ui/src/components/PopoverSurface.svelte` - `packages/ui/src/components/TagPill.svelte` - `packages/ui/src/components/calendar/ItemPreview.svelte` - `packages/ui/src/components/calendar/model.ts` - `packages/ui/src/components/menu/FloatingSurface.svelte` - `packages/ui/src/components/menu/Menu.svelte` - `packages/ui/src/components/viewer/QuickLook.svelte` - `packages/ui/src/index.ts` - `packages/ui/src/tokens.css` ## Commits and head - `1be403679` Assert Task attachments survive metadata writes - `ac1ba7e08` Verify Task note flows and capture Inspector states - HEAD: `ac1ba7e0811f47399f32b5db21cd90053a797b34` ## Gate output ```text $ cargo fmt --check (no output; exit code 0) $ cargo clippy -p calternal-plugin-notes --all-targets -- -D warnings Finished `dev` profile [unoptimized + debuginfo] target(s) in 14.57s $ cargo test -p calternal-plugin-notes task_create_attach_tick_detach_and_replay -- --nocapture running 1 test test tests::task_create_attach_tick_detach_and_replay ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 170 filtered out; finished in 1.14s $ bun run check $ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json User browser caches use userStorage; only documented device/public-link exceptions remain. Text sizes and UI shape values use shared role tokens. UI transitions and animation options use shared motion tokens or documented exceptions. Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/taskmeta-659/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings $ bunx vitest run src/lib/tasks/writes.test.ts src/lib/calendar/data.test.ts --maxWorkers=2 RUN v5.0.1 /home/kayg/Developer/calternal-wt/taskmeta-659/apps/web Test Files 2 passed (2) Tests 32 passed (32) Start at 05:03:09 Duration 6.26s (transform 88%, import 10%, tests 2%) $ bun run build ✓ built in 35.80s > Using @sveltejs/adapter-static Wrote site to "build" ✔ done $ bun run test:e2e:task-header-659 PASS Task Note 390px light PASS Notes and Calendar Task Inspectors 390px light; title and every row cropped at 3× device scale PASS Task Note 390px dark PASS Notes and Calendar Task Inspectors 390px dark; title and every row cropped at 3× device scale PASS Task Note 820px light PASS Notes and Calendar Task Inspectors 820px light; title and every row cropped at 3× device scale PASS Task Note 820px dark PASS Notes and Calendar Task Inspectors 820px dark; title and every row cropped at 3× device scale PASS Task Note 1440px light PASS Notes and Calendar Task Inspectors 1440px light; title and every row cropped at 3× device scale PASS Task Note 1440px dark PASS Notes and Calendar Task Inspectors 1440px dark; title and every row cropped at 3× device scale $ CARGO_PROFILE_DEV_DEBUG=line-tables-only CARGO_INCREMENTAL=0 CARGO_BUILD_JOBS=4 SCCACHE_DISABLE=1 TMPDIR="$PWD/target/tmp" cargo clean Removed 16483 files, 9.0GiB total ``` Earlier in this job, `calternal-notes-core` clippy/tests and `calternal-server` clippy/tests passed. The core test suite ran 521 + 19 + 5 + 7 + 12 tests; server had 107 passed and 3 ignored. ## Known gaps - An earlier full `cargo test -p calternal-plugin-notes` run had 170 passing tests and one unrelated failure: `tests::daily_and_composer_preserve_unrelated_bytes` received HTTP 404 from the daily Journal GET after a 201 POST. The final focused attachment regression passed; the full suite was not rerun. - Per-crate `calternal-fs` and `calternal-plugin-files` gates were not run in this job. The job exceeded the four-hour limit; run them in the merge round. - The full web unit suite, full E2E suite, and adversarial matrices were left for the merge round. - The `when done` recurrence is stored and shown, but does not create Calendar instances. DESIGN does not define how that rule chooses a next date. ## UX gaps closed - Keyboard, pointer, and touch status actions use the same behavior; the phone Inspector action targets meet the 44 px requirement. The screenshot run asserts title and Inspector baseline alignment, stable deep links, and the macOS shortcut platform on all widths and themes. - Recurrence save/reload and Calendar projection, status and Undo synchronization, root and child identity, reminder editing, Copy link, and linked-day navigation pass in the focused production-build E2E. ## UX gaps left - `when done` recurrence has no Calendar projection until its next-date rule is defined. Actual touch activation and VoiceOver were not separately exercised; the mobile target sizes and semantic names/roles/states were checked in the browser. ## Decisions - Keep `when done` recurrence visible and stored, without projecting an occurrence until DESIGN defines the next-date rule. ## Screenshots Attached 156 PNGs (phone/tablet/desktop, light/dark, macOS emulation, plus 3× title and row crops): [download the screenshot archive](https://git.kayg.org/attachments/50d9b95b-8c75-46e1-bda8-72ee5b0c59ca). ## For the merge round Run the remaining Rust crate gates: ```sh CARGO_PROFILE_DEV_DEBUG=line-tables-only CARGO_INCREMENTAL=0 CARGO_BUILD_JOBS=4 TMPDIR="$PWD/target/tmp" cargo clippy -p calternal-fs --all-targets -- -D warnings CARGO_PROFILE_DEV_DEBUG=line-tables-only CARGO_INCREMENTAL=0 CARGO_BUILD_JOBS=4 TMPDIR="$PWD/target/tmp" cargo test -p calternal-fs CARGO_PROFILE_DEV_DEBUG=line-tables-only CARGO_INCREMENTAL=0 CARGO_BUILD_JOBS=4 TMPDIR="$PWD/target/tmp" cargo clippy -p calternal-plugin-files --all-targets -- -D warnings CARGO_PROFILE_DEV_DEBUG=line-tables-only CARGO_INCREMENTAL=0 CARGO_BUILD_JOBS=4 TMPDIR="$PWD/target/tmp" cargo test -p calternal-plugin-files CARGO_PROFILE_DEV_DEBUG=line-tables-only CARGO_INCREMENTAL=0 CARGO_BUILD_JOBS=4 TMPDIR="$PWD/target/tmp" cargo test -p calternal-plugin-notes ``` The Notes suite must resolve or explain the Journal GET 404. Then run `bun run test`, `bun run test:e2e`, and `tests/adversarial/run.sh` against the merged server. These prove the combined web regressions, app flows, and API robustness/authz matrices. The profile `bench/task-header-659.mjs` is present; no performance measurement was made because this issue is not a performance issue.
Author
Owner

Task Inspector polish from the 820 px screenshot review. Head: 01f975625 on job/taskmeta-659 (not pushed).

Changes (in the shared components, so Notes and Calendar both get them):

  1. Due: new shared formatItemDateTime (Intl, DATE_TIME_SEPARATOR = ' · '). Due now reads "Sun, 4 Oct · 09:30".
  2. Dates: new shared formatItemDate. Linked from, Created and Calendar Where all use it ("Sat, 3 Oct", "Sun, 4 Oct"). InspectorRow now gives in-app links the ink link role (ink text, accent on hover, no underline). The blue underlined browser link is gone.
  3. Tags: Edit is a Pill shape="chip" tone="secondary" with the same height and centre as the tag chips. touch-hit gives it the 44 px target on touch. Keeps the "Edit Task tags" tooltip.
  4. Reminder: values use text-height touch-hit targets instead of 44 px boxes. IconLabel text sets the baseline, so "+1 more" shares the baseline of the times. The 0.19em offset is removed. At narrow widths "+1 more" wraps to the next line.
  5. Copy link: Inspector takes a copyLink prop and renders the shared CopyLink beside Close, with a tooltip. Calendar passes the item link (reuses contextHref). Notes passes the Task link, or the Note link for other Notes. The floating body button is removed.
  6. Dividers: InspectorRow has no hairline, the same as the Settings change. New shared InspectorSection (small muted heading plus spacing). Task uses it, and Calendar's Kind and Where sit under their own Details heading.

Gates:

  • bun run check: COMPLETED 5832 FILES 0 ERRORS 0 WARNINGS 0 FILES_WITH_PROBLEMS
  • Vitest (time, InspectorRow, InfoPanel, tasks/writes): Test Files 4 passed (4) / Tests 38 passed (38)
  • bun e2e/task-header-659.mjs with this branch's server (/mnt/hdd/targets/jobs/taskmeta-659/debug/calternal-server; the 7a binary does not contain this branch's server commits): exit 0. New assertions cover: no row dividers, the spaced Due separator, one reminder baseline per line, the Edit chip height and centre, the header Copy link, the Details heading and the Where ink link.
  • Note: in two earlier runs the existing double-click step ("Nested paragraph edited", line ~1023) timed out while the host was busy. Inspector changes do not touch that step. It looks flaky; I did not investigate it.

Screenshots (macOS emulation, en-GB, 390/820/1440, light and dark) in apps/artifacts/taskmeta-659/:

  • task-inspector-{390,820,1440}-{light,dark}.png (Notes)
  • calendar-task-inspector-{390,820,1440}-{light,dark}.png
  • 3x row crops: notes-row-<row>-<w>-<theme>-3x.png, calendar-row-<row>-<w>-<theme>-3x.png, calendar-row-details-{kind,where}-<w>-<theme>-3x.png
  • 3x header crops: notes-inspector-header-actions-<w>-<theme>-3x.png, calendar-inspector-header-actions-<w>-<theme>-3x.png

Scope note: removing the InspectorRow divider also affects the Files and Photos Inspectors (shared component). The Files hero Copy link has not moved.

Task Inspector polish from the 820 px screenshot review. Head: `01f975625` on `job/taskmeta-659` (not pushed). Changes (in the shared components, so Notes and Calendar both get them): 1. **Due:** new shared `formatItemDateTime` (Intl, `DATE_TIME_SEPARATOR = ' · '`). Due now reads "Sun, 4 Oct · 09:30". 2. **Dates:** new shared `formatItemDate`. Linked from, Created and Calendar Where all use it ("Sat, 3 Oct", "Sun, 4 Oct"). `InspectorRow` now gives in-app links the ink link role (ink text, accent on hover, no underline). The blue underlined browser link is gone. 3. **Tags:** Edit is a `Pill shape="chip" tone="secondary"` with the same height and centre as the tag chips. `touch-hit` gives it the 44 px target on touch. Keeps the "Edit Task tags" tooltip. 4. **Reminder:** values use text-height `touch-hit` targets instead of 44 px boxes. IconLabel text sets the baseline, so "+1 more" shares the baseline of the times. The 0.19em offset is removed. At narrow widths "+1 more" wraps to the next line. 5. **Copy link:** `Inspector` takes a `copyLink` prop and renders the shared `CopyLink` beside Close, with a tooltip. Calendar passes the item link (reuses `contextHref`). Notes passes the Task link, or the Note link for other Notes. The floating body button is removed. 6. **Dividers:** `InspectorRow` has no hairline, the same as the Settings change. New shared `InspectorSection` (small muted heading plus spacing). Task uses it, and Calendar's Kind and Where sit under their own **Details** heading. Gates: - `bun run check`: `COMPLETED 5832 FILES 0 ERRORS 0 WARNINGS 0 FILES_WITH_PROBLEMS` - Vitest (time, InspectorRow, InfoPanel, tasks/writes): `Test Files 4 passed (4)` / `Tests 38 passed (38)` - `bun e2e/task-header-659.mjs` with this branch's server (`/mnt/hdd/targets/jobs/taskmeta-659/debug/calternal-server`; the 7a binary does not contain this branch's server commits): exit 0. New assertions cover: no row dividers, the spaced Due separator, one reminder baseline per line, the Edit chip height and centre, the header Copy link, the Details heading and the Where ink link. - Note: in two earlier runs the existing double-click step ("Nested paragraph edited", line ~1023) timed out while the host was busy. Inspector changes do not touch that step. It looks flaky; I did not investigate it. Screenshots (macOS emulation, en-GB, 390/820/1440, light and dark) in `apps/artifacts/taskmeta-659/`: - `task-inspector-{390,820,1440}-{light,dark}.png` (Notes) - `calendar-task-inspector-{390,820,1440}-{light,dark}.png` - 3x row crops: `notes-row-<row>-<w>-<theme>-3x.png`, `calendar-row-<row>-<w>-<theme>-3x.png`, `calendar-row-details-{kind,where}-<w>-<theme>-3x.png` - 3x header crops: `notes-inspector-header-actions-<w>-<theme>-3x.png`, `calendar-inspector-header-actions-<w>-<theme>-3x.png` Scope note: removing the InspectorRow divider also affects the Files and Photos Inspectors (shared component). The Files hero Copy link has not moved.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
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#659
No description provided.