Skip to content

feat(web,mobile): Today scheduling clarity, race-safe completion, URL day-nav - #152

Merged
thomasluizon merged 1 commit into
mainfrom
feature/today-scheduling-concurrency-daynav
Jun 8, 2026
Merged

feat(web,mobile): Today scheduling clarity, race-safe completion, URL day-nav#152
thomasluizon merged 1 commit into
mainfrom
feature/today-scheduling-concurrency-daynav

Conversation

@thomasluizon

Copy link
Copy Markdown
Owner

Three correlated issues from one PRD (today-scheduling-concurrency-daynav), bundled into one frontend PR. Paired backend PR: thomasluizon/orbit-api#196.

#149 — Scheduling status on the Today list

  • Overdue marker now also shows on child rows (dropped the !isChild gate).
  • New future hint: relative in 6d (≤7 days), absolute scheduled 13 Jun beyond.
  • The check-circle is disabled on recurring/flexible rows that can't be logged for the date, via a shared canLogHabitOnDate predicate mirroring the backend ValidateTargetDate (one-time tasks stay tappable). No API change.

#150 — Race-safe completion (client half)

  • Replaced the decaying recentlyCompletedPromptIdsRef + 1400 ms timer with a deterministic, authoritative-cache-derived parent-prompt check + a promptedParentIdsRef once-guard (re-arms on done<total). The "complete the parent?" prompt fires exactly once per burst. Backend idempotency is in the paired PR.

#151 — URL-driven day navigation

  • The selected day is now the URL (?date=YYYY-MM-DD); bare route = today; back steps days. selectedDate/followToday removed from persisted store state; midnight/foreground roll-to-today preserved via a today-tick.

Parity & validation

  • web ↔ mobile mirrored (parity-checker ✅); shared helpers + i18n (en+pt-BR) in packages/shared (i18n-syncer ✅); canLogHabitOnDate ↔ backend ValidateTargetDate (contract-aligner, 0 drift).
  • type-check ✅ · lint ✅ (0/0) · Vitest web 1465 / shared 850 / mobile 411 ✅.
  • Playwright day-nav spec authored (apps/web/e2e/tests/22-day-nav.spec.ts); runs in CI.

Closes #149
Closes #150
Closes #151

🤖 Generated with Claude Code

… day-nav

Bundle of three correlated issues from one PRD on the Today view:

- #149: surface overdue (now on child rows too) and future ("in 6d" /
  "scheduled 13 Jun") scheduling status, and disable the log control on
  recurring/flexible rows that can't be logged for the date (mirrors the
  backend ValidateTargetDate via a shared canLogHabitOnDate predicate).
- #150: replace the racy 1400ms decaying-ref parent-prompt gate with a
  deterministic, authoritative-cache-derived, coalesced once-guard so the
  "complete the parent?" prompt fires exactly once per burst (client half;
  backend idempotency ships in the paired orbit-api PR).
- #151: make the selected day URL-driven (?date=), bare route = today, with
  selectedDate removed from persisted store state.

Parity preserved across apps/web and apps/mobile; shared helpers + i18n live
in packages/shared.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
orbit-ui-mobile-web Ignored Ignored Jun 8, 2026 6:28pm

@sonarqubecloud

sonarqubecloud Bot commented Jun 8, 2026

Copy link
Copy Markdown

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Three well-scoped features land together coherently and the implementation is tight throughout.

#149 – Scheduling clarity. Removing the !isChild gate on the overdue token is the right root-cause fix; the old gate was an arbitrary restriction. canLogHabitOnDate correctly mirrors LogHabitCommand.ValidateTargetDate — one-time tasks (no frequencyUnit) are still tappable for future dates, recurring/flexible are blocked; the disabled dot (opacity 0.4, non-interactive) is a clean UX signal. computeHabitFutureHint is pure, well-tested, and the 7-day threshold for relative-vs-absolute is sensible.

#150 – Race-safe completion. The replacement of the decaying recentlyCompletedPromptIdsRef + 1400 ms timer with promptedParentIdsRef as a once-guard is a genuine root-cause fix: the old approach raced against cache invalidation timing; the new one reads authoritative cache state and fires the prompt exactly once per burst, re-arming only when progress drops below total. The burst test (3 siblings → 1 dialog) covers the key invariant.

#151 – URL day navigation. Removing selectedDate/followToday from persisted store state is the right call — ephemeral navigation state has no business in localStorage/SecureStore. The today-tick refactor (useState(getTodayDate) + handleTodayRollover) handles midnight rollover and app-foreground correctly on both platforms without the store coupling. Store version bump to 2 and migration that strips the legacy keys is clean.

Parity, i18n, tests. Web ↔ mobile mirrored throughout; both locales updated together; shared helpers canLogHabitOnDate and computeHabitFutureHint live in packages/shared with their own unit tests; the E2E Playwright spec covers the golden path for URL-driven day nav; Vitest counts (web 1465 / shared 850 / mobile 411) confirm no regressions.

One minor cleanup (non-blocking): apps/web/e2e/tests/22-day-nav.spec.ts line 13 has an empty test.afterAll(async () => {}) stub — no-op, but per CLAUDE.md rule 2 stub functions should be deleted. Worth a follow-up squash or a quick cleanup commit before merge.

@thomasluizon
thomasluizon merged commit ec00d7b into main Jun 8, 2026
8 checks passed
@thomasluizon
thomasluizon deleted the feature/today-scheduling-concurrency-daynav branch June 8, 2026 18:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

URL-driven day navigation; bare route = today Make rapid habit completion race-safe (client + backend) Surface scheduling status in the Today view

1 participant