Skip to content

feat: resume threads after usage limits reset

MacroscopeApp / Macroscope - UI Consistency failed Aug 28, 2026 in 3m 12s

UI Consistency: 3 issues found

Three findings in apps/web/src/components/ChatView.tsx (new usage-limit resume banner wiring):

  • apps/web/src/components/ChatView.tsx (L1651-1654) — the "Automatic resume scheduled for …" label uses a raw new Intl.DateTimeFormat(undefined, { dateStyle, timeStyle }) instead of the shared ~/timestampFormat helpers, so it ignores the user's settings.timestampFormat (12-/24-hour) and the desktop host locale that every other chat timestamp honours. timestampFormat is already in scope (L1378); snoozeWakeDescription solves the identical "wake later" label.
  • apps/web/src/components/ChatView.tsx (L1644-1646) — the fallback error copy "T3 is resuming this thread now." is shown even when the resume is scheduled far in the future (contradicting the description line right below it) and when no resume is scheduled at all. Derive the fallback from usageLimitResume / nextAttemptAt.
  • apps/web/src/components/ChatView.tsx (L6979) — onDismiss is suppressed for the whole duration of lastErrorClass === "usage_limit", so after the user cancels auto-resume the banner becomes permanently non-dismissible (and keeps hasTimelineTopBanner true). Gate the suppression on usageLimitResume !== null.

No issues found with primitive usage: ThreadErrorBanner keeps composing the shared Alert/AlertAction/Button primitives, and the new action uses variant="outline" size="xs", matching the existing parked-thread banner action.

Details

Note

Your check run agent prompt is: .macroscope/check-run-agents/ui-consistency.md
More information about how Check Run Agents work can be found in our Help Center.

Files examined in scope: apps/web/src/components/ChatView.tsx, apps/web/src/components/chat/ThreadErrorBanner.tsx, apps/web/src/components/chat/ThreadErrorBanner.test.tsx.

Context consulted outside scope: apps/web/src/components/ui/button.tsx (confirmed xs / icon-xs variants exist and focus/disabled behavior is retained), apps/web/src/components/ui/alert.tsx (AlertAction slot layout supports two children), apps/web/src/timestampFormat.ts and apps/web/src/components/Sidebar.snooze.ts (shared timestamp/locale contract and the analogous snooze wake label), and the server-side resume lifecycle (decider.ts, ProviderCommandReactor.ts) to confirm the client states usageLimitResume === null, nextAttemptAt === null, and future nextAttemptAt are all reachable.

Not flagged: the generic "Saving…" pending label in ThreadErrorBanner (differs from the verb-specific pending copy used by the parked-thread banner, but is an aesthetic/copy preference only), and the added actionDescription/actionPending props, which keep the existing dismiss semantics and accessibility attributes intact.


🔍 Agent activity — 13 tool calls

💭 I want to check the web files that changed, particularly ChatView.tsx and ThreadErrorBanner.tsx along with its test, and verify whether the Button component supports an "xs" size.

1. browse_code — apps/web/src/components/chat/ThreadErrorBanner.tsx

2. browse_code — apps/web/src/components/ui/button*

3. browse_code — apps/web/src/components/ui/button.tsx

4. browse_code — apps/web/src/components/ui/alert.tsx

💭 The banner reuses shared Button and Alert primitives, which seems reasonable, though I'm noting the "Saving…" label during pending state and wondering whether other components use a spinner convention instead, so I should check for existing loading patterns. I also want to look at how ChatView wires up the error banner, particularly how it falls back to a message about the thread resuming. I'm digging into a potential copy issue: when the usage limit banner shows because of isUsageLimitError but no resume is actually scheduled (usageLimitResume === null), the fallback text still claims "T3 is resuming this thread now." paired with a "Resume when available" action, which seems misleading to the user. I want to trace through the ChatView code to confirm this scenario actually occurs before flagging it.

5. git_tools — visibleThreadError|threadError|displayedThreadError|usageLimitResume|showUsageLimitResumeBanner

💭 I'm looking at how the displayed thread error is computed within the scope of apps/web/src, checking whether the logic for showing the usage-limit resume banner bypasses the normal dismissal masking, and whether that's actually intentional given dismissal is suppressed for usage-limit banners. Also, the fallback banner text "T3 is resuming this thread now." seems misleading when the resume is actually scheduled hours in the future rather than immediate, since the accompanying action description already clarifies the scheduled time—this is a possible copy inconsistency worth flagging, though borderline as a UI issue. Separately, the generic "Saving…" pending label doesn't fit actions like "Resume when available" or "Cancel auto-resume," and swapping labels shifts button width, which is a minor aesthetic and accessibility concern. I also notice the DateTimeFormat instance is created inline and non-reactive, which is fine here, but I want to check whether the codebase has a shared date/time formatting utility used elsewhere for consistency, so I'm searching for other Intl.DateTimeFormat usages across the web app.

6. git_tools — Intl.DateTimeFormat|formatRelative|dateStyle:|timeStyle:

7. git_tools — Saving…|Spinner|isPending|Loader

💭 I'm noticing the codebase already has time formatting helpers and locale resolution utilities, but the new code creates its own Intl.DateTimeFormat instance inline per render instead of reusing the existing module-level pattern seen elsewhere in the app.

8. browse_code — apps/web/src/timestampFormat.ts

💭 I notice the new banner formats time directly with Intl.DateTimeFormat using default locale settings, bypassing the user's configured timestamp format (12/24 hour) that every other chat timestamp respects via formatShortTimestamp. This is a real inconsistency worth flagging.

9. git_tools — timestampFormat

💭 I've confirmed ChatView already reads the user's timestamp format setting, and the analogous snooze feature respects the 12/24-hour preference and locale via its own formatting helper—but the new "Automatic resume scheduled for..." string ignores both, which is a real inconsistency worth flagging. Now I want to check Sidebar.snooze.ts's snoozeWakeDescription to see if it can be reused here.

10. browse_code — apps/web/src/components/Sidebar.snooze.ts

💭 I should use a timestampFormat-aware helper like snoozeWakeDescription instead of a raw formatter here. I want to double check that timestampFormat is in scope for both usages, and it looks like both are within the same ChatViewContent component starting around line 1316. There's also a smaller issue with ThreadErrorBanner showing a generic "Saving…" label for pending resume/cancel actions, which is imprecise copy and could cause a minor layout shift since other buttons use a Spinner pattern instead. And I'm noticing a third issue: the banner's fallback copy claims T3 is resuming "now" even in cases where the resume is actually scheduled for a future time with no active error. Now I'm honing in on three concrete findings worth flagging inline: using the shared timestamp formatting utility instead of a raw Intl.DateTimeFormat call for consistency with the app's timestamp settings, the loss of the dismiss affordance for usage-limit banners once auto-resume is cancelled (making the banner permanently stuck), and the contradictory "resuming now" copy when a future resume time is actually scheduled. I want to check whether ChatView already imports the shared timestamp helper before writing that suggestion.

11. git_tools — snooze|timestampFormat|Sidebar.snooze

💭 Let me check lines 4845-4870 to see how the "snoozed" description formats wake time in ChatView.

12. browse_code — apps/web/src/components/ChatView.tsx

💭 I notice the generic "Saving…" label is inconsistent with the established pattern of action-specific pending text like "Waking...", so I could mention it as a minor optional suggestion in the ThreadErrorBanner comment without treating it as a blocking issue. Deciding to focus on three concrete issues instead: the timestamp formatting in ChatView that ignores user locale settings, a dismiss behavior regression where the banner becomes non-dismissible under certain error classes even after cancelling auto-resume, and a fallback message that conflicts with scheduled resume times. I'll post these as separate short inline comments rather than combining them. For the timestamp fix, since ChatView doesn't already import the snooze description helper, I need to either propose adding that import or use a simpler locale-aware formatter, though the simpler option would lose day-of-week context that matters for longer resume delays. Writing suggested diff... Now I'm considering whether onDismiss should stay bound regardless of pending resume state, since dismissal is scoped per error message key rather than per action. The cleanest fix here is to only suppress dismissal when a resume is actively pending — checking usageLimitResume !== null rather than the broader banner flag — so a usage-limit error with no scheduled resume still keeps the dismiss affordance. I'll suggest that as a self-contained inline change on the relevant line. For the fallback text, I want to distinguish two cases: when a resume time exists, show "T3 will resume this thread automatically," and only show "resuming now" when there's truly no scheduled attempt. But I realize if usageLimitResume itself is null, treating that as "resuming now" is also misleading since nothing's actually scheduled — I need to handle that case separately rather than lumping it in with the immediate-resume condition. Now I'm pinning down exact line numbers for the diff comments so the review notes reference the right spots in both the old and new versions of the file. Given that timestamp formatting genuinely violates the project's 12/24-hour user setting contract, I'm deciding to mark this review as a failure state rather than a pass, since it's a real regression risk. Now I'll draft the actual review comments.

13. modify_pr — Reviewed the web UI changes (ChatView.tsx, chat/ThreadErrorBanner.tsx). The banner itself composes the shared Alert/Button primitives correctly (size="xs" + variant="outline" matches the parked-thread banner action). Three findings on the new usage-limit resume surface in ChatView.tsx.


Agent Credits: 142 credits

Token usage: 22 uncached input · 878,309 cache-read input · 104,078 cache-write input · 10,135 output
Agent Credits may also include non-token charges from external tools such as web research.