feat(web,mobile): Today scheduling clarity, race-safe completion, URL day-nav - #152
Conversation
… 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>
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
|
There was a problem hiding this comment.
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.



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
!isChildgate).in 6d(≤7 days), absolutescheduled 13 Junbeyond.canLogHabitOnDatepredicate mirroring the backendValidateTargetDate(one-time tasks stay tappable). No API change.#150 — Race-safe completion (client half)
recentlyCompletedPromptIdsRef+ 1400 ms timer with a deterministic, authoritative-cache-derived parent-prompt check + apromptedParentIdsRefonce-guard (re-arms ondone<total). The "complete the parent?" prompt fires exactly once per burst. Backend idempotency is in the paired PR.#151 — URL-driven day navigation
?date=YYYY-MM-DD); bare route = today; back steps days.selectedDate/followTodayremoved from persisted store state; midnight/foreground roll-to-today preserved via a today-tick.Parity & validation
packages/shared(i18n-syncer ✅);canLogHabitOnDate↔ backendValidateTargetDate(contract-aligner, 0 drift).apps/web/e2e/tests/22-day-nav.spec.ts); runs in CI.Closes #149
Closes #150
Closes #151
🤖 Generated with Claude Code