Files
2026-09-06 15:14:49 +01:00

4.7 KiB

created, status, requirements, system_design, legacy_specs
created status requirements system_design legacy_specs
2026-09-05 done
REQ-UI-TASK-PROMPT-TRANSCRIPT-VISIBILITY-001
../../specs/ui/system-design/task-prompt-transcript-visibility.md

Implementation Plan: Suppress invalid task timestamps

Overview

Prevent the task transcript from displaying the browser's Invalid Date text when it renders the frontend-only task-description fallback. The fallback has content but no persisted message timestamp, so the timestamp action is omitted; the shared legacy relative formatter also returns an empty label for malformed input. The implementation is one sequential frontend slice with focused unit regressions.

Root cause

useProcessedMessages creates TASK_DESCRIPTION_SYNTHETIC_ID with created_at: "" when history is exhausted without a stored user prompt. MessageActions always renders MessageTimestamp, and the legacy formatRelativeTime helper treats the invalid Date as though its numeric comparisons were valid before falling through to date.toLocaleDateString(). The browser therefore supplies the visible string Invalid Date.

Scope

In scope

  • Validate a message timestamp before rendering the timestamp action and its absolute-time disclosure.
  • Make the shared legacy relative formatter return an empty string for empty or malformed input.
  • Add unit coverage for the observed synthetic fallback path and the shared formatter contract.

Out of scope

  • Changing task-description fallback eligibility, message persistence, or API timestamp fields.
  • Adding a new mobile composition or changing action-row sizing, touch targets, navigation, or scrolling.
  • Reworking the separate locale-aware formatter in lib/i18n/formats.ts.

Technical approach

  • apps/web/lib/utils.ts: return "" immediately when the parsed date is invalid in formatRelativeTime, using the shared strict parser.
  • apps/web/lib/utils/strict-timestamp.ts: centralize strict RFC3339 calendar, clock, offset, and fractional-second validation for timestamp consumers.
  • apps/web/components/task/chat/messages/message-actions.tsx: validate
  • createdAt in MessageTimestamp with the shared parser before deriving the absolute label or mounting the <time>/Drawer surface. Invalid timestamps keep content actions visible but omit only the timestamp affordance.
  • apps/web/lib/state/slices/session/turn-actions.ts: delegate the existing nanosecond parser API to the shared implementation so session reconciliation and UI formatting enforce the same wire contract.
  • Keep the existing formatRelativeTime import and action-row presentation so valid message timestamps and desktop/mobile interaction behavior are unchanged.

Tests

  • apps/web/lib/utils.test.ts: add an invalid and empty input regression for formatRelativeTime, including values that Date.parse would normalize.
  • apps/web/components/task/chat/messages/message-actions.test.tsx: add a synthetic-fallback timestamp case asserting no <time> element or Invalid Date text/disclosure is rendered for empty, malformed, and normalized-malformed values. This covers AC-UI-TASK-PROMPT-TRANSCRIPT-VISIBILITY-001.15.

E2E tests

No new Playwright scenario is required. This repair changes only timestamp normalization and conditionally removes an affordance from an existing inline message row; it does not change mobile composition, touch behavior, navigation, scrolling, or viewport geometry. The focused message-action test covers the shared desktop/mobile rendering path, and existing task-chat mobile flows remain the rendered transcript smoke coverage.

Work orders

Verification results

Passed:

  • pnpm --filter @kandev/web test -- --run lib/utils.test.ts components/task/chat/messages/message-actions.test.tsx: 51 tests passed.
  • pnpm --filter @kandev/web test -- --run lib/state/slices/session/turn-actions.test.ts: 54 tests passed.
  • ESLint passed for the four changed frontend source and test files.
  • pnpm run typecheck passed from apps/web.
  • python3 scripts/lint-spec-files.py --all passed.
  • git diff --check passed.
  • Managed Chromium and Pixel 5 capture specs passed with synthetic task data; both screenshots contain no Invalid Date text. Temporary capture specs were removed after capture.

Risks

  • A malformed timestamp supplied by a persisted message will lose its timestamp affordance instead of showing a misleading value; message content and other actions remain available.
  • The focused test command may regenerate frontend release-note/changelog artifacts as part of the package pretest hook; unrelated generated changes must not be included.