Skip to content

bug(sessions): the in-feed Thinking counter counts through laptop sleep and can stick for hours #963

Closed

Description

@Juliusolsson05

Summary

After the laptop sleeps, an agent pane's in-feed work indicator can read Thinking · 20h03m. The counter is WorkIndicator painting Date.now() - turnStartedAt (useElapsedSeconds), and nothing in the session pipeline knows the machine slept. Reported by the user on 2026-09-12 against the Thinking counter specifically.

Expected

  • A turn whose stream died while the machine was asleep does not keep showing a live counter after wake.
  • A turn that genuinely continues after wake does not count the sleep as working time.
  • A prompt sent after wake starts its own clock.

Actual (reproduced against the real adapters and reducers)

The reproduction drives the real ClaudeProxyAdapter / CodexResponsesAdapter and the renderer's real foldSemanticEvent + reduceStreamPhase, in the order useIpcSubscriptions runs them, with only the wall clock simulated. Timings come from a real recording (below).

Scenario Claude proxy Codex proxy
Stream severed by sleep, provider never retries Stuck Thinking forever on the pre-sleep clock (still 20h03m the next morning). No timer exists that can close the flow; the only stale-flow reap runs inside the next flow's first chunk. A prompt sent now "joins live work" (submitJoinsLiveWork), so it inherits the old clock. Shows the sleep until the watchdog's first tick after wake (≤10 s), then clears.
Client tool running across sleep, never returns — Stuck awaiting-tool on the pre-sleep clock: the watchdog seals the flow but the turn_completed bridge refuses to leave awaiting-tool.
Tool returns / turn continues after wake Counter includes the whole sleep (turnStartedAt is only stamped while null). Counter includes the whole sleep.
Stream retried after wake Correct: reap publishes idle, clock restarts. —
Turn finished before sleep Correct: no counter. —

Mechanism, by file:

  • src/renderer/src/session-runtime/semantic/streamPhaseMachine.ts:63-72: turnStartedAt is set only while null and cleared only on an idle phase or a clean turn_completed.
  • packages/claude-code-headless/src/proxy/ClaudeProxyAdapter.ts:1009-1022: stale-flow reap only on a new flow's first chunk; with the proxy on, screen idle is suppressed.
  • packages/codex-headless/src/proxy/CodexResponsesAdapter.ts:983-1021: watchdog seals the turn but publishes no idle phase.
  • src/main/index.ts:672-673: powerMonitor suspend/resume feeds only the performance monitor; no session code reacts to sleep.
  • All activity clocks use Date.now(); the run journal's monotonicMs (performance.now()) also advanced through sleep in real recordings, so it cannot detect sleep either.

Evidence (real, on disk)

  • Turn across a confirmed clamshell sleep, 2026-08-31 → 09-01: Claude transcript (prompt 20:57:09 PDT, last assistant 22:45:08, turn_duration 39,888,429 ms = 11.08 h written after wake), the matching Codex rollout (turn_aborted: wall span 11.62 h vs duration_ms 1.48 h), the Agent Code run journal (app alive through the sleep; wall and monotonic deltas equal), and /var/log/powermanagement sleep 23:43:59 → wake 08:01:40.
  • Unclean shutdown, 2026-08-29 → 08-30: 20.7 h gap in a Claude transcript with a tool_use that never got a result; next app run reports app.prior_unclean_shutdown. (A cold relaunch starts every runtime empty, so this shape cannot itself produce the counter; sleep with the app running is the reproduced cause.)

Impact

Every agent pane after a laptop sleep; Dispatch rows, Close Old Agents and Close Idle Orchestration Agents read the same phase, so a stuck phase also shows the agent as working and keeps it from being judged idle.

Environment

macOS (darwin 25.6), Agent Code at d12cd347, Claude and Codex with the proxy enabled.

Reproduction test and plan: branch feat/agent-working-time, src/renderer/src/session-runtime/semantic/turnClockAcrossSleep.test.ts and docs/decomposition/agent-working-time.md.

Activity

  1. Juliusolsson05 commented on Sep 13, 2026

    @Juliusolsson05
    OwnerAuthor

    Blocks #964 (agent working-hours analytics), which must record working time from the fixed substrate rather than the broken counter. Plan and decisions: docs/decomposition/agent-working-time.md on feat/agent-working-time.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions