feat(formatters): surface account timezone + today's local date on list responses - #151
Conversation
…st responses Date-relative reasoning (this week, weekday, "now") had nothing in the payload stating the account's timezone, so the model guessed: fresh agents inferred "now" from the newest updatedTime stamp and sometimes reported the wrong day of week. The wall-clock date the model has from its own context is in an unknown zone; relative filters it delegates to the server already resolve correctly, but reasoning it does itself did not. Add formatAccountDateContext(timeZone, now?) and prepend a one-line header to task-list responses (formatTaskList, used by list and list_all_uncompleted) and schedule responses (formatScheduleList, zone resolved from the schedules): Account timezone: America/Denver | Today: 2026-08-24 (Monday) The zone is already resolved for rendering local times, so this is one line and no extra API calls. now is injectable for deterministic tests; falls back to a UTC-labelled reading when no usable zone is available. Shared code, so it lands on both the stdio and Worker entry points. Closes #150. Claude-Session: https://claude.ai/code/session_01HbZLaLAKnrR3ZoZkZUk1Dn
ReviewSmall, well-scoped change that does exactly what the description says. Went through Code quality
Correctness
Scope / consistency
Tests
Security / performance
Overall this looks correct, minimal, and consistent with the codebase's existing timezone-handling conventions ( |
There was a problem hiding this comment.
🟡 Changes recommended
The UTC fallback path in formatAccountDateContext can produce a UTC date while still labeling the account timezone as a valid zone, which can mislead consumers about what “Today” is relative to.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds an account “date context” header to key list-format responses so downstream date-relative reasoning can anchor to the account timezone and the correct local “today” (instead of guessing from timestamps).
Changes:
- Added
formatAccountDateContext(timeZone, now?)to compute a one-lineAccount timezone … | Today: YYYY-MM-DD (Weekday)context string. - Prepended the context line to
formatTaskListandformatScheduleListtext responses (while keeping taskstructuredContentunchanged). - Added Vitest coverage for pinned instants, near-midnight rollover, fallbacks, and header presence in both formatters.
File summaries
| File | Description |
|---|---|
| tests/date-context-formatter.spec.ts | Adds tests for the new account date context helper and verifies header presence in task/schedule list text output. |
| src/utils/responseFormatters.ts | Prepends the account date context line to task and schedule list response text. |
| src/utils/dateFormat.ts | Introduces formatAccountDateContext implemented via Intl.DateTimeFormat(...).formatToParts with fallbacks. |
Review details
- Files reviewed: 3/3 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| } catch { | ||
| // fall through to a minimal UTC reading | ||
| } | ||
| return `Account timezone: ${usableZone ?? 'unknown (times shown in UTC)'} | Today: ${now.toISOString().slice(0, 10)}`; |
Update the CHANGELOG [Unreleased] section and README for the changes shipped since the #132-#141 wave: - #151: account timezone + today's local date header on list/schedule responses - #149: list_all_uncompleted honors dueDate/priority; list gains completedAfter/completedBefore; schedule working hours; motion_statuses in the essential tier - #148: dueDate filter reduced to account-zone calendar date - wrangler observability logs enabled README: add list_all_uncompleted to motion_tasks operations, describe the new dueDate/completion filters and the timezone header, and correct the motion_schedules description to match the tool (working-hour templates, not calendar events). Claude-Session: https://claude.ai/code/session_01HbZLaLAKnrR3ZoZkZUk1Dn
Release 2.9.0. Stamps the [Unreleased] section (the #132-#151 wave: Worker auth hardening, timezone-aware date handling, list/schedule filters, and the structured-content and CORS fixes) with the release date, and bumps package.json/package-lock.json. Claude-Session: https://claude.ai/code/session_01HbZLaLAKnrR3ZoZkZUk1Dn
Closes #150.
Why
Date-relative reasoning ("what's due this week?", "when am I free today?") had nothing in the payload stating the account's timezone. The model knows the wall-clock date from its own context but not that the account is, say,
America/Denver— so in live testing fresh agents inferred "now" from the newest taskupdatedTimestamp and sometimes reported the wrong weekday ("Sunday" on a Monday). Relative filters the model delegates to the server (dueDate:'today') already resolve correctly; the gap is the date reasoning the model does itself.What
formatAccountDateContext(timeZone, now?)indateFormat.tsreturns one line:Account timezone: America/Denver | Today: 2026-08-24 (Monday). Built fromIntl.formatToParts(order-independent, workerd-safe);nowis injectable for deterministic tests; falls back to a UTC-labelled reading when no usable zone is available.formatTaskList(used bylistandlist_all_uncompleted) andformatScheduleList(zone resolved from the schedules viaresolveDisplayTimeZone).The zone is already resolved for rendering local times, so this is one line and no extra API calls. Shared code, so it lands on both the stdio and Worker entry points.
What it helps
Correct weekday and "this week / next week / end of month" window math in the right zone; "when am I free" anchored to the real current time and correct weekday; eliminates the wrong-day-of-week class of errors.
Tests / review
New
tests/date-context-formatter.spec.ts: pinned-instant zone+weekday rendering, midnight local-vs-UTC day rollover, no-zone and invalid-IANA-zone fallbacks, and header presence in both formatters. Full suite (693) green;type-check,worker:type-check, andtest:typesall clean. A code-review pass found no issues at confidence ≥80 (verified timezone math, no positional-parse break, header stays out ofstructuredContent).Note on the related free/busy limitation
Separately researched whether a real "when am I free" (meeting-aware) tool is feasible: the public Motion API exposes no calendar-events, free/busy, availability, or booking endpoint. Task
scheduledStart/scheduledEnd+/schedulesworking hours are the only signals a personal API key can reach; a true free/busy lives only in Motion's undocumented internal app API. So that limitation is not addressable on this API surface and is intentionally out of scope here.https://claude.ai/code/session_01HbZLaLAKnrR3ZoZkZUk1Dn