From eb0932bddad0daa47c36cd20599944c3dc45482a Mon Sep 17 00:00:00 2001 From: Claude Code Date: Sat, 15 Aug 2026 17:59:12 +0000 Subject: Initial commit: tungsten's memory tree --- project_doot_isallday_midnight_bug.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 project_doot_isallday_midnight_bug.md (limited to 'project_doot_isallday_midnight_bug.md') diff --git a/project_doot_isallday_midnight_bug.md b/project_doot_isallday_midnight_bug.md new file mode 100644 index 0000000..bd5bf30 --- /dev/null +++ b/project_doot_isallday_midnight_bug.md @@ -0,0 +1,22 @@ +--- +name: project-doot-isallday-midnight-bug +description: "FIXED 2026-08-10 (commit 9a3ece4): dated doot-native/Google tasks landed in the widget's undated/floating bucket (rendering as due 'today') instead of their correct day, because every task due-date is midnight-anchored and trips the IsAllDay 'is this all-day' heuristic" +metadata: + node_type: memory + type: project + originSessionId: 647d8c60-e602-440e-b8b3-d8422fcbcb1d +--- + +**Status: FIXED and deployed**, commit `9a3ece4` on 2026-08-10. Originally diagnosed 2026-07-29 and deliberately set aside; recurred as a fresh report ("tasks scheduled for tomorrow show up right on the webview but on today in the widget") which prompted implementing it. + +**Root cause:** doot-native and Google Tasks due dates are always midnight-anchored (even with a real due date), which trips `TimelineItem.ComputeDaySection`'s "midnight means no specific time" heuristic and sets `IsAllDay = true` on them. `TimelineItemToWidgetItem` then left `wi.Start` nil for any Task/GTask with `IsAllDay == true`, and the Android client's undated/floating pool (`DootWidget.kt`'s `floating` list) has zero per-day awareness — it just packs items forward from "now" — so a task due tomorrow rendered as if due today. + +**Important correction to the original 2026-07-29 fix plan:** that plan proposed gating `ComputeDaySection`'s midnight heuristic to `Event`/`Meal` types only. Re-verifying it against the live code (rather than implementing on trust) surfaced a real problem: it would also flip `IsAllDay` to `false` for *same-day* dated tasks, moving them on the **web** from the untimed-item strip into the hourly calendar grid with a fabricated "12:00 AM" time label — fixing the widget by breaking the web's already-correct rendering. `IsAllDay`/`ComputeDaySection` is shared by both clients; the web buckets by `DaySection` (computed from `Time`, unaffected by `IsAllDay`) and only uses `IsAllDay` for the same-day untimed-vs-grid visual choice, which the naive fix would have broken. + +**Actual fix implemented:** added `TimelineItem.Undated bool` — a signal genuinely independent of `IsAllDay`, true only for the caller's two genuinely-dateless constructions in `internal/handlers/timeline_logic.go` (nativeUndated tasks, gtasks with no due date), both of which use `Time = now` as a layout placeholder rather than a real date. `ComputeDaySection`/`IsAllDay` were left completely untouched (zero web-side risk). `TimelineItemToWidgetItem` routes on `Undated` instead: dated Task/GTask items now get `wi.Start` populated (so the Android client's existing, already-correct Start-based day bucketing — `todayScheduledEvents`/`tomorrowItems` in `DootWidget.kt` — runs for them), while genuinely undated ones keep the original nil-Start floating treatment. + +Also fixed the identical latent bug in `wi.DueDate`'s guard in the same pass: it only checked `!Time.IsZero()`, but `nativeUndated`'s `Time=now` placeholder is non-zero, so an undated task's Android detail popup would show a fabricated due date. Now guards on `!Undated` too. + +**Verification:** new tests `TestTimelineItemToWidgetItem_DatedGTaskGetsStart` and `_UndatedTaskWithPlaceholderTime_NilDueDate`, plus three existing tests updated because they had encoded the bug's "Start stays nil for any IsAllDay task" as expected behavior. Proved the fix by reverting `widget.go` to the pre-fix condition against a real backup and confirming exactly the three tests exercising the new behavior failed, then restored and confirmed byte-identical. `go build`/`vet`/`test` all clean; deployed to tungsten and health-checked. + +**Takeaway for future similar reports:** when a proposed fix and its own diagnosis are more than a session old, re-derive the trace against current code before implementing rather than trusting the old plan verbatim — the plan itself had a real, would-have-shipped bug (web-side regression) that only surfaced from re-checking, not from the original diagnosis. -- cgit v1.2.3