Skip to content

Step the activity feed back a day at a time, and add timeline --date - #957

Draft
robzolkos wants to merge 7 commits into
mainfrom
activity-feed-days
Draft

robzolkos wants to merge 7 commits into
mainfrom
activity-feed-days

Conversation

@robzolkos

@robzolkos robzolkos commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Draft until basecamp/basecamp-sdk#1038 merges. go.mod pins that PR's head (ab6fefd08980), a commit not yet on basecamp-sdk main. Once it merges, re-run make bump-sdk against main, scripts/sync-mcp-model.sh against that checkout and make update-nix-hash, and this can leave draft.

What

  • TUI activity feed. It reads today, then steps back a day at a time until it has 80 events or has read a week. The first request is undated, so today is the person's Basecamp day. The dated requests count back from today on this machine, so no day is skipped when Basecamp's date is ahead. When the dates agree, that day comes back twice, and an event seen twice is kept once. A busy account still makes one request. A quiet one makes up to eight.
  • basecamp timeline --date <day> reads another day of the account-wide feed. It accepts YYYY-MM-DD, today and yesterday. With --date, the breadcrumbs offer the exact day before. Without it they offer --date <YYYY-MM-DD>, because this machine's clock can't name Basecamp's today. It refuses three things: a value that isn't a date (bc3 would read it as today), a future day (dateparse reads monday as next Monday), and --date with --in, --person or me (bc3 ignores the day there).
  • The default summary says N events today instead of N recent events.
  • Bumps the SDK to basecamp-sdk#1038, where Progress takes *ProgressOptions. Re-syncs the vendored MCP model (scripts/sync-mcp-model.sh) to that commit. The MCP catalog's GetProgressReport gains date but not project_ids[] or people_ids[]. A list naming no visible ID clears the user's saved web filter, so a read-only GET could change a setting. The sync script now drops parameters by policy (POLICY_EXCLUDED_PARAMETERS) and refuses to run if a parameter it names disappears. Copilot found this one too, and TestCatalogKeepsTheActivityFilterOffTheProgressReport failed before the change.

Why

bc3's progress.json serves one day per request, today by default. The TUI asked once, undated. After midnight its feed was empty, and its Yesterday and Older groups never filled. The CLI had no way to ask for another day. Card: [SDK] Pass the activity date and filters through.

Not done here: project and person filters for the feed. The SDK can send them now. But an omitted list means the user's saved web filter, and a list with no visible ID clears that filter (see basecamp/basecamp-sdk#1038), so they need their own UI decision.

Testing

Repro. The first commit adds only tests, and they fail on main:

  • TestFetchTimelineEventsReachesPastMidnight gets [1] (today's event only) instead of [1 2 3 4].
  • The lookback test gets one request instead of eight.
  • The timeline --date tests fail with unknown flag: --date.

Fix. The second commit makes them pass. A Copilot review found that a Basecamp date ahead of the machine's skipped a day. The fourth commit fixes that, and its new test failed first ([1 3] instead of [1 2 3]). A second review found two more problems. Stepping a local timestamp back a day skipped a date where a clock change removes midnight (America/Santiago, 2026-09-06). And --date ' ' read today. The fifth commit fixes both, and its three new tests failed first. bin/ci passes locally at 0426ca02, rebased onto main d74f1d5f.

Real run against a local bc3 (verify-basecamp-cli, run 20261023-133116-activity-feed-days, bc3 2c59c3fdc61). bc3's timeline files there are identical to master 9c09e30bc9. The fixtures carry almost no timeline history, and the verify server runs no job workers. So I relayed and backdated fixture events in the isolated verify_cli database: 5 today, 8 on 2026-10-22, 2 on 2026-10-20, 2 on 2026-10-13.

main (fe7738a7) this branch (058d22d2, before the rebase onto main)
timeline --all 5 events, today only 5 events, "5 events today", breadcrumb --date <YYYY-MM-DD>
timeline --date 2026-10-22 Unknown option: --date, exit 1 8 events, all 2026-10-22, matching an independent GET progress.json?date=2026-10-22. Breadcrumb --date 2026-10-21
--date tomorrow / someday / --in n/a usage error, exit 1, no request
TUI Activity view (tmux capture) Just Now and Today groups, 5 events adds Yesterday (8 events) and Older (2 events, 2026-10-20)

The 2026-10-13 events sit outside the week of lookback, as designed.

Not proven.

  • That bc3's Link header carries date into page 2 of a day. No seeded day passed 50 events.
  • The TUI's poll cost on a quiet account. Up to eight requests per account per poll while Activity is focused, inside the 10/s request bucket from Spend a rate-limit token on every request, not every operation #909. Reasoned, not measured.
  • The time-zone handling is covered by unit tests only (Basecamp ahead, Basecamp behind, a midnight clock change). No real run had mismatched zones.
  • --date yesterday goes through the shared dateparse, which steps a timestamp. So in a zone whose clock change removes midnight, it can name two days back during the first hour after the change. That parser is shared with other commands, so I left it alone.

Copilot AI balanced review requested due to automatic review settings October 23, 2026 18:01
@github-actions github-actions Bot added commands CLI command implementations tui Terminal UI sdk SDK wrapper and provenance tests Tests (unit and e2e) deps labels Oct 23, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Mixed Basecamp and machine-local dates can skip activity days in the TUI feed and produce incorrect previous-day navigation.

2 open issues
What changed in this PR

Adds historical activity browsing to the CLI and fills the TUI feed beyond today using the SDK’s new date options.

Changes:

  • Reads the TUI feed backward by day, stopping at 80 events or seven requests and deduplicating overlapping events.
  • Adds timeline --date with validation, day-specific summaries, and previous-day breadcrumbs.
  • Updates the SDK dependency, provenance, MCP model, and CLI surface snapshot.

[!TIP]
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.

File Description
internal/​version/​sdk-provenance.json Records the updated SDK revision.
internal/​tui/​workspace/​data/​timeline_feed_test.go Tests lookback limits, feed size, and deduplication.
internal/​tui/​workspace/​data/​hub_global.go Fetches activity across multiple days.
internal/​mcpserver/​model/​PROVENANCE.json Records the synchronized model revision.
internal/​mcpserver/​model/​openapi.json Adds progress date and filter parameters.
internal/​commands/​timeline.go Implements date selection and previous-day navigation.
internal/​commands/​timeline_test.go Tests date requests and invalid combinations.
go.sum Updates SDK checksums.
go.mod Pins the SDK version supporting progress options.
.surface Registers the new timeline --date flag.

Review effort: Balanced

Comment thread internal/commands/timeline.go Outdated
Comment thread internal/tui/workspace/data/hub_global.go Outdated
Copilot AI balanced review requested due to automatic review settings October 23, 2026 18:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Date validation still permits whitespace-only input to silently fetch today’s activity.

2 open issues
1 previously missed issue

In code that hasn't changed since last review

Medium severity Reject whitespace-only dates instead of treating them as today

internal/​commands/​timeline.go:87

A whitespace-only value such as --date ' ' bypasses the invalid-date safeguard. dateparse.ParseFrom trims it to an empty string, and the SDK's ParseDate("") returns a zero date without an error. The request then omits the date and silently reads today instead of rejecting the input. Reject a zero parsed date here; the early return already handles an omitted date. Add a whitespace-only case to TestTimelineDateRefusals to verify a usage error and no request.

Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings October 23, 2026 18:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Date handling can skip activity days across midnight daylight-saving transitions, and whitespace-only dates bypass validation.

No open issues 🎉

4 previously missed issues

In code that hasn't changed since last review

Medium severity Resolve yesterday using UTC calendar midnight

internal/​commands/​timeline.go:86

dateparse.ParseFrom implements yesterday with local-time AddDate, so the new flag can request the wrong day across a midnight daylight-saving transition. At 2026-09-07 00:30 in America/Santiago, --date yesterday resolves to September 5 instead of September 6 because September 6's 00:30 does not exist. Parse against UTC midnight carrying the same calendar date—not now.UTC(), which can change the date—and add a regression test.

Medium severity Reject whitespace-only date values

internal/​commands/​timeline.go:87

A whitespace-only value such as --date ' ' silently reads today instead of producing a usage error. dateparse.ParseFrom trims it to an empty string, and the SDK's ParseDate("") returns an unset date without an error, so the request omits date. Reject a zero parsed date when a nonempty value was supplied, and add whitespace to the refusal tests.

Medium severity Use calendar dates for previous-day breadcrumb

internal/​commands/​timeline.go:161

The previous-day breadcrumb can skip a date when daylight saving removes local midnight. In America/Santiago, --date 2026-09-06 converts to September 5 at 23:00 before subtraction, so the breadcrumb offers September 4 instead of September 5. Requesting September 7 also skips September 6 when subtraction lands on its nonexistent midnight. Use calendar-only day.AddDays(-1) and test navigation across this transition.

Medium severity Use calendar dates when walking dated activity

internal/​tui/​workspace/​data/​hub_global.go:629

The dated walk can skip a day across a midnight daylight-saving transition because AddDate preserves the local clock time. For example, at 2026-09-07 00:30 in America/Santiago, subtracting one day normalizes the nonexistent September 6 00:30 to September 5. The next iteration requests September 5 again, leaving September 6's activity out of the feed. Convert to a calendar-only Date before subtracting with AddDays, and add a regression test for this transition.

2 resolved since last review

Review effort: Balanced

@robzolkos robzolkos closed this Oct 23, 2026
@robzolkos robzolkos reopened this Oct 23, 2026
bc3's progress.json serves one day per request, today unless `date` names
another. The TUI's activity feed asks once, undated, so after midnight it
is empty and its Yesterday and Older groups never fill. `basecamp
timeline` has no way to ask for another day at all.

The feed tests serve progress.json one day at a time, as bc3 does, and
expect the feed to step back a day at a time until it is full or a week
has passed, keeping an event it sees twice once. The command tests expect
`timeline --date` to send the day, offer the day before as the next step,
and refuse a date that isn't one, a future date, and a date for the
project or person timelines, which bc3 doesn't filter by day.
Bumps the SDK to basecamp-sdk#1038's head (732fb9cd2bbd), which teaches
Progress the `date`, `project_ids[]` and `people_ids[]` parameters through
a new ProgressOptions.

The TUI's activity feed asks for today, undated so the day is the one in
the person's Basecamp time zone, then steps back from yesterday on this
machine one day per request until it has 80 events or has read a week. If
the two time zones disagree on the date a day can come back twice, so an
event already seen is skipped. A busy account still makes one request.

`basecamp timeline --date` reads another day of the account-wide feed. It
accepts YYYY-MM-DD, today and yesterday, refuses anything else (bc3 reads an
unparseable date as today) and a future day (dateparse reads a weekday name
as the next one), and refuses --date with a project or person timeline,
which bc3 doesn't filter by day. Its breadcrumbs offer the day before.
The feed's undated request reads today in the person's Basecamp time zone,
and the dated walk began at yesterday on this machine. When Basecamp's
date was a day ahead, the machine's today was never read. The walk now
begins at the machine's today; when the dates agree that day comes back
twice, which the seen-set already absorbs, at one extra request for a
quiet account.

`timeline` without --date had the same gap in its breadcrumb, which named
the machine's yesterday. Undated, the CLI can't know Basecamp's today, so
the breadcrumb now offers `--date <YYYY-MM-DD>`; with --date it still names
the exact day before.
Copilot AI balanced review requested due to automatic review settings October 23, 2026 19:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Date handling can skip activity days around midnight daylight-saving transitions and accepts whitespace-only input as today's feed.

3 open issues

Review effort: Balanced

Comment thread internal/commands/timeline.go Outdated
Comment thread internal/commands/timeline.go Outdated
Comment thread internal/tui/workspace/data/hub_global.go Outdated
Stepping a local timestamp back a day skips a date where a clock change
removes midnight: in America/Santiago, 2026-09-07 00:30 minus a day is
2026-09-05 23:30. The TUI walk and the --date breadcrumb now subtract from
a calendar date (Date.AddDays), and the future check compares dates.

`--date ' '` trimmed to nothing, parsed as the zero date and read today's
feed; a date that parses to nothing is now refused like any other.
Copilot AI balanced review requested due to automatic review settings October 23, 2026 19:32

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The newly exposed MCP filters can reset saved user settings through an operation still permitted in read-only mode.

1 open issue
3 resolved since last review

Review effort: Balanced

Comment thread internal/mcpserver/model/openapi.json Outdated
@jeremy

jeremy commented Oct 23, 2026

Copy link
Copy Markdown
Member

🤖 Reviewed at 7b9aee1 (still a draft). CI is green. Copilot's latest thread, about the MCP read-only filter, is still open, and I won't repeat it. The calendar-date arithmetic, the Santiago tests, the blank-date refusal, and the dedupe on the overlapping first day all look right. The new tests would fail if the walk started at yesterday, or if dates went through a timestamp again.

The future-date refusal uses this machine's date, but the feed is in Basecamp's time zone. timelineDay refuses any day after basecamp.DateOf(now) (timeline.go#L90), which is the local date. This PR's own TUI walk handles Basecamp being a day ahead (hub_global.go#L615-L626). The CLI flag doesn't. Take someone whose machine is on US Pacific time and whose Basecamp is set to Tokyo. In their evening, --date <Basecamp's today> and --date tomorrow are both refused as "in the future", even though Basecamp has activity for that day. Allowing one day past the local date (DateOf(now).AddDays(1)) fixes it, and still catches the main problem, which is --date monday resolving to next week. The "in future" test case would need a date two days out.

Smaller: one failed day now throws away the whole account. fetchTimelineEvents returns nil, err on any of its up to eight requests (hub_global.go#L626-L633), and FanOut then drops that account from the merged feed. A quiet account used to make one request every poll and now makes eight, so a transient error or a 429 on, say, day five is more likely, and it now also discards days zero to four. Returning what's collected so far once len(events) > 0 (and the error only when there's nothing) keeps the feed working.

Its only change since 732fb9cd is a MIGRATING.md note, but the vendored MCP
model has to be synced from the commit go.mod pins.
Syncing the MCP model to basecamp-sdk#1038 served GetProgressReport's new
project_ids[] and people_ids[] to MCP clients, read-only mode included. bc3
clears the caller's saved web activity filter when such a list names no ID
the caller can see, so that read-only GET could change a user setting.

The model sync now drops parameters by policy, refusing to run if one it
names is gone, and drops those two; `date` stays. The CLI exposes neither
list anywhere yet, so this keeps MCP in step with it.
TestCatalogKeepsTheActivityFilterOffTheProgressReport failed before the
sync (extra project_ids[] and people_ids[]) and passes after.
Copilot AI balanced review requested due to automatic review settings October 23, 2026 19:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

The upstream SDK remains unmerged, its required post-merge refresh is pending, and a date-line lookback gap remains unresolved.

No open issues 🎉

1 previously missed issue

In code that hasn't changed since last review

Medium severity Include the skipped intervening day for two-date timezone offsets

internal/​tui/​workspace/​data/​hub_global.go:629

The walk still skips a day when Basecamp is two calendar dates ahead: at October 23, 23:30 in Pacific/Pago_Pago (UTC−11), it is October 25 in Pacific/Kiritimati (UTC+14). The undated request reads October 25, then the dated walk starts on October 23, omitting October 24 even when the feed has room. Include the intervening machine-local tomorrow or anchor the walk to Basecamp's effective date, and add a regression test for this 25-hour offset.

1 resolved since last review

Review effort: Balanced

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

commands CLI command implementations deps sdk SDK wrapper and provenance tests Tests (unit and e2e) tui Terminal UI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants