Repository navigation
Conversation
kantord
left a comment
There was a problem hiding this comment.
Thanks for this, the pins-first approach, the mutation table and the careful row-click handling (the dialogs render outside the row, and the actions cell and name link stop propagation) made it easy to review. The port is faithful. I'm requesting changes for two things, both in the inventory.
1. The exact next-run and last-run dates are now hover-only
On main the inventory cells showed the date as text. Now the visible text is relative ("in 13h", "10h ago") and the date moved to a native title on a non-focusable TableCell:
- Desktop cells: schedules-workspace.tsx L414-L424
- Phone list: L465 shows "last 10h ago" with no title, and dropped the visible date.
- The detail page's Next/Last run facts were already title-only on
main(schedule-detail.tsx L307-L313), so that part isn't new. It does mean the exact time of an upcoming run is reachable nowhere without a mouse. The run log only shows past runs.
Keyboard and touch users can't reach a title on a non-focusable element, and screen readers announce it inconsistently. The pinning test hides this: disclosed() joins textContent with every title, so "keeps the full next and last run dates available" (L193-L197, L328-L330) passes on a tooltip alone.
Please make the exact date reachable without hover, for example a <time dateTime> element with a secondary or screen-reader-only line, on the inventory and the phone list (and ideally the detail facts), and make the test assert that instead of text-or-title.
A related, smaller point: describeNextRun goes through formatUntilTime, which returns "<1m" for a due-or-past time. An enabled schedule whose next run is overdue would read "in <1m" indefinitely, where the exact date used to show it was stuck. Worth a "due" or "overdue" label.
2. Row click has no modifier or selection guard
The row onClick calls navigate() unconditionally (L384-L395). Reading the code:
- Ctrl/Cmd-click on the row body navigates in the same tab. Only the name link is protected.
- Selecting text inside a row and releasing there fires a click and navigates. I couldn't confirm this in a browser.
A guard that skips navigation when a modifier key is held or window.getSelection() is non-empty is the minimal fix. No test covers a row-body modifier click or a selection (the modifier-click test uses the name link).
Nits, not blocking
- The status dots'
animate-pulse(schedule-status.tsx L51, L62) have nomotion-reduce:animate-none;table.tsxalready usesmotion-reduce:. phone-overflow.spec.tsL282-L284 still says "Schedule task button" and keeps thepending: #2194marker at 320px. The shorter phone header may resolve it, so please check whether the marker can go.- User-docs still say "run history" (scheduled-tasks.md L88, studio.md L70) while the heading is now "Run log".
- Relative times are computed at render, so they only refresh on a re-render. I didn't check whether the query has a refetch interval.
I read the code and CI only and didn't run the app or the tests.
Fully or partially written by an AI agent.
… became text #2369's review fix shows the detail page's next and last run dates as text, so their `<dd>` titles are gone. The guard now names the one title left (the Repeat fact's raw cron) instead of counting three. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… became text #2369's review fix shows the detail page's next and last run dates as text, so their `<dd>` titles are gone. The guard now names the one title left (the Repeat fact's raw cron) instead of counting three. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Pins what #2299's presentation changes must keep: row data and links, the full next and last run dates, the row actions and keyboard stops, the mobile list, the create dialog's focus return, the distinct empty messages, and the detail page's facts, actions and fire history. Every test passes on the current components. Refs #2299 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…rototype
- Status is a dot with a Title-case label: beside the label in the
table, and alone (named for hover and screen readers) in the phone
list. Claimed and Running pulse; Completed keeps its own hollow dot.
The detail badge reads in Title case and pulses while live.
- Next and last run read as relative times ("in 13h", "10h ago"), with
the full date in the cell's title. The phone list's last run is
relative too.
- Clicking anywhere on a desktop row opens the detail page. The name
link stays the keyboard stop, and the actions cell and the name link
stop the click from reaching the row, so a menu item or a modified
click on the link does not also open the row.
- The header drops its subtitle and uses "New scheduled task" ("New" on
a phone) at size sm, with a stable accessible name.
- The table and lists are rounded-lg boxes without bg-card. A search or
status filter with no matches is a plain dashed box with a ghost
Clear filter button.
- The detail page uses the prototype's title size, caps its content at
max-w-4xl, and names the fire history "Run log", with a spinner while
it loads.
Refs #2299
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Review of #2369: - Next and last run render as a `<time dateTime>` that holds the exact local date as text. The inventory table and the phone list keep the prototype's relative label and carry the date as screen-reader text (the table cell keeps its hover title); the detail page's facts show it visibly ("in 13h · Oct 17, 2026, 2:00 AM") and drop the title. - An enabled schedule whose next run has passed reads "Due now" for a minute, then "Overdue", instead of "in <1m" for ever. - A row click with Ctrl, Cmd, Shift or Alt held, or one that ends a text selection, no longer opens the detail page. - The status dot and badge pulses stop under reduced motion. The pins no longer accept a title alone: they require the exact date as text inside a `<time>` for the instant. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…px marker The schedules spec drags a selection across a row's prompt and Ctrl/Cmd- and Shift-clicks the row body, and stays on the list, then opens the row with a plain click. The schedules page no longer overflows at 320px, so its pending #2194 marker goes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
At 390px the detail page's "Next run" label wrapped onto two lines and the exact date broke mid-time. The fact label no longer shrinks, and the visible date stays on one line, so a narrow row breaks after the " · " separator. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…dth check main's phone time-width check (#2365) opens the dialog through a "Schedule task" button. This stack names that button "New scheduled task", so the check timed out on the merge with main. It now uses the new name, like the other schedule journeys. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
4ba8a73 to
d2be1f1
Compare
… became text #2369's review fix shows the detail page's next and last run dates as text, so their `<dd>` titles are gone. The guard now names the one title left (the Repeat fact's raw cron) instead of counting three. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Review changes
Fixes for the review of 2026-10-16. Each point is a new commit on top of the reviewed head
ced382a20.71f18ea4d,23df06bd0). Next and last run render as a<time dateTime>that also holds the exact local date as text.titlefor the pointer.titles are gone, because the date is visible. A paused schedule's Next run is "—" with no date (it used to keep the date in itstitle).title:exactTime()requires the formatted date as text inside the<time>for that instant.71f18ea4d). An enabled schedule whose next run has passed reads "Due now" for its first minute, then "Overdue", instead of "in <1m" indefinitely.71f18ea4d,621fab049). A row-body click with Ctrl, Cmd, Shift or Alt held, or one that ends a text selection, leaves the page on the list. There are unit tests for each modifier and for a selection. A Chromium test drags a selection across a row's prompt, Ctrl/Cmd- and Shift-clicks the row, then opens it with a plain click. With the selection guard removed, that drag navigated to the detail page in Chromium, which confirms the case the review couldn't check.motion-reduce:animate-none(71f18ea4d).pending: #2194marker and the stale "Schedule task button" comment are gone (621fab049).98dfe5094).main(e17d8394d).main's phone time-width check from fix(studio): scan schedules in the breakpoint guard, and widen the schedule time field #2365 opened the dialog through a "Schedule task" button, which this PR names "New scheduled task", so it timed out on the merge. It now uses the new name (d2be1f193).scheduleInventoryPolling) in a visible tab. A refetch with unchanged data doesn't re-render, though, so a relative time can be up to a minute stale until the data changes, a filter or sort changes, or the page remounts. No timer was added.Tests. The schedules suite has 209 tests, all passing.
schedules-alignment.test.tsxadds 9 tests, andschedule-detail.test.tsadds 1. The pins were tightened as described above. One pin date became relative to the clock, so its next run stays in the future.e2e/schedules.spec.tsadds 1 test.Mutation proof (each applied alone, focused suite rerun):
titleonly (no hidden text)<time>withoutdateTimemotion-reduceGates through the shared lock, after the rebase onto
main: on this PR's headd2be1f193,task studio:checkpasses (server 252/252, web 1695/1695); on the top of the restacked stack (97f3f8c7f),task studio:browser153 passed, 41 skipped, andtask docspasses.Screenshots
Before =
ced382a(reviewed head), After =23df06b, Prototype =stack-08. Red marks a real difference. Amber marks a cell that was reviewed and is unchanged: the inventory's Before and After are pixel-identical, because the dates there are hidden text. The phone detail shots are scrolled to the Frequency facts; the prototype column is its top.Detail facts, desktop light. The exact dates are visible beside the relative ones.
Detail facts, phone dark.
Overdue next run, inventory desktop light (no prototype state):
Inventory, desktop light (unchanged pixels; the dates are now
<time>text for screen readers):All other frames
Summary
The schedule inventory and detail page now match the design prototype (
stack/08-tooling) where #2194 stopped short: status dots, relative times, clickable rows, the header and copy, the card shape, the empty filter, and the detail page's "Run log". The data, actions, routes, accessible names and keyboard stops are unchanged, and pinned first.Part of #2299
Part of #2210
What changed
title. Claimed and Running pulse. Completed keeps its own hollow dot. The detail badge reads in Title case and pulses while live.title. The phone list's last run is relative too.size="sm". Its accessible name is "New scheduled task" at every width. The empty inventory's button uses the same component.rounded-lgboxes withoutbg-card.role="status".text-[44px],text-3xlon a phone). The content below the header is capped atmax-w-4xlinstead of the whole page atmax-w-5xl. The fire history is headed Run log and shows a spinner and "Reading the run log…" while it loads.Differences from the prototype
Kept deliberately (#2299, "Deviations from the prototype to keep"):
New in this PR:
titleand hidden text, so it is read twice.Not changed here: the Schedule column's wording comes from Studio's
describeCron("At 02:00 AM") rather than the prototype's ("Daily at 2:00 AM"). #2299 doesn't list it.No regression
Pins first (
115d88a20,schedules-alignment.pinning.test.tsx, 14 tests), against the unchanged components: row data and name links, the full next and last run dates (as text ortitle), the dash and "Never" cells, the four row actions, the row's keyboard stops, the phone list's links and statuses, the create dialog's focus return, the distinct search and status-filter empty messages, and the detail page's facts, actions, loading status and fire rows. 14/14 pass before and after.Change (
ced382a20):schedules-alignment.test.tsx(13 tests) andschedule-status.test.tsx(5) cover the new behaviour.Modified tests.
schedules-workspace.test.tsx: three lookups of the create button change from "Schedule task" to "New scheduled task" (the new name). No assertion changed.schedule-status.test.tsx: the badge text is the Title-case label instead of the raw status. It still requires a distinct look per status.e2e/schedules.spec.ts: the create button's new name, and the status label's new case. The phone lookup is scoped tomain, because "Scheduled" now also matches the navigation link.Mutation proof (each applied alone, focused suite rerun):
titleremovedScreenshots
Before =
8cc7c23(main), After =ced382a, Prototype =stack-08, from the offline Playwright fixtures with the prototype's data and a clock frozen at 2026-09-25 12:30 UTC. Red boxes mark Before/After differences. A second After capture was pixel-identical apart from focus-ring and hover antialiasing on two frames.Inventory, desktop light. Matches the prototype apart from the dot colours, "in 13h" and the Schedule wording noted above.
Detail, desktop light. Matches the prototype's title size,
max-w-4xlcontent and "Run log". The Back link, Model fact and outcome badges are kept deviations.Phone list, light. Matches the prototype; the dot keeps Studio's colours.
All other frames
Inventory, dark and phone dark:
Row hover, desktop light and dark (the whole row opens the detail page):
Empty search, desktop and phone, light and dark (matches the prototype):
Detail, dark and phone light and dark:
Run log loading (the prototype column shows its loaded run log; its mock answers at once):
Gates
Through the shared lock, on
ced382a20:task studio:checkpasses: lint, typecheck and the generated-artifact check pass; server tests 252/252; web tests 1647/1647 (190 files).task studio:browserran at the top of the stack: 136 passed, 38 skipped.🤖 Generated with Claude Code