Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions docs/changes/unreleased/1809-tz-stable-tasks-heading-test.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
kind: internal
title: the tasks-page test fixtures pin their clocks to the local zone, so they no longer fail east of UTC
pr: 1809
surface: [chat]
invalidates:
- >-
The tasks-page tests were believed to be deterministic and host-independent.
They built their fixture clocks in a fixed -04:00 zone while the tasks window
buckets days in the viewer's LOCAL zone (usageBucketStart, and the "THE DAY
IS THE WRITER'S" law on SpendToday), so a date the tests assert — the chat
manual's `since aug 11`, and the `shift+← aug 2 – aug 25 →` window control —
shifted by a day once the foreign offset was read as local on a host far
enough east: green on a UTC runner, red on a UTC+8 box (#1808). Every
tasks-page fixture clock is now built in time.Local, so the tests read the
same dates under every $TZ.
---

No product behavior changed: the window following the viewer's local calendar
day is deliberate and stays. The tests were host-dependent because each fixture
injected a fixed -04:00 clock and then asserted dates that only matched in the
author's timezone band — one shared cause across tallyrows_test.go,
tasksplace_test.go, tasksparked_test.go, tasksconversation_test.go and
taskstable_test.go, two of whose tests were already red on a UTC+8 box.
Verified identical green from Pacific/Pago_Pago (UTC-11) to Pacific/Kiritimati
(UTC+14).
10 changes: 9 additions & 1 deletion internal/tui3/tallyrows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,15 @@ func TestTheTasksFootStillCountsEveryPieceByItsActualState(t *testing.T) {
// branches and the corpus is asked for what they produced, so neither heading can
// be restated as a literal that drifts.
func TestTheManualQuotesBothTasksOpeningHeadingsExactly(t *testing.T) {
loc := time.FixedZone("fixture", -4*60*60)
// The window buckets days in the viewer's LOCAL zone, on purpose: spend and
// subtask counts are attributed to the writer's own calendar day (session's
// usageBucketStart, and the "THE DAY IS THE WRITER'S" law on SpendToday). So
// the fixture clock has to live in that same zone, or the rendered `since`
// date — which the chat manual quotes verbatim — shifts by a day on any host
// where a fixed foreign offset crosses midnight once read as local. A
// `-04:00` clock did exactly that: green on a UTC runner, red on a UTC+8 box,
// `aug 11` versus `aug 12` (#1808).
loc := time.Local
now := time.Date(2026, time.August, 25, 13, 11, 0, 0, loc)
win := session.LastDays(now, 15)

Expand Down
2 changes: 1 addition & 1 deletion internal/tui3/tasksconversation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import (
// is the depth the old page could not draw. `thor clips` finished yesterday and
// has nothing under it but one row.
func tasksChatFixture() (session.World, session.UsageWindow, time.Time) {
loc := time.FixedZone("fixture", -4*60*60)
loc := time.Local // #1808: the window buckets in local time, so a fixed foreign offset would shift the dates by $TZ
now := time.Date(2026, time.September, 6, 13, 0, 0, 0, loc)
ago := func(d time.Duration) time.Time { return now.Add(-d) }

Expand Down
2 changes: 1 addition & 1 deletion internal/tui3/tasksparked_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
// one piece of work waiting on a person, and two admitted behind it that nothing
// has started.
func tasksParkedFixture() (session.World, session.UsageWindow, time.Time) {
loc := time.FixedZone("fixture", -4*60*60)
loc := time.Local // #1808: the window buckets in local time, so a fixed foreign offset would shift the dates by $TZ
now := time.Date(2026, time.September, 2, 23, 52, 0, 0, loc)
row := session.SessionRow{ID: "room-a", Title: "The Task Surface", Project: "codeaf", Open: true, Live: false}
entry := func(id, label, status string) session.TaskIndexEntry {
Expand Down
8 changes: 4 additions & 4 deletions internal/tui3/tasksplace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
var tasksWidths = []int{60, 80, 120, 200}

func tasksFixture() (session.World, session.UsageWindow, time.Time) {
loc := time.FixedZone("fixture", -4*60*60)
loc := time.Local // #1808: the window buckets in local time, so a fixed foreign offset would shift the dates by $TZ
now := time.Date(2026, time.August, 25, 13, 11, 0, 0, loc)
at := func(day, hour int) time.Time { return time.Date(2026, time.August, day, hour, 0, 0, 0, loc) }
entry := func(id, label, status string, ended time.Time, cost float64) session.TaskIndexEntry {
Expand Down Expand Up @@ -381,7 +381,7 @@ func TestTasksChangedSinceCountsOnlyLandedWorkAfterTheLook(t *testing.T) {
// that landed today with three workers under it, and a second root beside it
// that is nobody's family.
func tasksFamilyFixture() (session.World, session.UsageWindow, time.Time) {
loc := time.FixedZone("fixture", -4*60*60)
loc := time.Local // #1808: the window buckets in local time, so a fixed foreign offset would shift the dates by $TZ
now := time.Date(2026, time.August, 25, 13, 11, 0, 0, loc)
at := func(hour int) time.Time { return time.Date(2026, time.August, 25, hour, 0, 0, 0, loc) }
row := session.SessionRow{ID: "room-a", Title: "The Split", Project: "codeaf", Open: true}
Expand Down Expand Up @@ -573,7 +573,7 @@ func TestVisibleChildrenKeepTheirOwnSectionsWhenTheRootIsAbsent(t *testing.T) {
// detail beside each — and arriving OUT of time order, which is what the world
// scan hands this place.
func tasksPolishFixture() (session.World, session.UsageWindow, time.Time) {
loc := time.FixedZone("fixture", -4*60*60)
loc := time.Local // #1808: the window buckets in local time, so a fixed foreign offset would shift the dates by $TZ
now := time.Date(2026, time.September, 2, 23, 3, 0, 0, loc)
done := func(id, label, outcome string, files int, cost float64, ago time.Duration) session.TaskIndexEntry {
return session.TaskIndexEntry{
Expand Down Expand Up @@ -956,7 +956,7 @@ func TestTheTaskPageNamesTheConversationItCameOutOf(t *testing.T) {
// [homeName], whose title case has raised the id's first letter), so the
// composer keys on the trimmed title equal to the row's own id, folded.
func TestAConversationWithNoTitleIsCalledTheWordNotItsId(t *testing.T) {
loc := time.FixedZone("fixture", -4*60*60)
loc := time.Local // #1808: the window buckets in local time, so a fixed foreign offset would shift the dates by $TZ
now := time.Date(2026, time.September, 11, 14, 25, 0, 0, loc)
id := "de9ea39e6f4c18c3"
transcript := "/private/tmp/htw/codeaf/" + id + "/transcript.jsonl"
Expand Down
2 changes: 1 addition & 1 deletion internal/tui3/taskstable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
// their headings, and the rows inside one of them have three different prices so
// that descending is distinguishable from arriving.
func tasksTableFixture() (session.World, session.UsageWindow, time.Time) {
loc := time.FixedZone("fixture", -4*60*60)
loc := time.Local // #1808: the window buckets in local time, so a fixed foreign offset would shift the dates by $TZ
now := time.Date(2026, time.September, 11, 12, 0, 0, 0, loc)
ago := func(d time.Duration) time.Time { return now.Add(-d) }
work := func(room, id, label, status string, ended time.Time, files int, cost float64) session.TaskIndexEntry {
Expand Down